namespace LumaTunnel.Server.Core.Configuration; public sealed record ServerSettings { public string NodeId { get; init; } = Guid.NewGuid().ToString("N"); public string NodeName { get; init; } = Environment.MachineName; public string[] BindingUrls { get; init; } = ["http://localhost:3300/"]; public string PublicApiUrl { get; init; } = "http://localhost:3300/api"; public string PublicTunnelUrl { get; init; } = "ws://localhost:3300/tunnel/v1"; public int MaxConnections { get; init; } = 512; public int MaxConnectionsPerDevice { get; init; } = 128; public int MaxBufferedFramesPerStream { get; init; } = 32; public int ConnectTimeoutSeconds { get; init; } = 10; public int IdleTimeoutMinutes { get; init; } = 15; public bool AllowPrivateTargets { get; init; } public int[] BlockedPorts { get; init; } = [25]; public int ApiRequestsPerMinutePerIp { get; init; } = 120; public void Validate() { if (string.IsNullOrWhiteSpace(NodeId)) throw new InvalidOperationException("NodeId is required."); if (string.IsNullOrWhiteSpace(NodeName)) throw new InvalidOperationException("NodeName is required."); if (BindingUrls.Length == 0 || BindingUrls.Any(url => !Uri.TryCreate(url, UriKind.Absolute, out _))) throw new InvalidOperationException("At least one valid absolute binding URL is required."); if (!Uri.TryCreate(PublicApiUrl, UriKind.Absolute, out _)) throw new InvalidOperationException("PublicApiUrl must be an absolute URI."); if (!Uri.TryCreate(PublicTunnelUrl, UriKind.Absolute, out var tunnelUri) || tunnelUri.Scheme is not ("ws" or "wss")) throw new InvalidOperationException("PublicTunnelUrl must use ws or wss."); if (MaxConnections <= 0 || MaxConnectionsPerDevice <= 0 || MaxConnectionsPerDevice > MaxConnections) throw new InvalidOperationException("Connection limits are invalid."); if (ConnectTimeoutSeconds is < 1 or > 120) throw new InvalidOperationException("ConnectTimeoutSeconds must be between 1 and 120."); } }