wip: notifications

This commit is contained in:
Nikolai Giman
2026-03-22 20:01:05 +01:00
parent 5c4859743e
commit c403673f4d
108 changed files with 5662 additions and 582 deletions
+4
View File
@@ -0,0 +1,4 @@
export interface Dispatcher {
register(): void;
registerWorkers(): Promise<void>;
}
+4 -4
View File
@@ -3,10 +3,10 @@ import type { TasksSchemaTypeForSelect } from 'taskview-db-schemas';
import { $logger } from '../modules/logget';
export interface AppEvents {
'task.created': { task: TasksSchemaTypeForSelect; userId: number };
'task.updated': { task: TasksSchemaTypeForSelect; changes: Record<string, unknown>; userId: number };
'task.assigneesChanged': { taskId: number; userIds: number[] };
'task.deleted': { taskId: number; goalId: number };
'task.created': { task: TasksSchemaTypeForSelect; initiatorId: number };
'task.updated': { task: TasksSchemaTypeForSelect; changes: Record<string, unknown>; initiatorId: number };
'task.assigneesChanged': { taskId: number; userIds: number[]; initiatorId: number };
'task.deleted': { taskId: number; goalId: number; initiatorId: number };
}
type EventName = keyof AppEvents;
+22
View File
@@ -1,5 +1,6 @@
import { PgBoss } from 'pg-boss';
import { $logger } from '../modules/logget';
import { Database } from '../modules/db';
let boss: PgBoss | null = null;
@@ -29,3 +30,24 @@ export function getJobQueue(): PgBoss {
}
return boss;
}
/** Cancel jobs by singletonKey — finds and deletes matching queued jobs */
export async function cancelJobBySingletonKey(queueName: string, singletonKey: string): Promise<void> {
if (!boss) return;
const db = Database.getInstance();
const result = await db.query<{ id: string }>(
`SELECT id FROM pgboss.job WHERE name = $1 AND singleton_key = $2 AND state IN ('created', 'retry')`,
[queueName, singletonKey],
);
const count = result?.rows?.length ?? 0;
if (count === 0) {
$logger.info(`[JobQueue] Cancel: no jobs found for key="${singletonKey}"`);
return;
}
for (const row of result!.rows) {
await boss.deleteJob(queueName, row.id);
$logger.info(`[JobQueue] Deleted job id=${row.id} key="${singletonKey}"`);
}
}
+12 -9
View File
@@ -1,17 +1,20 @@
import { startJobQueue } from './JobQueue';
import { registerNotificationEventHandlers, registerNotificationWorkers } from '../tv-modules/notifications/notifications.events';
import type { Dispatcher } from './Dispatcher';
import { NotificationDispatcher } from '../tv-modules/notifications/NotificationDispatcher';
import { WebhooksDispatcher } from '../tv-modules/webhooks/WebhooksDispatcher';
const dispatchers: Dispatcher[] = [
new NotificationDispatcher(),
new WebhooksDispatcher(),
];
/**
* Register all event handlers (called on app init, before JobQueue starts)
*/
export function registerAllEventHandlers() {
registerNotificationEventHandlers();
dispatchers.forEach((d) => d.register());
}
/**
* Start JobQueue and register all job workers
*/
export async function startAllWorkers() {
await startJobQueue();
await registerNotificationWorkers();
for (const d of dispatchers) {
await d.registerWorkers();
}
}