import { and, desc, eq } from 'drizzle-orm'; import { Router } from 'express'; import { z } from 'zod'; import { db } from '../db/client'; import { pushNotifications } from '../db/schema'; import { requireDeviceAuth } from '../middleware/device-auth'; import { dispatchPushQueueOnce } from '../services/push'; const router = Router(); const listSchema = z.object({ status: z.string().optional(), limit: z.coerce.number().int().min(1).max(100).default(25), }); const notificationParamSchema = z.object({ notificationId: z.string().uuid(), }); router.get('/me', requireDeviceAuth, async (req, res) => { const parsed = listSchema.safeParse(req.query); if (!parsed.success) { res.status(400).json({ message: 'Invalid query params', errors: parsed.error.flatten() }); return; } const deviceAuth = req.deviceAuth; if (!deviceAuth) { res.status(401).json({ message: 'Unauthorized' }); return; } const result = await db.query.pushNotifications.findMany({ where: and( eq(pushNotifications.ownerUserId, deviceAuth.userId), eq(pushNotifications.recipientDeviceId, deviceAuth.deviceId), ), orderBy: [desc(pushNotifications.createdAt)], limit: parsed.data.limit, }); const filtered = parsed.data.status ? result.filter((item) => item.status === parsed.data.status) : result; res.json({ count: filtered.length, notifications: filtered }); }); router.post('/:notificationId/read', requireDeviceAuth, async (req, res) => { const parsedParams = notificationParamSchema.safeParse(req.params); if (!parsedParams.success) { res.status(400).json({ message: 'Invalid notificationId', errors: parsedParams.error.flatten() }); return; } const deviceAuth = req.deviceAuth; if (!deviceAuth) { res.status(401).json({ message: 'Unauthorized' }); return; } const [updated] = await db .update(pushNotifications) .set({ status: 'read', updatedAt: new Date(), }) .where( and( eq(pushNotifications.id, parsedParams.data.notificationId), eq(pushNotifications.ownerUserId, deviceAuth.userId), eq(pushNotifications.recipientDeviceId, deviceAuth.deviceId), ), ) .returning(); if (!updated) { res.status(404).json({ message: 'Notification not found' }); return; } res.json({ message: 'Notification marked as read', notification: updated }); }); router.post('/worker/dispatch', requireDeviceAuth, async (req, res) => { const deviceAuth = req.deviceAuth; if (!deviceAuth) { res.status(401).json({ message: 'Unauthorized' }); return; } const processed = await dispatchPushQueueOnce(); res.json({ message: 'Push queue dispatch completed', processed }); }); export default router;