import type { FastifyApp } from '@bytelyst/fastify-core'; import { BadRequestError, NotFoundError } from '../../lib/errors.js'; import { extractAuth } from '../../lib/auth.js'; import { PRODUCT_ID } from '../../lib/product-config.js'; import * as repo from './repository.js'; import { CreateNoteSchema, ListNotesQuerySchema, UpdateNoteSchema, type NoteDoc } from './types.js'; type RouteApp = Omit; export async function noteRoutes(app: RouteApp) { app.get('/notes/search', async req => { const auth = await extractAuth(req); const parsed = ListNotesQuerySchema.safeParse(req.query); if (!parsed.success) { throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')); } const result = await repo.listNotes(auth.sub, PRODUCT_ID, parsed.data); return { query: parsed.data.search ?? null, ...result, limit: parsed.data.limit, offset: parsed.data.offset, }; }); app.get('/notes', async req => { const auth = await extractAuth(req); const parsed = ListNotesQuerySchema.safeParse(req.query); if (!parsed.success) { throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ')); } const result = await repo.listNotes(auth.sub, PRODUCT_ID, parsed.data); return { ...result, limit: parsed.data.limit, offset: parsed.data.offset }; }); app.get('/notes/:id', async req => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const workspaceId = (req.query as { workspaceId?: string }).workspaceId; if (!workspaceId) { throw new BadRequestError('workspaceId is required'); } const note = await repo.getNote(id, workspaceId); if (!note || note.userId !== auth.sub || note.productId !== PRODUCT_ID) { throw new NotFoundError('Note not found'); } return note; }); app.post('/notes', async (req, reply) => { const auth = await extractAuth(req); const parsed = CreateNoteSchema.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: NoteDoc = { id: parsed.data.id, productId: PRODUCT_ID, workspaceId: parsed.data.workspaceId, userId: auth.sub, title: parsed.data.title, body: parsed.data.body, status: 'draft', tags: parsed.data.tags, links: parsed.data.links, sourceType: parsed.data.sourceType, sourceUri: parsed.data.sourceUri, createdAt: now, updatedAt: now, createdBy: auth.sub, updatedBy: auth.sub, agentId: parsed.data.agentId, }; const created = await repo.createNote(doc); reply.code(201); return created; }); app.patch('/notes/:id', async req => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const workspaceId = (req.query as { workspaceId?: string }).workspaceId; if (!workspaceId) { throw new BadRequestError('workspaceId is required'); } const parsed = UpdateNoteSchema.safeParse(req.body); if (!parsed.success) { throw new BadRequestError( parsed.error.issues.map((issue: { message: string }) => issue.message).join('; ') ); } const existing = await repo.getNote(id, workspaceId); if (!existing || existing.userId !== auth.sub || existing.productId !== PRODUCT_ID) { throw new NotFoundError('Note not found'); } const updated = await repo.updateNote(id, workspaceId, { ...parsed.data, updatedAt: new Date().toISOString(), updatedBy: auth.sub, }); if (!updated) { throw new NotFoundError('Note not found'); } return updated; }); app.post('/notes/:id/archive', async req => { const auth = await extractAuth(req); const { id } = req.params as { id: string }; const workspaceId = (req.body as { workspaceId?: string } | undefined)?.workspaceId; if (!workspaceId) { throw new BadRequestError('workspaceId is required'); } const existing = await repo.getNote(id, workspaceId); if (!existing || existing.userId !== auth.sub || existing.productId !== PRODUCT_ID) { throw new NotFoundError('Note not found'); } const updated = await repo.updateNote(id, workspaceId, { status: 'archived', updatedAt: new Date().toISOString(), updatedBy: auth.sub, }); if (!updated) { throw new NotFoundError('Note not found'); } return updated; }); }