mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-12 21:48:29 +09:00
113 lines
4.6 KiB
TypeScript
113 lines
4.6 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {AdminAuditReadActions} from '@app/api/admin/AdminAuditActions';
|
|
import {recordAdminRead, recordAdminWrite} from '@app/api/admin/AdminAuditRecorder';
|
|
import {Config} from '@app/api/Config';
|
|
import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
|
import {requireAdminACL} from '@app/api/middleware/AdminMiddleware';
|
|
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
|
|
import {OpenAPI} from '@app/api/middleware/ResponseTypeMiddleware';
|
|
import {RateLimitConfigs} from '@app/api/RateLimitConfig';
|
|
import type {HonoApp} from '@app/api/types/HonoEnv';
|
|
import {Validator} from '@app/api/Validator';
|
|
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
|
|
import {LIMIT_CATEGORY_LABELS, LIMIT_KEY_METADATA, LIMIT_KEYS} from '@fluxer/constants/src/LimitConfigMetadata';
|
|
import type {LimitConfigSnapshot, LimitRule} from '@fluxer/limits/src/LimitTypes';
|
|
import {LimitConfigGetResponse, LimitConfigUpdateRequest} from '@fluxer/schema/src/domains/admin/AdminSchemas';
|
|
|
|
function formatConfig(service: LimitConfigService) {
|
|
const config = service.getConfigSnapshot();
|
|
const defaults = service.getDefaultConfigSnapshot();
|
|
return {
|
|
limit_config: config,
|
|
limit_config_json: JSON.stringify(config, null, 2),
|
|
self_hosted: Config.instance.selfHosted,
|
|
defaults: Object.fromEntries(defaults.rules.map((rule) => [rule.id, rule.limits])),
|
|
metadata: LIMIT_KEY_METADATA,
|
|
categories: LIMIT_CATEGORY_LABELS,
|
|
limit_keys: LIMIT_KEYS,
|
|
};
|
|
}
|
|
|
|
function describeLimitRule(rule: LimitRule): string {
|
|
return JSON.stringify([
|
|
rule.filters?.traits ?? [],
|
|
rule.filters?.guildFeatures ?? [],
|
|
LIMIT_KEYS.map((key) => rule.limits[key] ?? null),
|
|
]);
|
|
}
|
|
|
|
function countChangedLimitRules(before: LimitConfigSnapshot, after: LimitConfigSnapshot): number {
|
|
const previous = new Map(before.rules.map((rule) => [rule.id, describeLimitRule(rule)]));
|
|
const next = new Map(after.rules.map((rule) => [rule.id, describeLimitRule(rule)]));
|
|
const ruleIds = new Set([...previous.keys(), ...next.keys()]);
|
|
return Array.from(ruleIds).filter((ruleId) => previous.get(ruleId) !== next.get(ruleId)).length;
|
|
}
|
|
|
|
export function LimitConfigAdminController(app: HonoApp) {
|
|
app.get(
|
|
'/admin/limit-config',
|
|
RateLimitMiddleware(RateLimitConfigs.ADMIN_LOOKUP),
|
|
requireAdminACL(AdminACLs.INSTANCE_LIMIT_CONFIG_VIEW),
|
|
OpenAPI({
|
|
operationId: 'get_admin_limit_config',
|
|
summary: 'Get limit configuration',
|
|
description:
|
|
'Retrieves rate limit configuration including message limits, upload limits, and request throttles. Shows defaults, metadata, and any modifications from defaults. Requires INSTANCE_LIMIT_CONFIG_VIEW permission.',
|
|
responseSchema: LimitConfigGetResponse,
|
|
statusCode: 200,
|
|
security: 'adminApiKey',
|
|
tags: 'Admin',
|
|
}),
|
|
async (ctx) => {
|
|
const limitConfigService = ctx.get('limitConfigService') as LimitConfigService;
|
|
const response = formatConfig(limitConfigService);
|
|
await recordAdminRead(ctx, {
|
|
targetType: 'limit_config',
|
|
targetId: 0n,
|
|
action: AdminAuditReadActions.GET_LIMIT_CONFIG,
|
|
metadata: {rule_count: response.limit_config.rules.length},
|
|
});
|
|
return ctx.json(response);
|
|
},
|
|
);
|
|
app.put(
|
|
'/admin/limit-config',
|
|
RateLimitMiddleware(RateLimitConfigs.ADMIN_USER_MODIFY),
|
|
requireAdminACL(AdminACLs.INSTANCE_LIMIT_CONFIG_UPDATE),
|
|
Validator('json', LimitConfigUpdateRequest),
|
|
OpenAPI({
|
|
operationId: 'replace_admin_limit_config',
|
|
summary: 'Replace limit configuration',
|
|
description:
|
|
'Replaces the stored limit configuration, which covers message throughput, upload sizes, and request throttles, with the supplied document. Changes apply immediately to all new operations. Requires INSTANCE_LIMIT_CONFIG_UPDATE permission.',
|
|
responseSchema: LimitConfigGetResponse,
|
|
statusCode: 200,
|
|
security: 'adminApiKey',
|
|
tags: 'Admin',
|
|
}),
|
|
async (ctx) => {
|
|
const limitConfigService = ctx.get('limitConfigService') as LimitConfigService;
|
|
const data = ctx.req.valid('json');
|
|
const normalized: LimitConfigSnapshot = {
|
|
...data.limit_config,
|
|
traitDefinitions: data.limit_config.traitDefinitions ?? [],
|
|
};
|
|
const previous = limitConfigService.getConfigSnapshot();
|
|
await limitConfigService.updateConfig(normalized);
|
|
const response = formatConfig(limitConfigService);
|
|
await recordAdminWrite(ctx, {
|
|
targetType: 'limit_config',
|
|
targetId: 0n,
|
|
action: 'update_limit_config',
|
|
metadata: {
|
|
rule_count: response.limit_config.rules.length,
|
|
changed_rule_count: countChangedLimitRules(previous, response.limit_config),
|
|
trait_definition_count: response.limit_config.traitDefinitions.length,
|
|
},
|
|
});
|
|
return ctx.json(response);
|
|
},
|
|
);
|
|
}
|