SPB Git

Repository Public

index.ts 1.7 KB 32 lines typescript

1import type { FastifyInstance } from 'fastify';2import { z } from 'zod';3import { Id, Notification } from '@cr/shared';4import { authOf, parse, type Ctx } from '../../http.js';5import { NOTIF_COLS, notificationDto, type NotificationRow } from './service.js';67const ListQuery = z.object({ limit: z.coerce.number().int().min(1).max(200).default(50), unread: z.enum(['0', '1']).optional() });8const ReadBody = z.object({ ids: z.array(Id).max(500).optional(), all: z.boolean().optional() });910export default async function notificationsPlugin(app: FastifyInstance, ctx: Ctx): Promise<void> {11  app.get('/notifications', { config: { doc: { summary: 'Recent notifications', query: ListQuery, response: z.array(Notification), responseName: 'NotificationList' } } }, async (req) => {12    const a = authOf(req);13    const q = parse(ListQuery, req.query);14    const rows = await ctx.db.q<NotificationRow>(15      `SELECT ${NOTIF_COLS} FROM notifications WHERE user_id = $1 AND ($2::boolean IS NOT TRUE OR read_at IS NULL)16        ORDER BY created_at DESC LIMIT $3`,17      [a.userId, q.unread === '1', q.limit],18    );19    return rows.map(notificationDto);20  });21  app.post('/notifications/read', { config: { doc: { summary: 'Mark notifications read ({ids} or {all:true})', body: ReadBody, bodyName: 'NotificationsRead' } } }, async (req) => {22    const a = authOf(req);23    const body = parse(ReadBody, req.body);24    const rows = await ctx.db.q(25      `UPDATE notifications SET read_at = now()26        WHERE user_id = $1 AND read_at IS NULL AND ($2::boolean OR id = ANY($3::uuid[])) RETURNING id`,27      [a.userId, body.all === true, body.ids ?? []],28    );29    return { ok: true, updated: rows.length };30  });31}32
Files main
Menu

Theme