import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; import { BadRequestError, NotFoundError } from '@bytelyst/errors'; import { extractAuth } from '../../lib/auth.js'; import { PRODUCT_ID } from '../../lib/product-config.js'; import * as repo from './repository.js'; import { CreateSavedViewSchema, ListSavedViewsQuerySchema, UpdateSavedViewSchema, type SavedViewDoc, } from './types.js'; export async function savedViewRoutes(app: FastifyInstance) { app.get('/saved-views', async (req: FastifyRequest) => { const auth = await extractAuth(req); const parsed = ListSavedViewsQuerySchema.safeParse(req.query); if (!parsed.success) { throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')); } const result = await repo.listSavedViews(auth.sub, PRODUCT_ID, parsed.data); return { ...result, limit: parsed.data.limit, offset: parsed.data.offset }; }); app.post('/saved-views', async (req: FastifyRequest, reply: FastifyReply) => { const auth = await extractAuth(req); const parsed = CreateSavedViewSchema.safeParse(req.body); if (!parsed.success) { throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')); } const now = new Date().toISOString(); const doc: SavedViewDoc = { id: parsed.data.id, productId: PRODUCT_ID, userId: auth.sub, name: parsed.data.name, scope: parsed.data.scope, description: parsed.data.description, query: parsed.data.query, filters: parsed.data.filters, sortOrder: parsed.data.sortOrder, createdAt: now, updatedAt: now, createdBy: auth.sub, updatedBy: auth.sub, }; const created = await repo.createSavedView(doc); reply.code(201); return created; }); app.get('/saved-views/:id', async (req: FastifyRequest) => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const view = await repo.getSavedView(id, auth.sub); if (!view || view.productId !== PRODUCT_ID) { throw new NotFoundError('Saved view not found'); } return view; }); app.patch('/saved-views/:id', async (req: FastifyRequest) => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const parsed = UpdateSavedViewSchema.safeParse(req.body); if (!parsed.success) { throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')); } const existing = await repo.getSavedView(id, auth.sub); if (!existing || existing.productId !== PRODUCT_ID) { throw new NotFoundError('Saved view not found'); } const updated = await repo.updateSavedView(id, auth.sub, { ...parsed.data, updatedAt: new Date().toISOString(), updatedBy: auth.sub, }); if (!updated) { throw new NotFoundError('Saved view not found'); } return updated; }); app.delete('/saved-views/:id', async (req: FastifyRequest, reply: FastifyReply) => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const existing = await repo.getSavedView(id, auth.sub); if (!existing || existing.productId !== PRODUCT_ID) { throw new NotFoundError('Saved view not found'); } const deleted = await repo.deleteSavedView(id, auth.sub); if (!deleted) { throw new NotFoundError('Saved view not found'); } reply.code(204).send(); }); }