Implement Owner Panel features and maintenance mode handling

- Introduced the Owner Panel with new routes and UI components for managing guilds, user blacklists, feature flags, and bot presence.
- Added maintenance mode functionality to prevent non-owner users from executing commands during maintenance periods.
- Enhanced localization with new keys for Owner Panel features in both English and German.
- Updated job processing to include a presence refresh job, ensuring the bot's status is regularly updated.
- Improved environment configuration to support owner user IDs for access control.
This commit is contained in:
smueller
2026-07-22 16:10:30 +02:00
parent bcbfa07697
commit 8a8aefb4fb
42 changed files with 2385 additions and 50 deletions

View File

@@ -0,0 +1,76 @@
import { Queue } from 'bullmq';
import { redis } from './redis';
export const OWNER_QUEUE_NAMES = [
'moderation',
'backups',
'automod',
'verification',
'reminders',
'giveaways',
'tickets',
'birthdays',
'stats',
'feeds',
'schedules',
'guild-backups',
'suggestions',
'presence'
] as const;
export type OwnerQueueName = (typeof OWNER_QUEUE_NAMES)[number];
const globalForOwnerQueues = globalThis as unknown as {
__nexumiOwnerQueues?: Map<string, Queue>;
};
function getQueue(name: string): Queue {
const cache = globalForOwnerQueues.__nexumiOwnerQueues ?? new Map<string, Queue>();
globalForOwnerQueues.__nexumiOwnerQueues = cache;
let queue = cache.get(name);
if (!queue) {
queue = new Queue(name, { connection: redis });
cache.set(name, queue);
}
return queue;
}
export interface QueueStats {
name: string;
waiting: number;
active: number;
completed: number;
failed: number;
delayed: number;
}
export async function getAllQueueStats(): Promise<QueueStats[]> {
return Promise.all(
OWNER_QUEUE_NAMES.map(async (name) => {
const queue = getQueue(name);
const counts = await queue.getJobCounts('wait', 'active', 'completed', 'failed', 'delayed');
return {
name,
waiting: counts.wait,
active: counts.active,
completed: counts.completed,
failed: counts.failed,
delayed: counts.delayed
};
})
);
}
export async function retryFailedJobs(queueName: string, limit = 25): Promise<number> {
if (!(OWNER_QUEUE_NAMES as readonly string[]).includes(queueName)) {
throw new Error('Unknown queue');
}
const queue = getQueue(queueName);
const failed = await queue.getFailed(0, limit - 1);
let retried = 0;
for (const job of failed) {
await job.retry();
retried += 1;
}
return retried;
}