refactor progress
This commit is contained in:
441
packages/api/src/admin/controllers/GuildAdminController.tsx
Normal file
441
packages/api/src/admin/controllers/GuildAdminController.tsx
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {createGuildID} from '@fluxer/api/src/BrandedTypes';
|
||||
import {requireAdminACL} from '@fluxer/api/src/middleware/AdminMiddleware';
|
||||
import {RateLimitMiddleware} from '@fluxer/api/src/middleware/RateLimitMiddleware';
|
||||
import {OpenAPI} from '@fluxer/api/src/middleware/ResponseTypeMiddleware';
|
||||
import {RateLimitConfigs} from '@fluxer/api/src/RateLimitConfig';
|
||||
import {AdminRateLimitConfigs} from '@fluxer/api/src/rate_limit_configs/AdminRateLimitConfig';
|
||||
import type {HonoApp} from '@fluxer/api/src/types/HonoEnv';
|
||||
import {Validator} from '@fluxer/api/src/Validator';
|
||||
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
|
||||
import {
|
||||
BanGuildMemberRequest,
|
||||
ClearGuildFieldsRequest,
|
||||
DeleteGuildRequest,
|
||||
ForceAddUserToGuildRequest,
|
||||
KickGuildMemberRequest,
|
||||
ListGuildMembersRequest,
|
||||
LookupGuildRequest,
|
||||
ReloadGuildRequest,
|
||||
ShutdownGuildRequest,
|
||||
TransferGuildOwnershipRequest,
|
||||
UpdateGuildFeaturesRequest,
|
||||
UpdateGuildNameRequest,
|
||||
UpdateGuildSettingsRequest,
|
||||
UpdateGuildVanityRequest,
|
||||
} from '@fluxer/schema/src/domains/admin/AdminGuildSchemas';
|
||||
import {
|
||||
GuildUpdateResponse,
|
||||
ListGuildEmojisResponse,
|
||||
ListGuildMembersResponse,
|
||||
ListGuildStickersResponse,
|
||||
LookupGuildResponse,
|
||||
SuccessResponse,
|
||||
} from '@fluxer/schema/src/domains/admin/AdminSchemas';
|
||||
import {GuildIdParam} from '@fluxer/schema/src/domains/common/CommonParamSchemas';
|
||||
|
||||
export function GuildAdminController(app: HonoApp) {
|
||||
app.post(
|
||||
'/admin/guilds/lookup',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP),
|
||||
requireAdminACL(AdminACLs.GUILD_LOOKUP),
|
||||
Validator('json', LookupGuildRequest),
|
||||
OpenAPI({
|
||||
operationId: 'lookup_guild',
|
||||
summary: 'Look up guild',
|
||||
description:
|
||||
'Retrieves complete guild details including metadata, settings, and statistics. Look up by guild ID or vanity slug. Requires GUILD_LOOKUP permission.',
|
||||
responseSchema: LookupGuildResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
return ctx.json(await adminService.lookupGuild(ctx.req.valid('json')));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/list-members',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP),
|
||||
requireAdminACL(AdminACLs.GUILD_LIST_MEMBERS),
|
||||
Validator('json', ListGuildMembersRequest),
|
||||
OpenAPI({
|
||||
operationId: 'list_guild_members',
|
||||
summary: 'List guild members',
|
||||
description:
|
||||
'Lists all guild members with pagination. Returns member IDs, join dates, and roles. Requires GUILD_LIST_MEMBERS permission.',
|
||||
responseSchema: ListGuildMembersResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
return ctx.json(await adminService.listGuildMembers(ctx.req.valid('json')));
|
||||
},
|
||||
);
|
||||
|
||||
app.get(
|
||||
'/admin/guilds/:guild_id/emojis',
|
||||
RateLimitMiddleware(AdminRateLimitConfigs.ADMIN_LOOKUP),
|
||||
requireAdminACL(AdminACLs.ASSET_PURGE),
|
||||
Validator('param', GuildIdParam),
|
||||
OpenAPI({
|
||||
operationId: 'list_guild_emojis',
|
||||
summary: 'List guild emojis',
|
||||
description:
|
||||
'Lists all custom emojis in a guild. Returns ID, name, and creation date. Used for asset inventory and purge operations. Requires ASSET_PURGE permission.',
|
||||
responseSchema: ListGuildEmojisResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
return ctx.json(await adminService.listGuildEmojis(guildId));
|
||||
},
|
||||
);
|
||||
|
||||
app.get(
|
||||
'/admin/guilds/:guild_id/stickers',
|
||||
RateLimitMiddleware(AdminRateLimitConfigs.ADMIN_LOOKUP),
|
||||
requireAdminACL(AdminACLs.ASSET_PURGE),
|
||||
Validator('param', GuildIdParam),
|
||||
OpenAPI({
|
||||
operationId: 'list_guild_stickers',
|
||||
summary: 'List guild stickers',
|
||||
description:
|
||||
'Lists all stickers in a guild. Returns ID, name, and asset information. Used for asset inventory and purge operations. Requires ASSET_PURGE permission.',
|
||||
responseSchema: ListGuildStickersResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
return ctx.json(await adminService.listGuildStickers(guildId));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/clear-fields',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_UPDATE_SETTINGS),
|
||||
Validator('json', ClearGuildFieldsRequest),
|
||||
OpenAPI({
|
||||
operationId: 'clear_guild_fields',
|
||||
summary: 'Clear guild fields',
|
||||
description:
|
||||
'Clears specified optional guild fields such as icon, banner, or description. Logged to audit log. Requires GUILD_UPDATE_SETTINGS permission.',
|
||||
responseSchema: null,
|
||||
statusCode: 204,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
await adminService.clearGuildFields(ctx.req.valid('json'), adminUserId, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/update-features',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_UPDATE_FEATURES),
|
||||
Validator('json', UpdateGuildFeaturesRequest),
|
||||
OpenAPI({
|
||||
operationId: 'update_guild_features',
|
||||
summary: 'Update guild features',
|
||||
description:
|
||||
'Enables or disables guild feature flags. Modifies verification levels and community settings. Changes are logged to audit log. Requires GUILD_UPDATE_FEATURES permission.',
|
||||
responseSchema: GuildUpdateResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
const body = ctx.req.valid('json');
|
||||
const guildId = createGuildID(body.guild_id);
|
||||
return ctx.json(
|
||||
await adminService.updateGuildFeatures({
|
||||
guildId,
|
||||
addFeatures: body.add_features,
|
||||
removeFeatures: body.remove_features,
|
||||
adminUserId,
|
||||
auditLogReason,
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/update-name',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_UPDATE_NAME),
|
||||
Validator('json', UpdateGuildNameRequest),
|
||||
OpenAPI({
|
||||
operationId: 'update_guild_name',
|
||||
summary: 'Update guild name',
|
||||
description:
|
||||
'Changes a guild name. Used for removing inappropriate names or correcting display issues. Logged to audit log. Requires GUILD_UPDATE_NAME permission.',
|
||||
responseSchema: GuildUpdateResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
return ctx.json(await adminService.updateGuildName(ctx.req.valid('json'), adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/update-settings',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_UPDATE_SETTINGS),
|
||||
Validator('json', UpdateGuildSettingsRequest),
|
||||
OpenAPI({
|
||||
operationId: 'update_guild_settings',
|
||||
summary: 'Update guild settings',
|
||||
description:
|
||||
'Modifies guild configuration including description, region, language and other settings. Logged to audit log. Requires GUILD_UPDATE_SETTINGS permission.',
|
||||
responseSchema: GuildUpdateResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
return ctx.json(await adminService.updateGuildSettings(ctx.req.valid('json'), adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/transfer-ownership',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_TRANSFER_OWNERSHIP),
|
||||
Validator('json', TransferGuildOwnershipRequest),
|
||||
OpenAPI({
|
||||
operationId: 'transfer_guild_ownership',
|
||||
summary: 'Transfer guild ownership',
|
||||
description:
|
||||
'Transfers guild ownership to another user. Used when owner is inactive or for administrative recovery. Logged to audit log. Requires GUILD_TRANSFER_OWNERSHIP permission.',
|
||||
responseSchema: GuildUpdateResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
return ctx.json(await adminService.transferGuildOwnership(ctx.req.valid('json'), adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/update-vanity',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_UPDATE_VANITY),
|
||||
Validator('json', UpdateGuildVanityRequest),
|
||||
OpenAPI({
|
||||
operationId: 'update_guild_vanity',
|
||||
summary: 'Update guild vanity',
|
||||
description:
|
||||
'Updates a guild vanity URL slug. Sets custom short URL and prevents duplicate slugs. Logged to audit log. Requires GUILD_UPDATE_VANITY permission.',
|
||||
responseSchema: GuildUpdateResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
return ctx.json(await adminService.updateGuildVanity(ctx.req.valid('json'), adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/force-add-user',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_FORCE_ADD_MEMBER),
|
||||
Validator('json', ForceAddUserToGuildRequest),
|
||||
OpenAPI({
|
||||
operationId: 'force_add_user_to_guild',
|
||||
summary: 'Force add user to guild',
|
||||
description:
|
||||
'Forcefully adds a user to a guild. Bypasses normal invite flow for administrative account recovery. Logged to audit log. Requires GUILD_FORCE_ADD_MEMBER permission.',
|
||||
responseSchema: SuccessResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
const requestCache = ctx.get('requestCache');
|
||||
return ctx.json(
|
||||
await adminService.forceAddUserToGuild({
|
||||
data: ctx.req.valid('json'),
|
||||
requestCache,
|
||||
adminUserId,
|
||||
auditLogReason,
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/ban-member',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_BAN_MEMBER),
|
||||
Validator('json', BanGuildMemberRequest),
|
||||
OpenAPI({
|
||||
operationId: 'ban_guild_member',
|
||||
summary: 'Ban guild member',
|
||||
description:
|
||||
'Permanently bans a user from a guild. Prevents user from joining. Logged to audit log. Requires GUILD_BAN_MEMBER permission.',
|
||||
responseSchema: null,
|
||||
statusCode: 204,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
await adminService.banGuildMember(ctx.req.valid('json'), adminUserId, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/kick-member',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_KICK_MEMBER),
|
||||
Validator('json', KickGuildMemberRequest),
|
||||
OpenAPI({
|
||||
operationId: 'kick_guild_member',
|
||||
summary: 'Kick guild member',
|
||||
description:
|
||||
'Temporarily removes a user from a guild. User can rejoin. Logged to audit log. Requires GUILD_KICK_MEMBER permission.',
|
||||
responseSchema: null,
|
||||
statusCode: 204,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
await adminService.kickGuildMember(ctx.req.valid('json'), adminUserId, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/reload',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_RELOAD),
|
||||
Validator('json', ReloadGuildRequest),
|
||||
OpenAPI({
|
||||
operationId: 'reload_guild',
|
||||
summary: 'Reload guild',
|
||||
description:
|
||||
'Reloads a single guild state from database. Used to recover from corruption or sync issues. Logged to audit log. Requires GUILD_RELOAD permission.',
|
||||
responseSchema: SuccessResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
const body = ctx.req.valid('json');
|
||||
return ctx.json(await adminService.reloadGuild(body.guild_id, adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/shutdown',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_SHUTDOWN),
|
||||
Validator('json', ShutdownGuildRequest),
|
||||
OpenAPI({
|
||||
operationId: 'shutdown_guild',
|
||||
summary: 'Shutdown guild',
|
||||
description:
|
||||
'Shuts down and unloads a guild from the gateway. Guild data remains in database. Used for emergency resource cleanup. Logged to audit log. Requires GUILD_SHUTDOWN permission.',
|
||||
responseSchema: SuccessResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
const body = ctx.req.valid('json');
|
||||
return ctx.json(await adminService.shutdownGuild(body.guild_id, adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/guilds/delete',
|
||||
RateLimitMiddleware(RateLimitConfigs.ADMIN_GUILD_MODIFY),
|
||||
requireAdminACL(AdminACLs.GUILD_DELETE),
|
||||
Validator('json', DeleteGuildRequest),
|
||||
OpenAPI({
|
||||
operationId: 'delete_guild',
|
||||
summary: 'Delete guild',
|
||||
description:
|
||||
'Permanently deletes a guild. Deletes all channels, messages, and settings. Irreversible operation. Logged to audit log. Requires GUILD_DELETE permission.',
|
||||
responseSchema: SuccessResponse,
|
||||
statusCode: 200,
|
||||
security: 'adminApiKey',
|
||||
tags: 'Admin',
|
||||
}),
|
||||
async (ctx) => {
|
||||
const adminService = ctx.get('adminService');
|
||||
const adminUserId = ctx.get('adminUserId');
|
||||
const auditLogReason = ctx.get('auditLogReason');
|
||||
const body = ctx.req.valid('json');
|
||||
return ctx.json(await adminService.deleteGuild(body.guild_id, adminUserId, auditLogReason));
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user