import type { FastifyInstance } from 'fastify'; import type { Sql } from '../db.js'; import { listPending, applyOne, applyAll, rejectOne, rewindOne, } from '../services/pending_changes.js'; /** * Resolve project root from a session's project path. */ async function resolveProjectRoot(sql: Sql, sessionId: string): Promise { const rows = await sql<{ path: string }[]>` SELECT p.path FROM sessions s JOIN projects p ON s.project_id = p.id WHERE s.id = ${sessionId} `; return rows.length > 0 ? rows[0]!.path : null; } /** * Resolve project root from a pending change's session. */ async function resolveProjectRootForChange(sql: Sql, changeId: string): Promise { const rows = await sql<{ path: string }[]>` SELECT p.path FROM pending_changes pc JOIN sessions s ON pc.session_id = s.id JOIN projects p ON s.project_id = p.id WHERE pc.id = ${changeId} `; return rows.length > 0 ? rows[0]!.path : null; } export function registerPendingRoutes(app: FastifyInstance, sql: Sql): void { // GET /api/sessions/:sessionId/pending — list pending changes for a session app.get<{ Params: { sessionId: string } }>( '/api/sessions/:sessionId/pending', async (req, reply) => { const sessionId = req.params.sessionId; const session = await sql<{ id: string }[]>`SELECT id FROM sessions WHERE id = ${sessionId}`; if (session.length === 0) { reply.code(404); return { error: 'session not found' }; } const pending = await listPending(sql, sessionId); return pending; }, ); // POST /api/sessions/:sessionId/pending/apply — apply all pending changes app.post<{ Params: { sessionId: string } }>( '/api/sessions/:sessionId/pending/apply', async (req, reply) => { const sessionId = req.params.sessionId; const projectRoot = await resolveProjectRoot(sql, sessionId); if (!projectRoot) { reply.code(404); return { error: 'session or project not found' }; } const results = await applyAll(sql, sessionId, projectRoot); return { results }; }, ); // POST /api/pending/:id/apply — apply a single pending change app.post<{ Params: { id: string } }>( '/api/pending/:id/apply', async (req, reply) => { const changeId = req.params.id; const projectRoot = await resolveProjectRootForChange(sql, changeId); if (!projectRoot) { reply.code(404); return { error: 'pending change or project not found' }; } const result = await applyOne(sql, changeId, projectRoot); if (!result.success) { reply.code(422); } return result; }, ); // POST /api/pending/:id/reject — reject a single pending change app.post<{ Params: { id: string } }>( '/api/pending/:id/reject', async (req, reply) => { const changeId = req.params.id; await rejectOne(sql, changeId); return { ok: true }; }, ); // POST /api/pending/:id/rewind — rewind (undo) an applied change app.post<{ Params: { id: string } }>( '/api/pending/:id/rewind', async (req, reply) => { const changeId = req.params.id; const projectRoot = await resolveProjectRootForChange(sql, changeId); if (!projectRoot) { reply.code(404); return { error: 'pending change or project not found' }; } const result = await rewindOne(sql, changeId, projectRoot); if (!result.success) { reply.code(422); } return result; }, ); }