using LumaTunnel.Server.Core.Configuration; using LumaTunnel.Server.Core.Persistence; using LumaTunnel.Server.Core.Security; namespace LumaTunnel.Server.Core.Runtime; public sealed class ServerRuntime { private static ServerRuntime? s_current; private readonly AtomicJsonStore _trafficStore; private ServerRuntime(ServerPaths paths, ServerSettings settings, DeviceRegistry devices, PairingCodeService pairingCodes, AtomicJsonStore trafficStore, TrafficCounters traffic) { Paths = paths; Settings = settings; Devices = devices; PairingCodes = pairingCodes; _trafficStore = trafficStore; Traffic = traffic; TargetPolicy = new TargetPolicy(settings); } public static ServerRuntime Current => s_current ?? throw new InvalidOperationException("Server runtime has not been initialized."); public ServerPaths Paths { get; } public ServerSettings Settings { get; } public DeviceRegistry Devices { get; } public PairingCodeService PairingCodes { get; } public TargetPolicy TargetPolicy { get; } public TrafficCounters Traffic { get; } public TunnelConnectionManager? TunnelConnections { get; set; } public static async Task InitializeAsync(ServerPaths? paths = null, CancellationToken cancellationToken = default) { paths ??= ServerPaths.CreateDefault(); paths.EnsureDirectories(); var settingsStore = new AtomicJsonStore(paths.SettingsFile); var settings = await settingsStore.LoadOrCreateAsync(static () => new ServerSettings(), cancellationToken).ConfigureAwait(false); settings.Validate(); var devices = new DeviceRegistry(new AtomicJsonStore(paths.DevicesFile)); var pairingCodes = new PairingCodeService(new AtomicJsonStore(paths.PairingCodesFile)); var trafficStore = new AtomicJsonStore(paths.TrafficFile); await devices.InitializeAsync(cancellationToken).ConfigureAwait(false); await pairingCodes.InitializeAsync(cancellationToken).ConfigureAwait(false); var traffic = await trafficStore.LoadOrCreateAsync(static () => new TrafficDatabase(), cancellationToken).ConfigureAwait(false); s_current = new ServerRuntime(paths, settings, devices, pairingCodes, trafficStore, new TrafficCounters(traffic.Devices)); return s_current; } public async Task FlushAsync(CancellationToken cancellationToken = default) { await Devices.SaveAsync(cancellationToken).ConfigureAwait(false); await _trafficStore.SaveAsync(Traffic.Snapshot(), cancellationToken).ConfigureAwait(false); } }