using ClawdDotNet.Core.Config;
namespace ClawdDotNet.Web;
///
/// Die geprüften Startangaben der Weboberfläche.
///
/// Getrennt von , weil dort Wünsche stehen und hier
/// Entscheidungen: Der Port ist eine Zahl, die Bindung eine von zweien, und ein Token,
/// das nicht taugt, ist gar keins. Was hier nicht durchkommt, kommt gar nicht erst an
/// den Kestrel.
///
/// Nur der TCP-Zugang steht hier. Der Socket, über den der Instanzmanager
/// die Instanz erreicht, braucht keine Einstellung: Seine Adresse folgt aus dem
/// Instanzverzeichnis, seine Grenze aus den Dateirechten. null aus
/// heißt deshalb „keine Weboberfläche", nicht „keine
/// Instanz".
///
public sealed record WebUiOptions(string Url, int Port, string Token, bool LoopbackOnly)
{
/// Kürzer als das hier darf ein Token nicht sein.
public const int MinimumTokenLength = 20;
///
/// Prüft die Angaben der Instanz.
///
/// Gibt null zurück, wenn die Weboberfläche abgeschaltet ist — das ist
/// kein Fehler und die Vorbelegung. trägt den Grund,
/// wenn sie eingeschaltet ist, aber nicht starten darf.
///
/// Die harte Regel steht hier: Bindung an alle Adressen ohne Token wird
/// abgelehnt, nicht bloß bemängelt. Ein Netzwerkdienst, der Agenten mit Datei-,
/// Mail- und FTP-Zugriff steuert, ist nichts, was versehentlich offen stehen darf.
/// Auch auf Loopback gilt Token-Pflicht: Auf einer Maschine, auf der Agenten
/// Browser-Werkzeuge bedienen, ist „localhost" keine Vertrauensgrenze.
///
public static WebUiOptions? TryCreate(InstanceConfig instance, out string? problem)
{
problem = null;
if (!instance.WebUiEnabled)
return null;
var loopbackOnly = !string.Equals(
instance.WebUiBinding, "any", StringComparison.OrdinalIgnoreCase);
var port = instance.WebServerPort;
if (port is < 1 or > 65535)
{
problem = $"Der Port {port} ist keiner. Erwartet wird 1 bis 65535.";
return null;
}
var token = instance.WebUiToken?.Trim() ?? "";
if (token.Length == 0)
{
problem = loopbackOnly
? "Die Weboberfläche braucht ein Zugangstoken (webUiToken in der Instanzdatei)."
: "Die Weboberfläche ist auf alle Adressen gebunden und hat kein Zugangstoken. "
+ "So wird sie nicht gestartet.";
return null;
}
if (token.Length < MinimumTokenLength)
{
problem = $"Das Zugangstoken ist zu kurz — mindestens {MinimumTokenLength} Zeichen.";
return null;
}
var host = loopbackOnly ? "127.0.0.1" : "0.0.0.0";
return new WebUiOptions($"http://{host}:{port}", port, token, loopbackOnly);
}
///
/// Die Adresse, die man einem Menschen zeigt. 0.0.0.0 ist keine, die sich
/// anklicken lässt.
///
public string DisplayUrl => LoopbackOnly ? Url : Url.Replace("0.0.0.0", Environment.MachineName);
}