import type { FastifyInstance } from 'fastify' import { z } from 'zod' import { db } from '../db/index.js' const bookmarkSchema = z.object({ categoryId: z.number().int().nullable().optional(), title: z.string().min(1).max(128), url: z.string().url(), icon: z.string().optional(), favorite: z.boolean().optional(), }) const categorySchema = z.object({ name: z.string().min(1).max(64), icon: z.string().optional(), sortOrder: z.number().int().optional(), }) export async function bookmarkRoutes(app: FastifyInstance) { app.addHook('onRequest', app.authenticate) app.get('/api/bookmarks/categories', async () => { const categories = db.prepare('SELECT * FROM bookmark_categories ORDER BY sort_order').all() return { categories } }) app.post('/api/bookmarks/categories', async (req, reply) => { const parsed = categorySchema.safeParse(req.body) if (!parsed.success) return reply.code(400).send({ error: 'Invalid input' }) const { name, icon, sortOrder } = parsed.data const result = db .prepare('INSERT INTO bookmark_categories (name, icon, sort_order) VALUES (?, ?, ?)') .run(name, icon ?? null, sortOrder ?? 0) return reply.code(201).send({ id: result.lastInsertRowid }) }) app.get('/api/bookmarks', async () => { const bookmarks = db.prepare('SELECT * FROM bookmarks ORDER BY created_at DESC').all() return { bookmarks } }) app.post('/api/bookmarks', async (req, reply) => { const parsed = bookmarkSchema.safeParse(req.body) if (!parsed.success) return reply.code(400).send({ error: 'Invalid input' }) const { categoryId, title, url, icon, favorite } = parsed.data const result = db .prepare( 'INSERT INTO bookmarks (category_id, title, url, icon, favorite) VALUES (?, ?, ?, ?, ?)' ) .run(categoryId ?? null, title, url, icon ?? null, favorite ? 1 : 0) return reply.code(201).send({ id: result.lastInsertRowid }) }) app.put('/api/bookmarks/:id', async (req, reply) => { const id = Number((req.params as { id: string }).id) const parsed = bookmarkSchema.partial().safeParse(req.body) if (!parsed.success) return reply.code(400).send({ error: 'Invalid input' }) const existing = db.prepare('SELECT * FROM bookmarks WHERE id = ?').get(id) as | { category_id: number | null; title: string; url: string; icon: string | null; favorite: number } | undefined if (!existing) return reply.code(404).send({ error: 'Not found' }) const categoryId = parsed.data.categoryId ?? existing.category_id const title = parsed.data.title ?? existing.title const url = parsed.data.url ?? existing.url const icon = parsed.data.icon ?? existing.icon const favorite = parsed.data.favorite ?? !!existing.favorite db.prepare( 'UPDATE bookmarks SET category_id = ?, title = ?, url = ?, icon = ?, favorite = ? WHERE id = ?' ).run(categoryId, title, url, icon, favorite ? 1 : 0, id) return { ok: true } }) app.delete('/api/bookmarks/:id', async (req, reply) => { const id = Number((req.params as { id: string }).id) db.prepare('DELETE FROM bookmarks WHERE id = ?').run(id) return reply.code(204).send() }) }