import { storage } from '../storage'; import { webPushService } from '../services/web-push.service'; import { eventBus } from '../routes/shared'; const SYNC_PUSH_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes const lastPushByOrganization = new Map(); /** * Sends a silent web-push to subscribed devices in the organization * telling them to run an offline sync (delta sync) because task data changed. * * The push has no visible notification; the Service Worker catches the * `SYNC_NEEDED` payload and registers a background sync. * * Throttled to one push per organization per cooldown window, and skipped * for users that currently have an active SSE connection (their open app * will sync via SSE immediately). */ export async function pushSyncNeeded(organizationId: number): Promise { try { const now = Date.now(); const lastPush = lastPushByOrganization.get(organizationId); if (lastPush != null && now - lastPush < SYNC_PUSH_COOLDOWN_MS) { console.log(`[WebPush] pushSyncNeeded skipped for org ${organizationId}: cooldown active`); return; } const subscriptions = await storage.getWebPushSubscriptionsByOrganization(organizationId); if (subscriptions.length === 0) return; let sent = 0; let skipped = 0; for (const sub of subscriptions) { try { // Если у пользователя открыто приложение с активным SSE, пуш не нужен — // он и так получит task_updated и запустит синхронизацию. if (eventBus.isUserConnected(sub.userId, organizationId)) { skipped++; continue; } const subscription = JSON.parse(sub.subscription); const success = await webPushService.sendNotification(subscription, { type: 'SYNC_NEEDED', data: { organizationId }, }); if (success) sent++; } catch (err) { console.error('[WebPush] pushSyncNeeded single subscription error:', err); } } lastPushByOrganization.set(organizationId, now); console.log(`[WebPush] pushSyncNeeded org=${organizationId} sent=${sent} skipped_sse=${skipped}/${subscriptions.length}`); } catch (err) { console.error('[WebPush] pushSyncNeeded error:', err); } }