mirror of
https://github.com/spacebarchat/server.git
synced 2026-03-29 09:50:20 +00:00
Admin API work
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.)
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
12
extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http
Normal file
12
extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http
Normal file
@@ -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"
|
||||
}
|
||||
|
||||
###
|
||||
@@ -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": "../../.."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user