diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs
new file mode 100644
index 00000000..45a9e437
--- /dev/null
+++ b/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs
@@ -0,0 +1,134 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Spacebar.Interop.Replication.Abstractions;
+using Spacebar.AdminApi.Extensions;
+using Spacebar.Models.AdminApi;
+using Spacebar.Interop.Authentication.AspNetCore;
+using Spacebar.Models.Db.Contexts;
+using Spacebar.Models.Db.Models;
+
+namespace Spacebar.AdminApi.Controllers;
+
+[ApiController]
+[Route("/channels")]
+public class ChannelController(
+ ILogger<ChannelController> logger,
+ SpacebarDbContext db,
+ IServiceProvider sp,
+ SpacebarAspNetAuthenticationService auth,
+ ISpacebarReplication replication
+) : ControllerBase {
+
+ [HttpDelete("{id}")]
+ public async Task DeleteById(string id) {
+ (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR);
+ replication.SendAsync(new() {
+ Origin = "AdminApi/DeleteChannelById",
+ ChannelId = id,
+ Event = "CHANNEL_DELETE",
+ Payload = await db.Channels.SingleAsync (x=>x.Id == id)
+ });
+
+ await db.Channels.Where(x => x.Id == id).ExecuteDeleteAsync();
+ }
+
+ private async IAsyncEnumerable<AsyncActionResult> DeleteMessagesForChannel(
+ // context
+ string? guildId, string channelId, string authorId,
+ // options
+ int messageDeleteChunkSize = 100
+ ) {
+ {
+ await using var ctx = sp.CreateAsyncScope();
+ await using var _db = ctx.ServiceProvider.GetRequiredService<SpacebarDbContext>();
+ var messagesInChannel = _db.Messages.AsNoTracking().Count(m => m.AuthorId == authorId && m.ChannelId == channelId && m.GuildId == guildId);
+ var remaining = messagesInChannel;
+ while (true) {
+ var messageIds = _db.Database.SqlQuery<string>($"""
+ DELETE FROM messages
+ WHERE id IN (
+ SELECT id FROM messages
+ WHERE author_id = {authorId}
+ AND channel_id = {channelId}
+ AND guild_id = {guildId}
+ LIMIT {messageDeleteChunkSize}
+ ) RETURNING id;
+ """).ToList();
+ if (messageIds.Count == 0) {
+ break;
+ }
+
+ await replication.SendAsync(new() {
+ ChannelId = channelId,
+ Event = "MESSAGE_BULK_DELETE",
+ Payload = new {
+ ids = messageIds,
+ channel_id = channelId,
+ guild_id = guildId,
+ },
+ Origin = "Admin API (GuildController.DeleteUser)",
+ });
+
+ yield return new("BULK_DELETED", new {
+ channel_id = channelId,
+ total = messagesInChannel,
+ deleted = messageIds.Count,
+ remaining = remaining -= messageIds.Count,
+ });
+ await Task.Yield();
+ }
+ }
+ }
+
+ private async IAsyncEnumerable<T> AggregateAsyncEnumerablesWithoutOrder<T>(params IEnumerable<IAsyncEnumerable<T>> enumerables) {
+ var enumerators = enumerables.Select(e => e.GetAsyncEnumerator()).ToList();
+ var tasks = enumerators.Select(e => e.MoveNextAsync().AsTask()).ToList();
+
+ try {
+ while (tasks.Count > 0) {
+ var completedTask = await Task.WhenAny(tasks);
+ var completedTaskIndex = tasks.IndexOf(completedTask);
+
+ if (completedTask.IsCanceled) {
+ try {
+ await enumerators[completedTaskIndex].DisposeAsync();
+ }
+ catch {
+ // ignored
+ }
+
+ enumerators.RemoveAt(completedTaskIndex);
+ tasks.RemoveAt(completedTaskIndex);
+ continue;
+ }
+
+ if (await completedTask) {
+ var enumerator = enumerators[completedTaskIndex];
+ yield return enumerator.Current;
+ tasks[completedTaskIndex] = enumerator.MoveNextAsync().AsTask();
+ }
+ else {
+ try {
+ await enumerators[completedTaskIndex].DisposeAsync();
+ }
+ catch {
+ // ignored
+ }
+
+ enumerators.RemoveAt(completedTaskIndex);
+ tasks.RemoveAt(completedTaskIndex);
+ }
+ }
+ }
+ finally {
+ foreach (var enumerator in enumerators) {
+ try {
+ await enumerator.DisposeAsync();
+ }
+ catch {
+ // ignored
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs
new file mode 100644
index 00000000..39d72b45
--- /dev/null
+++ b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Spacebar.Interop.Replication.Abstractions;
+using Spacebar.AdminApi.Extensions;
+using Spacebar.Models.AdminApi;
+using Spacebar.Interop.Authentication.AspNetCore;
+using Spacebar.Models.Db.Contexts;
+using Spacebar.Models.Db.Models;
+
+namespace Spacebar.AdminApi.Controllers;
+
+[ApiController]
+[Route("/discovery")]
+public class DiscoveryController(
+ ILogger<DiscoveryController> logger,
+ SpacebarDbContext db,
+ IServiceProvider sp,
+ SpacebarAspNetAuthenticationService auth,
+ ISpacebarReplication replication
+) : ControllerBase {
+ [HttpGet]
+ public async Task GetDiscoverableGuilds() {
+ (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR);
+ // var discoverableGuilds = db.Guilds
+ // .Where(x=>x.)
+ }
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs
index f140cccd..62df5a38 100644
--- a/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs
+++ b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs
@@ -98,10 +98,15 @@ public class GuildController(
member = new Member {
Id = userId,
GuildId = id,
- JoinedAt = DateTime.UtcNow,
+ JoinedAt = DateTime.Now,
PremiumSince = 0,
Roles = [await db.Roles.SingleAsync(r => r.Id == id)],
- Pending = false
+ Pending = false,
+ Settings = "{}",
+ Bio = "",
+ Mute = false,
+ Deaf = false,
+
};
await db.Members.AddAsync(member);
guild.MemberCount++;
diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs
new file mode 100644
index 00000000..862585b3
--- /dev/null
+++ b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs
@@ -0,0 +1,54 @@
+using System.Diagnostics;
+using ArcaneLibs;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+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 EmptyDmsController(
+ ILogger<EmptyDmsController> logger,
+ SpacebarAuthenticationConfiguration config,
+ SpacebarDbContext db,
+ IServiceProvider sp,
+ SpacebarAspNetAuthenticationService auth,
+ ISpacebarReplication replication
+) : ControllerBase {
+ [HttpGet("emptydms")]
+ public async IAsyncEnumerable<object> GetEmptyDms() {
+ (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR);
+
+ // TODO channel type enum
+ var channels = db.Channels
+ .Include(x=>x.Recipients)
+ .Include(x=>x.Messages)
+ .Where(x => x.Type == 1)
+ .Where(x => !x.Messages.Any() && x.Recipients.Count == 1)
+ ;
+
+ await using var db2Scope = sp.CreateAsyncScope();
+ await using var db3Scope = sp.CreateAsyncScope();
+ var db2 = db2Scope.ServiceProvider.GetRequiredService<SpacebarDbContext>();
+ var db3 = db3Scope.ServiceProvider.GetRequiredService<SpacebarDbContext>();
+ int count = 0;
+ await foreach (var channel in channels.AsAsyncEnumerable()) {
+ count++;
+ yield return new {
+ id = channel.Id,
+ msgs = await db2.Messages.Where(x => x.ChannelId == channel.Id).CountAsync(),
+ recips = await db3.Recipients.Where(x => x.ChannelId == channel.Id).CountAsync()
+ };
+ }
+
+ logger.LogInformation("Got {count} empty DM channels", count);
+
+ yield break;
+
+ }
+}
\ 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 7b76aabd..ebc43b6f 100644
--- a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs
+++ b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-using ArcaneLibs;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Spacebar.AdminApi.Extensions;
@@ -8,7 +6,6 @@ using Spacebar.Interop.Authentication.AspNetCore;
using Spacebar.Interop.Replication.Abstractions;
using Spacebar.Models.AdminApi;
using Spacebar.Models.Db.Contexts;
-using Spacebar.Models.Db.Models;
namespace Spacebar.AdminApi.Controllers;
@@ -184,6 +181,11 @@ public class UserController(
}
}
+ // [HttpGet("{id}/Dms")]
+ // public async IEnumerable<object> GetDmsAsync(string userId) {
+ // yield break; // TODO
+ // }
+
private async IAsyncEnumerable<AsyncActionResult> DeleteMessagesForChannel(
// context
string? guildId, string channelId, string authorId,
diff --git a/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http
new file mode 100644
index 00000000..06d9d174
--- /dev/null
+++ b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http
@@ -0,0 +1,12 @@
+@Spacebar.AdminApi_HostAddress = http://localhost:5112
+
+POST {{Spacebar.AdminApi_HostAddress}}/_spacebar/admin/guilds/1473141782615941382/force_join
+Content-Type: application/json
+Accept: application/json
+
+{
+ "MakeOwner": true,
+ "UserId": "1006598230156341276"
+}
+
+###
diff --git a/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json
index 03245773..0ee920ff 100644
--- a/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json
+++ b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json
@@ -2,16 +2,17 @@
"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": "Debug"
+ "Microsoft.AspNetCore": "Trace",
+ "Microsoft.AspNetCore.Mvc": "Warning",
+ "Microsoft.AspNetCore.HostFiltering": "Warning",
+ "Microsoft.AspNetCore.Cors": "Warning",
+ // "Microsoft.EntityFrameworkCore": "Warning"
+ "Microsoft.EntityFrameworkCore.Database.Command": "Debug",
+ "Microsoft.AspNetCore.Server.Kestrel.Connections": "Information"
}
},
"ConnectionStrings": {
- "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;",
+ "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;"
},
"RabbitMQ": {
"Host": "127.0.0.1",
@@ -19,9 +20,16 @@
"Username": "guest",
"Password": "guest"
},
- "SpacebarAdminApi": {
- "Enforce2FA": true,
- "OverrideUid": null,
- "DisableAuthentication": false
+ "Spacebar": {
+ "Authentication": {
+ "Enforce2FA": false,
+ "OverrideUid": "1006598230156341276",
+ "DisableAuthentication": true,
+ "PublicKeyPath": "../../../jwt.key.pub",
+ "PrivateKeyPath": "../../../jwt.key"
+ },
+ "UnixSocketReplication": {
+ "SocketDir": "../../.."
+ }
}
}
|