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 @@
+