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); }