diff --git a/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/Spacebar.DataMappings.AdminApi.csproj b/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/Spacebar.DataMappings.AdminApi.csproj new file mode 100644 index 000000000..809a0b7e5 --- /dev/null +++ b/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/Spacebar.DataMappings.AdminApi.csproj @@ -0,0 +1,15 @@ + + + + net10.0 + enable + enable + + + + + + + + + diff --git a/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/User.cs b/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/User.cs new file mode 100644 index 000000000..ea9c8255a --- /dev/null +++ b/extra/admin-api/DataMappings/Spacebar.DataMappings.AdminApi/User.cs @@ -0,0 +1,26 @@ +using Spacebar.Models.AdminApi; + +namespace Spacebar.DataMappings.AdminApi; + +public static class User +{ + // public static UserModel ToPartialUser(this Models.Db.Models.User user) + // { + // return new PartialUser() { + // Id = user.Id, + // Discriminator = user.Discriminator, + // Username = user.Username, + // AccentColor = user.AccentColor, + // Avatar = user.Avatar, + // AvatarDecorationData = user.AvatarDecorationData, + // Banner = user.Banner, + // Bot = user.Bot, + // Collectibles = user.Collectibles, + // DisplayNameStyles = user.DisplayNameStyles, + // // GlobalName = x.GlobalName, + // PrimaryGuild = user.PrimaryGuild, + // PublicFlags = user.PublicFlags, + // System = user.System, + // }; + // } +} \ No newline at end of file diff --git a/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/Spacebar.DataMappings.Generic.csproj b/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/Spacebar.DataMappings.Generic.csproj index 3f8936a6f..452a2e0e4 100644 --- a/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/Spacebar.DataMappings.Generic.csproj +++ b/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/Spacebar.DataMappings.Generic.csproj @@ -7,9 +7,9 @@ - + - + diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs new file mode 100644 index 000000000..228606f79 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs @@ -0,0 +1,70 @@ +using System.Diagnostics; +using ArcaneLibs; +using Microsoft.AspNetCore.Mvc; +using Spacebar.AdminApi.Extensions; +using Spacebar.Interop.Authentication; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.AdminApi; +using Spacebar.Models.Db.Contexts; + +namespace Spacebar.AdminApi.Controllers.TestControllers; + +[ApiController] +public class IpcTestController( + ILogger logger, + SpacebarAuthenticationConfiguration config, + SpacebarDbContext db, + IServiceProvider sp, + SpacebarAspNetAuthenticationService auth, + ISpacebarReplication replication +) : ControllerBase { + [HttpGet("test")] + public async IAsyncEnumerable Test() { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + var guildId = "1006649183970562092"; + // var roleId = "1006706520514028812"; //Administrator + var roleId = "1391303296148639051"; //Spacebar Maintainer + // int color = 16711680; //Administrator + int color = 99839; //Spacebar Maintainer + + int framerate = 30; + float delay = 1000f / framerate; + var secondsPerRotation = 6.243f; + // use delay, 255f = one rotation, lengthFactor = iterations to make a full rotation + var lengthFactor = (secondsPerRotation * 1000f / delay); + Console.WriteLine("Length factor: {0}, RPS: {1}", lengthFactor, 0); + var re = new RainbowEnumerator(lengthFactor: lengthFactor, offset: color, skip: 1); + var sw = Stopwatch.StartNew(); + while (true) { + var clr = re.Next(); + color = clr.r << 16 | clr.g << 8 | clr.b; + await replication.SendAsync(new() { + Event = "GUILD_ROLE_UPDATE", + GuildId = guildId, + Origin = "Admin API (GET /users/test)", + Payload = new { + guild_id = guildId, + role = new { + id = roleId, + guild_id = guildId, + color, + hoist = false, + managed = false, + mentionable = true, + name = "Spacebar Maintainer", + permissions = "8", + position = 5, + unicode_emoji = "", + flags = 0 + } + } + }); + + yield return $"{clr.r:X2} {clr.g:X2} {clr.b:X2} | {color:X8} | {sw.Elapsed} (waiting {Math.Max(0, (int)delay - (int)sw.ElapsedMilliseconds)} out of {delay} ms)"; + await Task.Delay(Math.Max(0, (int)delay - (int)sw.ElapsedMilliseconds)); + sw.Restart(); + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs index e9dfa15ce..7b76aabde 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs @@ -22,6 +22,10 @@ public class UserController( SpacebarAspNetAuthenticationService auth, ISpacebarReplication replication ) : ControllerBase { + /// + /// Get all users + /// + /// List of user objects [HttpGet] public async IAsyncEnumerable Get() { (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); @@ -77,6 +81,63 @@ public class UserController( } } + /// + /// Get user by ID + /// + /// User ID + /// User object + [HttpGet("{id}")] + public async Task GetById(string id) { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + return await db.Users + .Include(user => user.ApplicationBotUser) + .Include(user => user.MessageAuthors) + .Include(user => user.Sessions) + .Include(user => user.Templates) + .Include(user => user.VoiceStates) + .Include(user => user.Guilds) + .Select(x => new UserModel { + Id = x.Id, + Username = x.Username, + Discriminator = x.Discriminator, + Avatar = x.Avatar, + AccentColor = x.AccentColor, + Banner = x.Banner, + ThemeColors = x.ThemeColors, + Pronouns = x.Pronouns, + Phone = x.Phone, + Desktop = x.Desktop, + Mobile = x.Mobile, + Premium = x.Premium, + PremiumType = x.PremiumType, + Bot = x.Bot, + Bio = x.Bio, + System = x.System, + NsfwAllowed = x.NsfwAllowed, + MfaEnabled = x.MfaEnabled, + WebauthnEnabled = x.WebauthnEnabled, + CreatedAt = x.CreatedAt, + PremiumSince = x.PremiumSince, + Verified = x.Verified, + Disabled = x.Disabled, + Deleted = x.Deleted, + Email = x.Email, + Flags = x.Flags, + PublicFlags = x.PublicFlags, + Rights = x.Rights, + ApplicationBotUser = x.ApplicationBotUser == null ? null : new(), + ConnectedAccounts = new List(), + MessageCount = x.MessageAuthors.Count, // This property is weirdly named due to scaffolding, might patch later + SessionCount = x.Sessions.Count, + TemplateCount = x.Templates.Count, + VoiceStateCount = x.VoiceStates.Count, + GuildCount = x.Guilds.Count, + OwnedGuildCount = x.Guilds.Count(g => g.OwnerId == x.Id) + }) + .SingleAsync(x => x.Id == id); + } + [HttpGet("{id}/delete")] public async IAsyncEnumerable DeleteUser(string id, [FromQuery] int messageDeleteChunkSize = 100) { (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); @@ -171,129 +232,6 @@ public class UserController( } } - [HttpGet("duplicate")] - public async Task Duplicate() { - (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); - - var msg = db.Messages.First(); - var channels = db.Channels.Select(x => new { x.Id, x.GuildId }).ToList(); - int count = 1; - while (true) { - foreach (var channel in channels) { - var newMsg = new Message { - Id = $"{Random.Shared.NextInt64()}", - ChannelId = channel.Id, - GuildId = channel.GuildId, - AuthorId = msg.AuthorId, - Content = msg.Content, - MemberId = msg.MemberId, - Timestamp = msg.Timestamp, - EditedTimestamp = msg.EditedTimestamp, - Tts = msg.Tts, - MentionEveryone = msg.MentionEveryone, - Attachments = msg.Attachments, - Embeds = msg.Embeds, - Reactions = msg.Reactions, - Nonce = msg.Nonce, - PinnedAt = msg.PinnedAt, - Type = msg.Type, - }; - db.Messages.Add(newMsg); - count++; - } - - if (count % 100 == 0) { - await db.SaveChangesAsync(); - await db.Database.ExecuteSqlRawAsync("VACUUM FULL messages"); - } - - if (count >= 100_000) { - await db.SaveChangesAsync(); - await db.Database.ExecuteSqlRawAsync("VACUUM FULL messages"); - await db.Database.ExecuteSqlRawAsync("REINDEX TABLE messages"); - return Ok(); - } - } - } - - [HttpGet("duplicate/{id}")] - public async Task DuplicateMessage(ulong id, [FromQuery] int count = 100) { - (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); - - var msg = await db.Messages.FindAsync(id.ToString()); - int createdCount = 1; - while (true) { - var newMsg = new Message { - Id = $"{Random.Shared.NextInt64()}", - ChannelId = msg.ChannelId, - GuildId = msg.GuildId, - AuthorId = msg.AuthorId, - Content = msg.Content, - MemberId = msg.MemberId, - Timestamp = msg.Timestamp, - EditedTimestamp = msg.EditedTimestamp, - Tts = msg.Tts, - MentionEveryone = msg.MentionEveryone, - Attachments = msg.Attachments, - Embeds = msg.Embeds, - Reactions = msg.Reactions, - Nonce = msg.Nonce, - PinnedAt = msg.PinnedAt, - Type = msg.Type, - }; - db.Messages.Add(newMsg); - createdCount++; - - if (createdCount % 100 == 0) { - await db.SaveChangesAsync(); - } - - if (createdCount >= count) { - await db.SaveChangesAsync(); - await db.Database.ExecuteSqlRawAsync("VACUUM FULL messages"); - await db.Database.ExecuteSqlRawAsync("REINDEX TABLE messages"); - return Ok(); - } - } - - await db.SaveChangesAsync(); - await db.Database.ExecuteSqlRawAsync("VACUUM FULL messages"); - - return Ok(); - } - - [HttpGet("truncate_messages")] - public async Task TruncateMessages() { - (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); - - var channels = db.Channels.Select(x => new { x.Id, x.GuildId }).ToList(); - - var ss = new SemaphoreSlim(12, 12); - - async Task TruncateChannelMessages(string channelId, string guildId) { - await ss.WaitAsync(); - var tasks = Enumerable.Range(0, 99).Select(i => Task.Run(async () => { - await using var scope = sp.CreateAsyncScope(); - await using var _db = scope.ServiceProvider.GetRequiredService(); - // set timeout - _db.Database.SetCommandTimeout(6000); - await _db.Database.ExecuteSqlAsync($""" - DELETE FROM messages - WHERE channel_id = '{channelId}' - AND guild_id = '{guildId}' - AND id LIKE '%{i:00}'; - """); - - Console.WriteLine($"Truncated messages for {channelId} in {guildId} ending with {i}"); - })).ToList(); - await Task.WhenAll(tasks); - ss.Release(); - } - - var tasks = channels.Select(c => TruncateChannelMessages(c.Id, c.GuildId)).ToList(); - await Task.WhenAll(tasks); - } - private async IAsyncEnumerable AggregateAsyncEnumerablesWithoutOrder(params IEnumerable> enumerables) { (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); @@ -347,75 +285,4 @@ public class UserController( } } } - - // { - // "op": 0, - // "t": "GUILD_ROLE_UPDATE", - // "d": { - // "guild_id": "1006649183970562092", - // "role": { - // "id": "1006706520514028812", - // "guild_id": "1006649183970562092", - // "color": 16711680, - // "hoist": true, - // "managed": false, - // "mentionable": true, - // "name": "Adminstrator", - // "permissions": "9", - // "position": 5, - // "unicode_emoji": "💖", - // "flags": 0 - // } - // }, - // "s": 38 - // } - - [HttpGet("test")] - public async IAsyncEnumerable Test() { - (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); - - var guildId = "1006649183970562092"; - // var roleId = "1006706520514028812"; //Administrator - var roleId = "1391303296148639051"; //Spacebar Maintainer - // int color = 16711680; //Administrator - int color = 99839; //Spacebar Maintainer - - int framerate = 30; - float delay = 1000f / framerate; - var secondsPerRotation = 6.243f; - // use delay, 255f = one rotation, lengthFactor = iterations to make a full rotation - var lengthFactor = (secondsPerRotation * 1000f / delay); - Console.WriteLine("Length factor: {0}, RPS: {1}", lengthFactor, 0); - var re = new RainbowEnumerator(lengthFactor: lengthFactor, offset: color, skip: 1); - var sw = Stopwatch.StartNew(); - while (true) { - var clr = re.Next(); - color = clr.r << 16 | clr.g << 8 | clr.b; - await replication.SendAsync(new() { - Event = "GUILD_ROLE_UPDATE", - GuildId = guildId, - Origin = "Admin API (GET /users/test)", - Payload = new { - guild_id = guildId, - role = new { - id = roleId, - guild_id = guildId, - color, - hoist = false, - managed = false, - mentionable = true, - name = "Spacebar Maintainer", - permissions = "8", - position = 5, - unicode_emoji = "", - flags = 0 - } - } - }); - - yield return $"{clr.r:X2} {clr.g:X2} {clr.b:X2} | {color:X8} | {sw.Elapsed} (waiting {Math.Max(0, (int)delay - (int)sw.ElapsedMilliseconds)} out of {delay} ms)"; - await Task.Delay(Math.Max(0, (int)delay - (int)sw.ElapsedMilliseconds)); - sw.Restart(); - } - } } \ No newline at end of file diff --git a/extra/admin-api/SpacebarAdminAPI.slnx b/extra/admin-api/SpacebarAdminAPI.slnx index 56b9e3320..34ed54028 100644 --- a/extra/admin-api/SpacebarAdminAPI.slnx +++ b/extra/admin-api/SpacebarAdminAPI.slnx @@ -5,6 +5,7 @@ +