diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs deleted file mode 100644 index 0843218..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Spacebar.Interop.Authentication.AspNetCore; -using Spacebar.Interop.Replication.Abstractions; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Gateway; - -namespace Spacebar.GatewayOffload.Controllers; - -[ApiController] -[Route("/_spacebar/offload/gateway")] -public class ChannelStatusController(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) - : ControllerBase { - [HttpPost("ChannelStatuses")] - public async IAsyncEnumerable> GetChannelStatuses([FromBody] ChannelStatusesRequest req) { - await foreach (var res in GetChannelInfos(new() { - Fields = ["status"], - GuildIdRawValue = req.GuildIdRawValue, - })) { - yield return new() { - Payload = new() { - GuildId = res.Payload.GuildId, - Channels = res.Payload.Channels.Select(c => new ChannelStatus { - ChannelId = c.ChannelId, - Status = c.Status!, - }).ToList(), - } - }; - } - } - - [HttpPost("ChannelInfo")] - public async IAsyncEnumerable> GetChannelInfos([FromBody] ChannelInfoRequest req) { - var user = await authService.GetCurrentUserAsync(Request); - string[] statusOptions = [ - "Vibing ✨", - "Hanging out: 12%...", - "Communicating...", - // idk, i cant come up with more stuff, maybe suggestions welcome, or actually storing some data? - ]; - - foreach (var guildId in req.GuildIds ?? [req.GuildId!]) { - var channels = (await db.Channels.Include(x => x.VoiceStates).Where(x => x.Type == 2 && x.GuildId == guildId && x.VoiceStates.Count > 0) - .Select(x => x.Id) - .ToListAsync()) - .Select(x => new { - id = x, - status = statusOptions[new Random().Next(statusOptions.Length)], // TODO: We don't currently store channel statuses, so make some stuff up - voiceStartTime = DateTime.Now.Subtract(TimeSpan.FromMinutes(new Random().Next(1, 120))), // TODO: We also don't store voice start times, so make some stuff up - }).ToList(); - - yield return new() { - Payload = new() { - GuildId = guildId, - Channels = channels.Select(c => new ChannelInfo { - ChannelId = c.id, - Status = req.Fields.Contains("status") ? c.status : null, - VoiceStartTime = req.Fields.Contains("voice_start_time") ? c.voiceStartTime : null, - }).ToList(), - }, - }; - } - } -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs deleted file mode 100644 index 02bad39..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Spacebar.Interop.Authentication; -using Spacebar.Interop.Replication.Abstractions; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Gateway; -using Spacebar.Models.Generic; - -namespace Spacebar.GatewayOffload.Controllers; - -[ApiController] -[Route("/_spacebar/offload/gateway/Identify")] -public class IdentifyController(ILogger logger, SpacebarAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { - [HttpPost("")] - public async IAsyncEnumerable DoIdentify(IdentifyRequest payload) { - var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(payload.Token)); - var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(payload.Token)); - - var socketMeta = new SbWebsocketMeta() { - // Auth data - AccessToken = payload.Token, - UserId = user.Result.Id, - SessionId = session.Result.SessionId, - // Client capabilities - Capabilities = payload.Capabilities ??= 0, - LargeThreshold = payload.LargeTreshold ??= user.Result.Bot ? 20 : 250, - Intents = payload.Intents ??= (GatewayIntentFlags)0b_110_11111111_11111111_11111111_11111111, - // Sharding info - ShardId = payload.Shard?[0], - ShardCount = payload.Shard?[1], - }; - - if (socketMeta is { ShardId: not null, ShardCount: not null }) { - if (socketMeta.ShardId < 0 || socketMeta.ShardCount <= 0 || socketMeta.ShardId >= socketMeta.ShardCount) { - logger.LogWarning("Invalid sharding from {userId}: {shardId}/{shardCount}", user.Result.Id, socketMeta.ShardId, socketMeta.ShardCount); - yield return this.Close(CloseCode.InvalidShard); - yield break; - } - } - - yield return new ReplicationMessage() { - Payload = new() { }, - }; - } - - // TODO: type? also, implement this in gateway lol - private ReplicationMessage Close(CloseCode closeCode) => new() { - Origin = "IdentifyController", - Event = "SB_GW_CLOSE", - Payload = new { - code = closeCode, - } - }; -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs deleted file mode 100644 index 884a9de..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Collections.Frozen; -using System.Linq.Expressions; -using System.Text.Json; -using System.Text.Json.Nodes; -using ArcaneLibs.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Spacebar.DataMappings.Generic; -using Spacebar.Interop.Authentication.AspNetCore; -using Spacebar.Interop.Replication.Abstractions; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Db.Models; -using Spacebar.Models.Gateway; -using Spacebar.Models.Generic; - -namespace Spacebar.GatewayOffload.Controllers; - -[ApiController] -[Route("/_spacebar/offload/gateway/GuildSync")] -public class Op12Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase -{ - [HttpPost("")] - public async IAsyncEnumerable> DoGuildSync(List guildIds) - { - var user = await authService.GetCurrentUserAsync(Request); - guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) - .Intersect(guildIds) - .OrderByDescending(gi => db.Members.Count(m => m.GuildId == gi)) - .ToList(); - - var syncs = guildIds.Select(GetGuildSyncAsync).ToList().ToAsyncResultEnumerable(); - await foreach (var res in syncs) - { - yield return new() - { - Origin = "OFFLOAD_GUILD_SYNC", - UserId = user.Id, - Event = "GUILD_SYNC", - CreatedAt = DateTime.Now, - Payload = res - }; - } - } - - // TODO: figure out how to abstract this to a function without EFCore complaining about not being translatable... - private static Expression> IsOnline = (Session session) => session.Status != "offline" && session.Status != "invisible" && session.Status != "unknown"; - - private async Task GetGuildSyncAsync(string guildId) - { - await using var sc = sp.CreateAsyncScope(); - var _db = sc.ServiceProvider.GetRequiredService(); - var memberCount = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId).CountAsync(); - - var offlineTreshold = DateTime.Now.Subtract(TimeSpan.FromDays(14)); - var isLargeGuild = memberCount > 10000; - - var members = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId) - .Include(x => x.IdNavigation) - .ThenInclude(x => x.Sessions.Where(s => - !s.IsAdminSession && ( - // see TODO on IsOnline - somehow need to replicate `IsOnline(s)` - s.Status != "offline" && s.Status != "invisible" && s.Status != "unknown" - ) && (!isLargeGuild || s.LastSeen >= offlineTreshold))) - .Where(x => x.IdNavigation.Sessions.Count > 0) // ignore members without sessions - .ToListAsync(); - - var mappedPartialUsers = members.Select(x => x.IdNavigation).ToFrozenDictionary(x => x.Id, x => x.ToPartialUser()); - var mappedMembers = members.ToFrozenDictionary(m => m.Id, m => m.ToPublicMember(mappedPartialUsers[m.Id])); - - var presences = members.Select(x => x.IdNavigation).Where(x => x.Sessions.Count > 0).ToFrozenDictionary(x => x.Id, x => - { - var sortedSessions = x.Sessions.OrderByDescending(s => s.LastSeen).ToList(); - return new Presence() - { - GuildId = guildId, - User = mappedPartialUsers[x.Id], - Activities = x.Sessions.Where(s => s.Status is not ("offline" or "invisible" or "unknown")) - .SelectMany(s => JsonSerializer.Deserialize(s.Activities) ?? []).ToList(), - Status = sortedSessions.FirstOrDefault(s => !string.IsNullOrWhiteSpace(s.Status))?.Status ?? "offline", - ClientStatus = JsonSerializer.Deserialize(sortedSessions.First(s => !string.IsNullOrWhiteSpace(s.ClientStatus)).ClientStatus) ?? - new() - }; - }).Where(x => x.Value.Activities.Count > 0).ToFrozenDictionary(); - - var r = new GuildSyncResponse() - { - GuildId = guildId, - Members = mappedMembers.Values.ToList(), - Presences = presences.Values.ToList() - }; - return r; - } -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs deleted file mode 100644 index 57f1390..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Spacebar.GatewayOffload.Extensions.Db; -using Spacebar.Interop.Authentication.AspNetCore; -using Spacebar.Interop.Replication.Abstractions; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Gateway; -using Spacebar.Models.Generic; - -namespace Spacebar.GatewayOffload.Controllers; - -[ApiController] -[Route("/_spacebar/offload/gateway/LazyRequest")] -public class Op14Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { - [HttpPost] - // TODO: actually return something? - public async IAsyncEnumerable DoLazyRequest([FromBody] LazyRequest payload) { - var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(Request)); - var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(Request)); - - if (!await db.Members.AsNoTracking().AnyAsync(m => m.GuildId == payload.GuildId && m.Id == user.Result.Id)) { - logger.LogWarning("User {user} requested lazy member list for guild {guildId}, but is not a member", user.Result.Id, payload.GuildId); - yield break; - } - - if (payload.Channels.Count == 0) { - logger.LogWarning("User {user} requested lazy member list for guild {guildId}, but is not a member", user.Result.Tag, payload.GuildId); - yield break; - } - - // Fetch hoisted roles for the guild to define groups - var hoistedRoles = await db.Roles - .AsNoTracking() - .Where(r => r.GuildId == payload.GuildId && r.Hoist) - .OrderByDescending(r => r.Position) - .Select(r => new { r.Id }) - .ToListAsync(); - } - - private async Task GetMemberListIdAsync(SpacebarDbContext db, string guildId, string channelId) { - var channel = await db.Channels.AsNoTracking().FirstOrDefaultAsync(c => c.Id == channelId && c.GuildId == guildId); - if (channel == null) return null; - - if (string.IsNullOrWhiteSpace(channel.PermissionOverwrites) || channel.PermissionOverwrites == "[]") { - return "everyone"; - } - - return null; // TODO - } -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs deleted file mode 100644 index ac68bb2..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Collections.Frozen; -using System.Linq.Expressions; -using System.Text.Json; -using System.Text.Json.Nodes; -using ArcaneLibs.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Spacebar.DataMappings.Generic; -using Spacebar.Interop.Authentication.AspNetCore; -using Spacebar.Interop.Replication.Abstractions; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Db.Models; -using Spacebar.Models.Gateway; -using Spacebar.Models.Generic; - -namespace Spacebar.GatewayOffload.Controllers; - -[ApiController] -[Route("/_spacebar/offload/gateway/GuildMembers")] -public class Op8Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase -{ - [HttpPost("")] - public async IAsyncEnumerable> DoGuildSync(List guildIds) - { - var user = await authService.GetCurrentUserAsync(Request); - guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) - .Intersect(guildIds) - .OrderByDescending(gi => db.Members.Count(m => m.GuildId == gi)) - .ToList(); - - var syncs = guildIds.Select(GetGuildSyncAsync).ToList().ToAsyncResultEnumerable(); - await foreach (var res in syncs) - { - yield return new() - { - Origin = "OFFLOAD_GUILD_SYNC", - UserId = user.Id, - Event = "GUILD_SYNC", - CreatedAt = DateTime.Now, - Payload = res - }; - } - } - - // TODO: figure out how to abstract this to a function without EFCore complaining about not being translatable... - private static Expression> IsOnline = (Session session) => session.Status != "offline" && session.Status != "invisible" && session.Status != "unknown"; - - private async Task GetGuildSyncAsync(string guildId) - { - await using var sc = sp.CreateAsyncScope(); - var _db = sc.ServiceProvider.GetRequiredService(); - var memberCount = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId).CountAsync(); - - var offlineTreshold = DateTime.Now.Subtract(TimeSpan.FromDays(14)); - var isLargeGuild = memberCount > 10000; - - var members = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId) - .Include(x => x.IdNavigation) - .ThenInclude(x => x.Sessions.Where(s => - !s.IsAdminSession && ( - // see TODO on IsOnline - somehow need to replicate `IsOnline(s)` - s.Status != "offline" && s.Status != "invisible" && s.Status != "unknown" - ) && (!isLargeGuild || s.LastSeen >= offlineTreshold))) - .Where(x => x.IdNavigation.Sessions.Count > 0) // ignore members without sessions - .ToListAsync(); - - var mappedPartialUsers = members.Select(x => x.IdNavigation).ToFrozenDictionary(x => x.Id, x => x.ToPartialUser()); - var mappedMembers = members.ToFrozenDictionary(m => m.Id, m => m.ToPublicMember(mappedPartialUsers[m.Id])); - - var presences = members.Select(x => x.IdNavigation).Where(x => x.Sessions.Count > 0).ToFrozenDictionary(x => x.Id, x => - { - var sortedSessions = x.Sessions.OrderByDescending(s => s.LastSeen).ToList(); - return new Presence() - { - GuildId = guildId, - User = mappedPartialUsers[x.Id], - Activities = x.Sessions.Where(s => s.Status is not ("offline" or "invisible" or "unknown")) - .SelectMany(s => JsonSerializer.Deserialize(s.Activities) ?? []).ToList(), - Status = sortedSessions.FirstOrDefault(s => !string.IsNullOrWhiteSpace(s.Status))?.Status ?? "offline", - ClientStatus = JsonSerializer.Deserialize(sortedSessions.First(s => !string.IsNullOrWhiteSpace(s.ClientStatus)).ClientStatus) ?? - new() - }; - }).Where(x => x.Value.Activities.Count > 0).ToFrozenDictionary(); - - var r = new GuildSyncResponse() - { - GuildId = guildId, - Members = mappedMembers.Values.ToList(), - Presences = presences.Values.ToList() - }; - return r; - } -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Extensions/Db/UserExtensions.cs b/extra/admin-api/Spacebar.GatewayOffload/Extensions/Db/UserExtensions.cs deleted file mode 100644 index 27b4fb1..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Extensions/Db/UserExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Spacebar.Models.Db.Models; - -namespace Spacebar.GatewayOffload.Extensions.Db; - -public static class UserExtensions { - extension(User user) { - public string Tag => $"{user.Username}#{user.Discriminator}"; - } -} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Program.cs b/extra/admin-api/Spacebar.GatewayOffload/Program.cs deleted file mode 100644 index 947ca35..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Program.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using ArcaneLibs.Extensions; -using Microsoft.EntityFrameworkCore; -using Spacebar.Interop.Authentication; -using Spacebar.Interop.Authentication.AspNetCore; -using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Generic; - -var builder = WebApplication.CreateBuilder(args); -if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("APPSETTINGS_PATH"))) - builder.Configuration.AddJsonFile(Environment.GetEnvironmentVariable("APPSETTINGS_PATH")!); - -// Add services to the container. - -builder.Services.AddControllers(options => { - options.MaxValidationDepth = null; - // options.MaxIAsyncEnumerableBufferLimit = 1; -}).AddJsonOptions(options => { - options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - options.JsonSerializerOptions.WriteIndented = true; - options.JsonSerializerOptions.MaxDepth = 100; - // options.JsonSerializerOptions.DefaultBufferSize = ; -}).AddMvcOptions(o => { o.SuppressOutputFormatterBuffering = true; }); -// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi -builder.Services.AddOpenApi(); - -builder.Services.AddDbContextPool(options => { - options - .UseNpgsql(builder.Configuration.GetConnectionString("Spacebar")) - .EnableDetailedErrors(); -}); - -builder.Services.AddSingleton(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); - -var app = builder.Build(); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) { - app.MapOpenApi(); -} - -app.UseAuthorization(); - -app.MapControllers(); - -app.Run(); \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Properties/launchSettings.json b/extra/admin-api/Spacebar.GatewayOffload/Properties/launchSettings.json deleted file mode 100644 index 385142c..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Properties/launchSettings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "http://localhost:5019", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj b/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj deleted file mode 100644 index 8923cb3..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net10.0 - enable - enable - - - - - - - - - - - - - - - - - - diff --git a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.http b/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.http deleted file mode 100644 index dc84f63..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.http +++ /dev/null @@ -1,34 +0,0 @@ -@UpstreamApi_HostAddress = https://api.rory.server.spacebar.chat -@GatewayOffload_HostAddress = http://localhost:5019 - -GET {{UpstreamApi_HostAddress}}/api/v10/users/@me/guilds -Accept: application/json -Authorization: Bearer {{AccessToken}} - -### - -POST {{GatewayOffload_HostAddress}}/_spacebar/offload/gateway/GuildSync -Accept: application/json -Authorization: Bearer {{AccessToken}} -Content-Type: application/json - -[ - "1006649183970562092", - "1006688661691001233", - "1006724118650845953", - "1006725777779159397", - "1006929437075185067", - "1064985958721777984", - "1069727354182414384", - "1244318971698665103", - "1294758139447835873", - "1370456872691392086", - "1391838091291000099", - "1412646927261544598", - "1425141280262418979", - "1429366715866774152", - "1430867450484749537", - "1439953071347150967" -] - -### diff --git a/extra/admin-api/Spacebar.GatewayOffload/appsettings.Development.json b/extra/admin-api/Spacebar.GatewayOffload/appsettings.Development.json deleted file mode 100644 index 12d6d23..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/appsettings.Development.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Trace", - //Warning - "Microsoft.AspNetCore.Mvc": "Warning", - //Warning - "Microsoft.AspNetCore.HostFiltering": "Warning", - //Warning - "Microsoft.AspNetCore.Cors": "Warning", - //Warning - // "Microsoft.EntityFrameworkCore": "Warning" - "Microsoft.EntityFrameworkCore.Database.Command": "Information" - } - }, - "ConnectionStrings": { - "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5433; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;" - }, - "Spacebar": { - "Authentication": { - "PrivateKeyPath": "../../../jwt.key", - "PublicKeyPath": "../../../jwt.key.pub" -// "Enforce2FA": false, -// "OverrideUid": "1006598230156341276", -// "DisableAuthentication": true - } - } -} diff --git a/extra/admin-api/Spacebar.GatewayOffload/appsettings.json b/extra/admin-api/Spacebar.GatewayOffload/appsettings.json deleted file mode 100644 index 10f68b8..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/extra/admin-api/Spacebar.GatewayOffload/deps.json b/extra/admin-api/Spacebar.GatewayOffload/deps.json deleted file mode 100644 index b03c4aa..0000000 --- a/extra/admin-api/Spacebar.GatewayOffload/deps.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "pname": "ArcaneLibs", - "version": "1.0.1-preview.20260126-091403", - "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" - }, - { - "pname": "BCrypt.Net-Next", - "version": "4.1.0", - "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" - }, - { - "pname": "Microsoft.AspNetCore.OpenApi", - "version": "10.0.5", - "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.4", - "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.4", - "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.4", - "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.4", - "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" - }, - { - "pname": "Microsoft.IdentityModel.Abstractions", - "version": "8.16.0", - "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" - }, - { - "pname": "Microsoft.IdentityModel.JsonWebTokens", - "version": "8.16.0", - "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" - }, - { - "pname": "Microsoft.IdentityModel.Logging", - "version": "8.16.0", - "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" - }, - { - "pname": "Microsoft.IdentityModel.Tokens", - "version": "8.16.0", - "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" - }, - { - "pname": "Microsoft.OpenApi", - "version": "2.0.0", - "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" - }, - { - "pname": "Npgsql", - "version": "10.0.2", - "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" - }, - { - "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.1", - "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" - }, - { - "pname": "System.IdentityModel.Tokens.Jwt", - "version": "8.16.0", - "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" - } -] diff --git a/extra/admin-api/Spacebar.Offload/Controllers/ChannelStatusController.cs b/extra/admin-api/Spacebar.Offload/Controllers/ChannelStatusController.cs new file mode 100644 index 0000000..0843218 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Controllers/ChannelStatusController.cs @@ -0,0 +1,68 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Gateway; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway")] +public class ChannelStatusController(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) + : ControllerBase { + [HttpPost("ChannelStatuses")] + public async IAsyncEnumerable> GetChannelStatuses([FromBody] ChannelStatusesRequest req) { + await foreach (var res in GetChannelInfos(new() { + Fields = ["status"], + GuildIdRawValue = req.GuildIdRawValue, + })) { + yield return new() { + Payload = new() { + GuildId = res.Payload.GuildId, + Channels = res.Payload.Channels.Select(c => new ChannelStatus { + ChannelId = c.ChannelId, + Status = c.Status!, + }).ToList(), + } + }; + } + } + + [HttpPost("ChannelInfo")] + public async IAsyncEnumerable> GetChannelInfos([FromBody] ChannelInfoRequest req) { + var user = await authService.GetCurrentUserAsync(Request); + string[] statusOptions = [ + "Vibing ✨", + "Hanging out: 12%...", + "Communicating...", + // idk, i cant come up with more stuff, maybe suggestions welcome, or actually storing some data? + ]; + + foreach (var guildId in req.GuildIds ?? [req.GuildId!]) { + var channels = (await db.Channels.Include(x => x.VoiceStates).Where(x => x.Type == 2 && x.GuildId == guildId && x.VoiceStates.Count > 0) + .Select(x => x.Id) + .ToListAsync()) + .Select(x => new { + id = x, + status = statusOptions[new Random().Next(statusOptions.Length)], // TODO: We don't currently store channel statuses, so make some stuff up + voiceStartTime = DateTime.Now.Subtract(TimeSpan.FromMinutes(new Random().Next(1, 120))), // TODO: We also don't store voice start times, so make some stuff up + }).ToList(); + + yield return new() { + Payload = new() { + GuildId = guildId, + Channels = channels.Select(c => new ChannelInfo { + ChannelId = c.id, + Status = req.Fields.Contains("status") ? c.status : null, + VoiceStartTime = req.Fields.Contains("voice_start_time") ? c.voiceStartTime : null, + }).ToList(), + }, + }; + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Controllers/IdentifyController.cs b/extra/admin-api/Spacebar.Offload/Controllers/IdentifyController.cs new file mode 100644 index 0000000..02bad39 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Controllers/IdentifyController.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Mvc; +using Spacebar.Interop.Authentication; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Gateway; +using Spacebar.Models.Generic; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway/Identify")] +public class IdentifyController(ILogger logger, SpacebarAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { + [HttpPost("")] + public async IAsyncEnumerable DoIdentify(IdentifyRequest payload) { + var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(payload.Token)); + var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(payload.Token)); + + var socketMeta = new SbWebsocketMeta() { + // Auth data + AccessToken = payload.Token, + UserId = user.Result.Id, + SessionId = session.Result.SessionId, + // Client capabilities + Capabilities = payload.Capabilities ??= 0, + LargeThreshold = payload.LargeTreshold ??= user.Result.Bot ? 20 : 250, + Intents = payload.Intents ??= (GatewayIntentFlags)0b_110_11111111_11111111_11111111_11111111, + // Sharding info + ShardId = payload.Shard?[0], + ShardCount = payload.Shard?[1], + }; + + if (socketMeta is { ShardId: not null, ShardCount: not null }) { + if (socketMeta.ShardId < 0 || socketMeta.ShardCount <= 0 || socketMeta.ShardId >= socketMeta.ShardCount) { + logger.LogWarning("Invalid sharding from {userId}: {shardId}/{shardCount}", user.Result.Id, socketMeta.ShardId, socketMeta.ShardCount); + yield return this.Close(CloseCode.InvalidShard); + yield break; + } + } + + yield return new ReplicationMessage() { + Payload = new() { }, + }; + } + + // TODO: type? also, implement this in gateway lol + private ReplicationMessage Close(CloseCode closeCode) => new() { + Origin = "IdentifyController", + Event = "SB_GW_CLOSE", + Payload = new { + code = closeCode, + } + }; +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Controllers/Op12Controller.cs b/extra/admin-api/Spacebar.Offload/Controllers/Op12Controller.cs new file mode 100644 index 0000000..884a9de --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Controllers/Op12Controller.cs @@ -0,0 +1,93 @@ +using System.Collections.Frozen; +using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Nodes; +using ArcaneLibs.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.DataMappings.Generic; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; +using Spacebar.Models.Generic; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway/GuildSync")] +public class Op12Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase +{ + [HttpPost("")] + public async IAsyncEnumerable> DoGuildSync(List guildIds) + { + var user = await authService.GetCurrentUserAsync(Request); + guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) + .Intersect(guildIds) + .OrderByDescending(gi => db.Members.Count(m => m.GuildId == gi)) + .ToList(); + + var syncs = guildIds.Select(GetGuildSyncAsync).ToList().ToAsyncResultEnumerable(); + await foreach (var res in syncs) + { + yield return new() + { + Origin = "OFFLOAD_GUILD_SYNC", + UserId = user.Id, + Event = "GUILD_SYNC", + CreatedAt = DateTime.Now, + Payload = res + }; + } + } + + // TODO: figure out how to abstract this to a function without EFCore complaining about not being translatable... + private static Expression> IsOnline = (Session session) => session.Status != "offline" && session.Status != "invisible" && session.Status != "unknown"; + + private async Task GetGuildSyncAsync(string guildId) + { + await using var sc = sp.CreateAsyncScope(); + var _db = sc.ServiceProvider.GetRequiredService(); + var memberCount = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId).CountAsync(); + + var offlineTreshold = DateTime.Now.Subtract(TimeSpan.FromDays(14)); + var isLargeGuild = memberCount > 10000; + + var members = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId) + .Include(x => x.IdNavigation) + .ThenInclude(x => x.Sessions.Where(s => + !s.IsAdminSession && ( + // see TODO on IsOnline - somehow need to replicate `IsOnline(s)` + s.Status != "offline" && s.Status != "invisible" && s.Status != "unknown" + ) && (!isLargeGuild || s.LastSeen >= offlineTreshold))) + .Where(x => x.IdNavigation.Sessions.Count > 0) // ignore members without sessions + .ToListAsync(); + + var mappedPartialUsers = members.Select(x => x.IdNavigation).ToFrozenDictionary(x => x.Id, x => x.ToPartialUser()); + var mappedMembers = members.ToFrozenDictionary(m => m.Id, m => m.ToPublicMember(mappedPartialUsers[m.Id])); + + var presences = members.Select(x => x.IdNavigation).Where(x => x.Sessions.Count > 0).ToFrozenDictionary(x => x.Id, x => + { + var sortedSessions = x.Sessions.OrderByDescending(s => s.LastSeen).ToList(); + return new Presence() + { + GuildId = guildId, + User = mappedPartialUsers[x.Id], + Activities = x.Sessions.Where(s => s.Status is not ("offline" or "invisible" or "unknown")) + .SelectMany(s => JsonSerializer.Deserialize(s.Activities) ?? []).ToList(), + Status = sortedSessions.FirstOrDefault(s => !string.IsNullOrWhiteSpace(s.Status))?.Status ?? "offline", + ClientStatus = JsonSerializer.Deserialize(sortedSessions.First(s => !string.IsNullOrWhiteSpace(s.ClientStatus)).ClientStatus) ?? + new() + }; + }).Where(x => x.Value.Activities.Count > 0).ToFrozenDictionary(); + + var r = new GuildSyncResponse() + { + GuildId = guildId, + Members = mappedMembers.Values.ToList(), + Presences = presences.Values.ToList() + }; + return r; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Controllers/Op14Controller.cs b/extra/admin-api/Spacebar.Offload/Controllers/Op14Controller.cs new file mode 100644 index 0000000..57f1390 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Controllers/Op14Controller.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.GatewayOffload.Extensions.Db; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Gateway; +using Spacebar.Models.Generic; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway/LazyRequest")] +public class Op14Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { + [HttpPost] + // TODO: actually return something? + public async IAsyncEnumerable DoLazyRequest([FromBody] LazyRequest payload) { + var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(Request)); + var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(Request)); + + if (!await db.Members.AsNoTracking().AnyAsync(m => m.GuildId == payload.GuildId && m.Id == user.Result.Id)) { + logger.LogWarning("User {user} requested lazy member list for guild {guildId}, but is not a member", user.Result.Id, payload.GuildId); + yield break; + } + + if (payload.Channels.Count == 0) { + logger.LogWarning("User {user} requested lazy member list for guild {guildId}, but is not a member", user.Result.Tag, payload.GuildId); + yield break; + } + + // Fetch hoisted roles for the guild to define groups + var hoistedRoles = await db.Roles + .AsNoTracking() + .Where(r => r.GuildId == payload.GuildId && r.Hoist) + .OrderByDescending(r => r.Position) + .Select(r => new { r.Id }) + .ToListAsync(); + } + + private async Task GetMemberListIdAsync(SpacebarDbContext db, string guildId, string channelId) { + var channel = await db.Channels.AsNoTracking().FirstOrDefaultAsync(c => c.Id == channelId && c.GuildId == guildId); + if (channel == null) return null; + + if (string.IsNullOrWhiteSpace(channel.PermissionOverwrites) || channel.PermissionOverwrites == "[]") { + return "everyone"; + } + + return null; // TODO + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Controllers/Op8Controller.cs b/extra/admin-api/Spacebar.Offload/Controllers/Op8Controller.cs new file mode 100644 index 0000000..ac68bb2 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Controllers/Op8Controller.cs @@ -0,0 +1,93 @@ +using System.Collections.Frozen; +using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Nodes; +using ArcaneLibs.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.DataMappings.Generic; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; +using Spacebar.Models.Generic; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway/GuildMembers")] +public class Op8Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase +{ + [HttpPost("")] + public async IAsyncEnumerable> DoGuildSync(List guildIds) + { + var user = await authService.GetCurrentUserAsync(Request); + guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) + .Intersect(guildIds) + .OrderByDescending(gi => db.Members.Count(m => m.GuildId == gi)) + .ToList(); + + var syncs = guildIds.Select(GetGuildSyncAsync).ToList().ToAsyncResultEnumerable(); + await foreach (var res in syncs) + { + yield return new() + { + Origin = "OFFLOAD_GUILD_SYNC", + UserId = user.Id, + Event = "GUILD_SYNC", + CreatedAt = DateTime.Now, + Payload = res + }; + } + } + + // TODO: figure out how to abstract this to a function without EFCore complaining about not being translatable... + private static Expression> IsOnline = (Session session) => session.Status != "offline" && session.Status != "invisible" && session.Status != "unknown"; + + private async Task GetGuildSyncAsync(string guildId) + { + await using var sc = sp.CreateAsyncScope(); + var _db = sc.ServiceProvider.GetRequiredService(); + var memberCount = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId).CountAsync(); + + var offlineTreshold = DateTime.Now.Subtract(TimeSpan.FromDays(14)); + var isLargeGuild = memberCount > 10000; + + var members = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId) + .Include(x => x.IdNavigation) + .ThenInclude(x => x.Sessions.Where(s => + !s.IsAdminSession && ( + // see TODO on IsOnline - somehow need to replicate `IsOnline(s)` + s.Status != "offline" && s.Status != "invisible" && s.Status != "unknown" + ) && (!isLargeGuild || s.LastSeen >= offlineTreshold))) + .Where(x => x.IdNavigation.Sessions.Count > 0) // ignore members without sessions + .ToListAsync(); + + var mappedPartialUsers = members.Select(x => x.IdNavigation).ToFrozenDictionary(x => x.Id, x => x.ToPartialUser()); + var mappedMembers = members.ToFrozenDictionary(m => m.Id, m => m.ToPublicMember(mappedPartialUsers[m.Id])); + + var presences = members.Select(x => x.IdNavigation).Where(x => x.Sessions.Count > 0).ToFrozenDictionary(x => x.Id, x => + { + var sortedSessions = x.Sessions.OrderByDescending(s => s.LastSeen).ToList(); + return new Presence() + { + GuildId = guildId, + User = mappedPartialUsers[x.Id], + Activities = x.Sessions.Where(s => s.Status is not ("offline" or "invisible" or "unknown")) + .SelectMany(s => JsonSerializer.Deserialize(s.Activities) ?? []).ToList(), + Status = sortedSessions.FirstOrDefault(s => !string.IsNullOrWhiteSpace(s.Status))?.Status ?? "offline", + ClientStatus = JsonSerializer.Deserialize(sortedSessions.First(s => !string.IsNullOrWhiteSpace(s.ClientStatus)).ClientStatus) ?? + new() + }; + }).Where(x => x.Value.Activities.Count > 0).ToFrozenDictionary(); + + var r = new GuildSyncResponse() + { + GuildId = guildId, + Members = mappedMembers.Values.ToList(), + Presences = presences.Values.ToList() + }; + return r; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Extensions/Db/UserExtensions.cs b/extra/admin-api/Spacebar.Offload/Extensions/Db/UserExtensions.cs new file mode 100644 index 0000000..27b4fb1 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Extensions/Db/UserExtensions.cs @@ -0,0 +1,9 @@ +using Spacebar.Models.Db.Models; + +namespace Spacebar.GatewayOffload.Extensions.Db; + +public static class UserExtensions { + extension(User user) { + public string Tag => $"{user.Username}#{user.Discriminator}"; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Program.cs b/extra/admin-api/Spacebar.Offload/Program.cs new file mode 100644 index 0000000..947ca35 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Program.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; +using Microsoft.EntityFrameworkCore; +using Spacebar.Interop.Authentication; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Generic; + +var builder = WebApplication.CreateBuilder(args); +if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("APPSETTINGS_PATH"))) + builder.Configuration.AddJsonFile(Environment.GetEnvironmentVariable("APPSETTINGS_PATH")!); + +// Add services to the container. + +builder.Services.AddControllers(options => { + options.MaxValidationDepth = null; + // options.MaxIAsyncEnumerableBufferLimit = 1; +}).AddJsonOptions(options => { + options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; + options.JsonSerializerOptions.WriteIndented = true; + options.JsonSerializerOptions.MaxDepth = 100; + // options.JsonSerializerOptions.DefaultBufferSize = ; +}).AddMvcOptions(o => { o.SuppressOutputFormatterBuffering = true; }); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +builder.Services.AddDbContextPool(options => { + options + .UseNpgsql(builder.Configuration.GetConnectionString("Spacebar")) + .EnableDetailedErrors(); +}); + +builder.Services.AddSingleton(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) { + app.MapOpenApi(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Offload/Properties/launchSettings.json b/extra/admin-api/Spacebar.Offload/Properties/launchSettings.json new file mode 100644 index 0000000..385142c --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5019", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/extra/admin-api/Spacebar.Offload/Spacebar.Offload.csproj b/extra/admin-api/Spacebar.Offload/Spacebar.Offload.csproj new file mode 100644 index 0000000..8923cb3 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Spacebar.Offload.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/extra/admin-api/Spacebar.Offload/Spacebar.Offload.http b/extra/admin-api/Spacebar.Offload/Spacebar.Offload.http new file mode 100644 index 0000000..dc84f63 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/Spacebar.Offload.http @@ -0,0 +1,34 @@ +@UpstreamApi_HostAddress = https://api.rory.server.spacebar.chat +@GatewayOffload_HostAddress = http://localhost:5019 + +GET {{UpstreamApi_HostAddress}}/api/v10/users/@me/guilds +Accept: application/json +Authorization: Bearer {{AccessToken}} + +### + +POST {{GatewayOffload_HostAddress}}/_spacebar/offload/gateway/GuildSync +Accept: application/json +Authorization: Bearer {{AccessToken}} +Content-Type: application/json + +[ + "1006649183970562092", + "1006688661691001233", + "1006724118650845953", + "1006725777779159397", + "1006929437075185067", + "1064985958721777984", + "1069727354182414384", + "1244318971698665103", + "1294758139447835873", + "1370456872691392086", + "1391838091291000099", + "1412646927261544598", + "1425141280262418979", + "1429366715866774152", + "1430867450484749537", + "1439953071347150967" +] + +### diff --git a/extra/admin-api/Spacebar.Offload/appsettings.Development.json b/extra/admin-api/Spacebar.Offload/appsettings.Development.json new file mode 100644 index 0000000..12d6d23 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/appsettings.Development.json @@ -0,0 +1,29 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Trace", + //Warning + "Microsoft.AspNetCore.Mvc": "Warning", + //Warning + "Microsoft.AspNetCore.HostFiltering": "Warning", + //Warning + "Microsoft.AspNetCore.Cors": "Warning", + //Warning + // "Microsoft.EntityFrameworkCore": "Warning" + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + } + }, + "ConnectionStrings": { + "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5433; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;" + }, + "Spacebar": { + "Authentication": { + "PrivateKeyPath": "../../../jwt.key", + "PublicKeyPath": "../../../jwt.key.pub" +// "Enforce2FA": false, +// "OverrideUid": "1006598230156341276", +// "DisableAuthentication": true + } + } +} diff --git a/extra/admin-api/Spacebar.Offload/appsettings.json b/extra/admin-api/Spacebar.Offload/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/extra/admin-api/Spacebar.Offload/deps.json b/extra/admin-api/Spacebar.Offload/deps.json new file mode 100644 index 0000000..b03c4aa --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/deps.json @@ -0,0 +1,77 @@ +[ + { + "pname": "ArcaneLibs", + "version": "1.0.1-preview.20260126-091403", + "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" + }, + { + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" + }, + { + "pname": "Microsoft.AspNetCore.OpenApi", + "version": "10.0.5", + "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" + }, + { + "pname": "Microsoft.IdentityModel.Abstractions", + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" + }, + { + "pname": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" + }, + { + "pname": "Microsoft.IdentityModel.Logging", + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" + }, + { + "pname": "Microsoft.IdentityModel.Tokens", + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" + }, + { + "pname": "Microsoft.OpenApi", + "version": "2.0.0", + "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" + }, + { + "pname": "Npgsql", + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" + }, + { + "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" + }, + { + "pname": "System.IdentityModel.Tokens.Jwt", + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" + } +] diff --git a/extra/admin-api/Spacebar.Offload/http-client.private.env.json b/extra/admin-api/Spacebar.Offload/http-client.private.env.json new file mode 100644 index 0000000..8e13c59 --- /dev/null +++ b/extra/admin-api/Spacebar.Offload/http-client.private.env.json @@ -0,0 +1,5 @@ +{ + "dev": { + "AccessToken": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMDY1OTgyMzAxNTYzNDEyNzYiLCJpYXQiOjE3NjU4NjYxNDcsImtpZCI6IjVkZTcwNjZlNWQ5YTkxZDc4NGQ2NTY1Njc2Zjc0ZGY4NGQyMDllNzBjY2M3ZmZmNmFhNjgxNTkwODEwMWRjMWEiLCJ2ZXIiOjMsImRpZCI6IklCWkFSR01YT0UifQ.ALoB-4LXPaHTiUWRSoO3KIIc7CX2tP2vdebxmt10h3DqqBW57Zqx9zNImGxn0tV4cqFB1nZct3cZjJ_XVchtUF61AEgGR54QpV2sHAss2NMqZA_S3WK7UigFJYDddWUt2D_GrvzUYUVJ_WB4gt-tXekKzB2K6dazTEFYPFSY6xINBWed" + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Program.cs b/extra/admin-api/Spacebar.UApi/Program.cs index afdb42a..3ebc2db 100644 --- a/extra/admin-api/Spacebar.UApi/Program.cs +++ b/extra/admin-api/Spacebar.UApi/Program.cs @@ -8,7 +8,6 @@ using Spacebar.Interop.Authentication; using Spacebar.Interop.Authentication.AspNetCore; using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Generic.Constants; using Spacebar.UApi.Services; var builder = WebApplication.CreateBuilder(args); @@ -114,7 +113,6 @@ foreach (var header in responseMessage.Headers) context.Response.Headers[header.Key] = header.Value.ToArray(); foreach (var header in responseMessage.Content.Headers) context.Response.Headers[header.Key] = header.Value.ToArray(); - context.Response.Headers["X-SB-UApi-Status"] = "MISSING"; // await responseMessage.Content.CopyToAsync(context.Response.Body); var txt = await responseMessage.Content.ReadAsStringAsync(); diff --git a/extra/admin-api/SpacebarAdminAPI.slnx b/extra/admin-api/SpacebarAdminAPI.slnx index 34ed540..0578bc3 100644 --- a/extra/admin-api/SpacebarAdminAPI.slnx +++ b/extra/admin-api/SpacebarAdminAPI.slnx @@ -36,6 +36,6 @@ - + diff --git a/extra/admin-api/outputs.nix b/extra/admin-api/outputs.nix index 6f47df0..87f8e83 100644 --- a/extra/admin-api/outputs.nix +++ b/extra/admin-api/outputs.nix @@ -203,11 +203,11 @@ proj.Spacebar-Interop-Cdn-Abstractions ]; }; - Spacebar-GatewayOffload = makeNupkg { - name = "Spacebar.GatewayOffload"; - nugetDeps = Spacebar.GatewayOffload/deps.json; - projectFile = "Spacebar.GatewayOffload.csproj"; - srcRoot = ./Spacebar.GatewayOffload; + Spacebar-Offload = makeNupkg { + name = "Spacebar.Offload"; + nugetDeps = Spacebar.Offload/deps.json; + projectFile = "Spacebar.Offload.csproj"; + srcRoot = ./Spacebar.Offload; packNupkg = false; projectReferences = [ proj.Spacebar-DataMappings-Generic @@ -258,30 +258,30 @@ Expose = [ "5000" ]; }; }; - containers.docker.gateway-offload = pkgs.dockerTools.buildLayeredImage { - name = "spacebar-server-ts-gateway-offload"; - tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-AdminApi.version; + containers.docker.offload = pkgs.dockerTools.buildLayeredImage { + name = "spacebar-server-ts-offload"; + tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-Offload.version; contents = [ self.packages.${system}.Spacebar-AdminApi ]; config = { - Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-GatewayOffload}" ]; + Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-Offload}" ]; Expose = [ "5000" ]; }; }; containers.docker.cdn-cs = pkgs.dockerTools.buildLayeredImage { name = "spacebar-server-ts-cdn-cs"; - tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-AdminApi.version; + tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-Cdn.version; contents = [ self.packages.${system}.Spacebar-AdminApi ]; config = { - Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-AdminApi}" ]; + Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-Cdn}" ]; Expose = [ "5000" ]; }; }; containers.docker.uapi = pkgs.dockerTools.buildLayeredImage { name = "spacebar-server-ts-cdn-cs"; - tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-AdminApi.version; + tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-UApi.version; contents = [ self.packages.${system}.Spacebar-AdminApi ]; config = { - Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-AdminApi}" ]; + Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-UApi}" ]; Expose = [ "5000" ]; }; }; @@ -295,7 +295,7 @@ in pkgs.lib.recursiveUpdate (pkgs.lib.attrsets.unionOfDisjoint { } self.packages) { x86_64-linux = { - # spacebar-server-tests = self.packages.x86_64-linux.default.passthru.tests; + # spacebar-server-tests = self.packages.x86_64-linux.default.passthru.tests; docker-admin-api = self.containers.x86_64-linux.docker.admin-api; docker-gateway-offload = self.containers.x86_64-linux.docker.gateway-offload; docker-cdn-cs = self.containers.x86_64-linux.docker.cdn-cs; diff --git a/nix/modules/default/cs/gateway-offload-cs.nix b/nix/modules/default/cs/gateway-offload-cs.nix deleted file mode 100644 index 6187f07..0000000 --- a/nix/modules/default/cs/gateway-offload-cs.nix +++ /dev/null @@ -1,89 +0,0 @@ -self: -{ - config, - lib, - pkgs, - spacebar, - ... -}: - -let - secrets = import ../secrets.nix { inherit lib config; }; - cfg = config.services.spacebarchat-server; - jsonFormat = pkgs.formats.json { }; -in -{ - imports = [ ]; - options.services.spacebarchat-server.gatewayOffload = lib.mkOption { - default = { }; - description = "Configuration for C# gateway offload daemon."; - type = lib.types.submodule { - options = { - enable = lib.mkEnableOption "Enable gateway offload daemon (C#)."; - listenPort = lib.mkOption { - type = lib.types.port; - default = 3011; - description = "Port for the gateway offload daemon to listen on."; - }; - extraConfiguration = lib.mkOption { - type = jsonFormat.type; - default = import ./default-appsettings-json.nix; - description = "Extra appsettings.json configuration for the gateway offload daemon."; - }; - enableIdentify = lib.mkEnableOption "Enable offloading gateway opcode 2 (IDENTIFY)."; - enableGuildMembers = lib.mkEnableOption "Enable offloading gateway opcode 8 (REQUEST_GUILD_MEMBERS)."; - enableGuildSync = lib.mkEnableOption "Enable offloading gateway opcode 12 (GUILD_SYNC)."; - enableLazyRequest = lib.mkEnableOption "Enable offloading gateway opcode 14 (LAZY_REQUEST)."; - enableChannelStatuses = lib.mkEnableOption "Enable offloading gateway opcode 36 (CHANNEL_STATUSES)."; - enableChannelInfo = lib.mkEnableOption "Enable offloading gateway opcode 43 (CHANNEL_INFO)."; - }; - }; - }; - - config = lib.mkIf cfg.gatewayOffload.enable ( - let - makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; - in - { - assertions = [ - (import ./assert-has-connection-string.nix "Gateway Offload" cfg.gatewayOffload.extraConfiguration) - ]; - - services.spacebarchat-server.settings.offload = { - gateway = { - identifyUrl = lib.mkIf cfg.gatewayOffload.enableIdentify "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/Identify"; - guildMembersUrl = lib.mkIf cfg.gatewayOffload.enableGuildMembers "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/GuildMembers"; - guildSyncUrlUrl = lib.mkIf cfg.gatewayOffload.enableGuildSync "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/GuildSync"; - lazyRequestUrl = lib.mkIf cfg.gatewayOffload.enableLazyRequest "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/LazyRequest"; - channelStatusesUrl = lib.mkIf cfg.gatewayOffload.enableChannelStatuses "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/ChannelStatuses"; - channelInfoUrl = lib.mkIf cfg.gatewayOffload.enableChannelInfo "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/ChannelInfo"; - }; - }; - - systemd.services.spacebar-cs-gateway-offload = makeServerTsService { - description = "Spacebar Server - C# Gateway offload"; - environment = builtins.mapAttrs (_: val: builtins.toString val) ( - { - # things we set by default... - EVENT_TRANSMISSION = "unix"; - EVENT_SOCKET_PATH = "/run/spacebar/"; - } - // cfg.extraEnvironment - // { - # things we force... - # CONFIG_PATH = configFile; - CONFIG_READONLY = 1; - ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.gatewayOffload.listenPort}"; - STORAGE_LOCATION = cfg.cdnPath; - APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-gateway-offload.json" ( - lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.gatewayOffload.extraConfiguration - ); - } - ); - serviceConfig = { - ExecStart = "${self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-GatewayOffload}/bin/Spacebar.GatewayOffload"; - }; - }; - } - ); -} diff --git a/nix/modules/default/cs/offload-cs.nix b/nix/modules/default/cs/offload-cs.nix new file mode 100644 index 0000000..9186edc --- /dev/null +++ b/nix/modules/default/cs/offload-cs.nix @@ -0,0 +1,97 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + secrets = import ../secrets.nix { inherit lib config; }; + cfg = config.services.spacebarchat-server; + offloadCfg = cfg.offload; + jsonFormat = pkgs.formats.json { }; +in +{ + imports = [ ]; + options.services.spacebarchat-server.offload = lib.mkOption { + default = { }; + description = "Configuration for C# offload daemon."; + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Enable offload daemon (C#)."; + listenPort = lib.mkOption { + type = lib.types.port; + default = 3011; + description = "Port for the offload daemon to listen on."; + }; + extraConfiguration = lib.mkOption { + type = jsonFormat.type; + default = import ./default-appsettings-json.nix; + description = "Extra appsettings.json configuration for the offload daemon."; + }; + gateway = lib.mkOption { + description = "Gateway offloads"; + type = lib.types.submodule { + options = { + enableIdentify = lib.mkEnableOption "Enable offloading gateway opcode 2 (IDENTIFY)."; + enableGuildMembers = lib.mkEnableOption "Enable offloading gateway opcode 8 (REQUEST_GUILD_MEMBERS)."; + enableGuildSync = lib.mkEnableOption "Enable offloading gateway opcode 12 (GUILD_SYNC)."; + enableLazyRequest = lib.mkEnableOption "Enable offloading gateway opcode 14 (LAZY_REQUEST)."; + enableChannelStatuses = lib.mkEnableOption "Enable offloading gateway opcode 36 (CHANNEL_STATUSES)."; + enableChannelInfo = lib.mkEnableOption "Enable offloading gateway opcode 43 (CHANNEL_INFO)."; + }; + }; + }; + }; + }; + }; + + config = lib.mkIf offloadCfg.enable ( + let + makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + assertions = [ + (import ./assert-has-connection-string.nix "Gateway Offload" offloadCfg.extraConfiguration) + ]; + + services.spacebarchat-server.settings.offload = { + gateway = { + identifyUrl = lib.mkIf offloadCfg.gateway.enableIdentify "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/Identify"; + guildMembersUrl = lib.mkIf offloadCfg.gateway.enableGuildMembers "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/GuildMembers"; + guildSyncUrlUrl = lib.mkIf offloadCfg.gateway.enableGuildSync "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/GuildSync"; + lazyRequestUrl = lib.mkIf offloadCfg.gateway.enableLazyRequest "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/LazyRequest"; + channelStatusesUrl = lib.mkIf offloadCfg.gateway.enableChannelStatuses "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/ChannelStatuses"; + channelInfoUrl = lib.mkIf offloadCfg.gateway.enableChannelInfo "http://127.0.0.1:${builtins.toString offloadCfg.listenPort}/_spacebar/offload/gateway/ChannelInfo"; + }; + }; + + systemd.services.spacebar-cs-offload = makeServerTsService { + description = "Spacebar Server - C# offload"; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + # CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + ASPNETCORE_URLS = "http://0.0.0.0:${toString offloadCfg.listenPort}"; + STORAGE_LOCATION = cfg.cdnPath; + APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-offload.json" ( + lib.recursiveUpdate (import ./default-appsettings-json.nix) offloadCfg.extraConfiguration + ); + } + ); + serviceConfig = { + ExecStart = "${lib.getExe self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-Offload}"; + }; + }; + } + ); +} diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 001cdc7..fefb25c 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -20,7 +20,7 @@ (import ./gw-sharding.nix self) (import ./pion-sfu.nix self) (import ./cs/cdn-cs.nix self) - (import ./cs/gateway-offload-cs.nix self) + (import ./cs/offload-cs.nix self) (import ./cs/admin-api.nix self) (import ./cs/uapi.nix self) ]; diff --git a/nix/testVm/configuration.nix b/nix/testVm/configuration.nix index 10bee8e..f9df742 100644 --- a/nix/testVm/configuration.nix +++ b/nix/testVm/configuration.nix @@ -31,7 +31,7 @@ enable = true; apiEndpoint = sbLib.mkEndpointRaw "api.sb.localhost" 3001 8080 false; gatewayEndpoint = sbLib.mkEndpointRaw "gw.sb.localhost" 3002 8080 false; - extraGatewayPorts = lib.range 3100 3116; + extraGatewayPorts = lib.range 3100 3103; # 4 gateways total cdnEndpoint = sbLib.mkEndpointRaw "cdn.sb.localhost" 3003 8080 false; adminApiEndpoint = sbLib.mkEndpointRaw "admin.sb.localhost" 3004 8080 false; webrtcEndpoint = sbLib.mkEndpointRaw "voice.sb.localhost" 3005 8080 false; @@ -45,16 +45,24 @@ cdnSignatureIncludeUserAgent = false; cdnSignatureKey = "meow"; }; + limits = { + absoluteRate = { + register.enabled = false; + sendMessage.enabled = false; + }; + }; }; - gatewayOffload = { + offload = { enable = true; - enableIdentify = true; - enableGuildMembers = true; - enableGuildSync = true; - enableLazyRequest = true; - enableChannelStatuses = true; - enableChannelInfo = true; + gateway = { + enableIdentify = true; + enableGuildMembers = true; + enableGuildSync = true; + enableLazyRequest = true; + enableChannelStatuses = true; + enableChannelInfo = true; + }; extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; }; @@ -62,7 +70,7 @@ enable = true; extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; }; - + cdnCs = { enable = false; extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; @@ -80,7 +88,7 @@ extraEnvironment = { DATABASE = "postgres://postgres:postgres@127.0.0.1/spacebar"; -# LOG_REQUESTS = "-200,204,304"; + # LOG_REQUESTS = "-200,204,304"; LOG_REQUESTS = "-"; LOG_VALIDATION_ERRORS = true; #DB_LOGGING=true; @@ -93,6 +101,13 @@ in lib.trace ("Testing with config: " + builtins.toJSON cfg) cfg; services.nginx.enable = true; + services.nginx.recommendedOptimisation = true; + services.nginx.appendConfig = '' + worker_processes 6; + ''; + services.nginx.eventsConfig = '' + worker_connections 512; + ''; users.users.root.initialPassword = "root"; services.getty.autologinUser = "root"; diff --git a/nix/tests/test-bundle-starts.nix b/nix/tests/test-bundle-starts.nix index 3beedaa..16134f9 100644 --- a/nix/tests/test-bundle-starts.nix +++ b/nix/tests/test-bundle-starts.nix @@ -30,9 +30,11 @@ }; nginx.enable = true; - gatewayOffload = { + offload = { enable = true; - enableGuildSync = true; + gateway = { + enableGuildSync = true; + }; extraConfiguration.ConnectionStrings.Spacebar = "Host=127.0.0.1; Username=Spacebar; Password=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;"; }; };