import { storage } from '../storage'; import { eventBus } from '../routes/shared'; import { webPushService } from '../services/web-push.service'; /** * Sends web-push notifications about a task update to all task participants * who do NOT have an active SSE connection (i.e. are offline). * * Recipients = assignedTo + createdBy + all active taskUserAccess members, * minus the actor who triggered the event. */ export async function pushTaskUpdated(opts: { taskId: number; organizationId: number; formId: number; taskTitle: string; actorId: number; }): Promise { const { taskId, organizationId, formId, taskTitle, actorId } = opts; try { const candidates = new Set(); // Core fields: assignedTo / createdBy const task = await storage.getTask(taskId, organizationId); if (task) { if (task.assignedTo) candidates.add(task.assignedTo); if (task.createdBy) candidates.add(task.createdBy); } // All users who have interacted with the task (non-revoked access records) const accessList = await storage.getTaskUserAccess(taskId, organizationId); for (const entry of accessList) { if (!entry.revokedAt) { candidates.add(entry.userId); } } // Exclude the actor who triggered the event — they see it instantly candidates.delete(actorId); for (const uid of candidates) { if (!eventBus.isUserConnected(uid, organizationId)) { await webPushService.sendToUser(uid, organizationId, { title: 'Задача обновлена', body: taskTitle, icon: '/favicon.svg', url: `/forms/${formId}/tasks/${taskId}`, tag: `task-updated-${taskId}`, }); } } } catch (err) { console.error('[WebPush] pushTaskUpdated error:', err); } }