using System.Text.Json; using RelayServer.Models; using RelayServer.Services.Chat; using RelayServer.Services.Crypto; using SurrealDb.Net; namespace RelayServer.Services.Core; public sealed class ServerBootstrapService { // TODO: Make channels dynamically addable // TODO: Add logic for channel types (ENUM) // TODO: Add logic for channel groups for future UI use private readonly SurrealDbClient _db; private readonly CoreClientService _coreClient; private readonly ChannelCryptoService _cryptoService; public ServerBootstrapService( SurrealDbClient db, CoreClientService coreClient, ChannelCryptoService cryptoService) { _db = db; _coreClient = coreClient; _cryptoService = cryptoService; } public async Task InitializeAsync() { var keeper = await _coreClient.GetUserByUsernameAsync("Keeper317"); var kira = await _coreClient.GetUserByUsernameAsync("Ru_Kira"); var test = await _coreClient.GetUserByUsernameAsync("Test"); if (keeper is null || kira is null || test is null) throw new InvalidOperationException("One or more required users do not exist in RelayCore."); if (!keeper.Licensed || !kira.Licensed || !test.Licensed) throw new InvalidOperationException("One or more required users are not licensed."); Console.WriteLine($"Core verified user: {keeper.Username}"); Console.WriteLine($"Core verified user: {kira.Username}"); Console.WriteLine($"Core verified user: {test.Username}"); var server = await GetServerByNameAsync("Test Server"); if (server is null) { server = await _db.Create("servers", new Servers { Name = "Test Server", OwnerUserId = keeper.Id, CreatedAt = DateTime.UtcNow }); Console.WriteLine($"Server created: {ToJsonString(server)}"); } else { Console.WriteLine($"Server already exists: {ToJsonString(server)}"); } await EnsureServerMemberAsync(keeper.Id, true); await EnsureServerMemberAsync(kira.Id, false); await EnsureServerMemberAsync(test.Id, false); Console.WriteLine("Server members ensured."); var channel = await EnsureChannelAsync("general", DateTime.UtcNow); var channel2 = await EnsureChannelAsync("files", DateTime.UtcNow.Subtract(new TimeSpan(0, 4, 0, 0))); var channel3 = await EnsureChannelAsync("welcome", DateTime.UtcNow.Subtract(new TimeSpan(1, 4, 4, 4))); var channel4 = await EnsureChannelAsync("voice-general", DateTime.UtcNow.Subtract(new TimeSpan(0, 2, 0, 0))); Console.WriteLine($"Resolved channelId: {GetRecordId(channel.Id)}"); Console.WriteLine($"Resolved channelId: {GetRecordId(channel2.Id)}"); Console.WriteLine($"Resolved channelId: {GetRecordId(channel3.Id)}"); Console.WriteLine($"Resolved channelId: {GetRecordId(channel4.Id)}"); var existingKey = await GetLatestServerEncryptionKeyAsync(); if (existingKey is null) { var keyBase64 = _cryptoService.GenerateKey(); var serverKeys = E2EeHelper.GenerateRsaKeyPair(); existingKey = await _db.Create("server_encryption_keys", new ServerEncryptionKeys { KeyBase64 = keyBase64, PublicKey = serverKeys.publicKey, PrivateKey = serverKeys.privateKey, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }); Console.WriteLine("Server encryption key created."); } else { Console.WriteLine("Server encryption key already exists."); } ChatSocketBehavior.ServerPublicKey = existingKey.PublicKey; ChatSocketBehavior.ServerPrivateKey = existingKey.PrivateKey; ChatSocketBehavior.ChannelDbKey = existingKey.KeyBase64; } private static string ToJsonString(object? obj) { return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); } private static string GetRecordId(object? id) { if (id is null) return string.Empty; var json = JsonSerializer.Serialize(id); using var doc = JsonDocument.Parse(json); var root = doc.RootElement; var recordId = root.GetProperty("Id").GetString() ?? string.Empty; var table = root.GetProperty("Table").GetString() ?? string.Empty; return $"{table}:{recordId}"; } private async Task GetServerByNameAsync(string name) { var servers = await _db.Select("servers"); return servers.FirstOrDefault(x => x.Name == name); } private async Task GetServerMemberByUserIdAsync(string userId) { var members = await _db.Select("server_members"); return members.FirstOrDefault(x => x.UserId == userId); } private async Task GetChannelByNameAsync(string name) { var channels = await _db.Select("channels"); return channels.FirstOrDefault(x => x.Name == name); } private async Task GetLatestServerEncryptionKeyAsync() { var keys = await _db.Select("server_encryption_keys"); return keys .OrderByDescending(x => x.CreatedAt) .FirstOrDefault(); } private async Task EnsureServerMemberAsync(string userId, bool isOwner) { var existing = await GetServerMemberByUserIdAsync(userId); if (existing is not null) { Console.WriteLine($"Server member already exists for {userId}"); return; } await _db.Create("server_members", new ServerMembers { UserId = userId, JoinedAt = DateTime.UtcNow, IsOwner = isOwner }); Console.WriteLine($"Server member created for {userId}"); } private async Task EnsureChannelAsync(string name, DateTime createdAt) { var existing = await GetChannelByNameAsync(name); if (existing is not null) { Console.WriteLine($"Channel already exists: {name}"); return existing; } var channel = await _db.Create("channels", new Channels { Name = name, CreatedAt = createdAt }); Console.WriteLine($"Channel created: {ToJsonString(channel)}"); return channel; } }