Files
Nexumi/apps/webui/src/lib/queues.ts
smueller 47b6cfabbc Add suggestion status update job and queue integration
- Introduced a new `SuggestionStatusUpdateJob` type and corresponding worker to handle suggestion status updates in the bot's job processing.
- Added `suggestionsQueue` and `suggestionsQueueEvents` for managing suggestion-related jobs in both the bot and WebUI.
- Implemented `runSuggestionStatusUpdateJob` to facilitate staff actions on suggestions via the WebUI, ensuring consistent behavior with existing commands.
- Enhanced error handling for suggestion actions to improve user experience and reliability.
2026-07-22 15:39:31 +02:00

89 lines
3.6 KiB
TypeScript

import { Queue, QueueEvents } from 'bullmq';
import { redis } from './redis';
/**
* BullMQ queue/job names mirrored 1:1 from `apps/bot/src/queues.ts` and
* `apps/bot/src/jobs.ts`. The WebUI has no discord.js Client, so any
* dashboard action that needs to touch Discord (posting a giveaway message,
* drawing winners, restoring a guild backup) is enqueued here and picked up
* by the bot's existing workers - the WebUI never talks to Discord directly
* for these actions.
*/
export const giveawayQueueName = 'giveaways';
export const scheduleQueueName = 'schedules';
export const guildBackupQueueName = 'guild-backups';
export const suggestionsQueueName = 'suggestions';
const globalForQueues = globalThis as unknown as {
__nexumiGiveawayQueue?: Queue;
__nexumiScheduleQueue?: Queue;
__nexumiGuildBackupQueue?: Queue;
__nexumiSuggestionsQueue?: Queue;
__nexumiGiveawayQueueEvents?: QueueEvents;
__nexumiGuildBackupQueueEvents?: QueueEvents;
__nexumiSuggestionsQueueEvents?: QueueEvents;
};
export const giveawayQueue: Queue =
globalForQueues.__nexumiGiveawayQueue ?? new Queue(giveawayQueueName, { connection: redis });
export const scheduleQueue: Queue =
globalForQueues.__nexumiScheduleQueue ?? new Queue(scheduleQueueName, { connection: redis });
export const guildBackupQueue: Queue =
globalForQueues.__nexumiGuildBackupQueue ?? new Queue(guildBackupQueueName, { connection: redis });
export const suggestionsQueue: Queue =
globalForQueues.__nexumiSuggestionsQueue ?? new Queue(suggestionsQueueName, { connection: redis });
/**
* QueueEvents instances are only needed for jobs where the dashboard needs
* to wait for the bot to finish (e.g. ending a giveaway so the winners list
* is immediately visible). They open their own Redis connection, so they are
* cached on the global object the same way as the queues themselves.
*/
export const giveawayQueueEvents: QueueEvents =
globalForQueues.__nexumiGiveawayQueueEvents ??
new QueueEvents(giveawayQueueName, { connection: redis.duplicate() });
export const guildBackupQueueEvents: QueueEvents =
globalForQueues.__nexumiGuildBackupQueueEvents ??
new QueueEvents(guildBackupQueueName, { connection: redis.duplicate() });
export const suggestionsQueueEvents: QueueEvents =
globalForQueues.__nexumiSuggestionsQueueEvents ??
new QueueEvents(suggestionsQueueName, { connection: redis.duplicate() });
globalForQueues.__nexumiGiveawayQueue = giveawayQueue;
globalForQueues.__nexumiScheduleQueue = scheduleQueue;
globalForQueues.__nexumiGuildBackupQueue = guildBackupQueue;
globalForQueues.__nexumiSuggestionsQueue = suggestionsQueue;
globalForQueues.__nexumiGiveawayQueueEvents = giveawayQueueEvents;
globalForQueues.__nexumiGuildBackupQueueEvents = guildBackupQueueEvents;
globalForQueues.__nexumiSuggestionsQueueEvents = suggestionsQueueEvents;
const DEFAULT_WAIT_TIMEOUT_MS = 15_000;
/**
* Adds a job and waits (bounded) for the bot worker to finish it, so the
* dashboard can immediately reflect the result instead of polling. Falls
* back to "not confirmed" (does not throw) on timeout - the DB row created
* by the worker is still the source of truth and will show up on next load.
*/
export async function addJobAndAwait<T = unknown>(
queue: Queue,
queueEvents: QueueEvents,
jobName: string,
data: unknown,
options: Parameters<Queue['add']>[2] = {},
timeoutMs = DEFAULT_WAIT_TIMEOUT_MS
): Promise<{ confirmed: boolean; result: T | null }> {
const job = await queue.add(jobName, data, options);
try {
const result = (await job.waitUntilFinished(queueEvents, timeoutMs)) as T;
return { confirmed: true, result };
} catch {
return { confirmed: false, result: null };
}
}