import type { FastifyInstance, FastifyReply } from 'fastify'; import type { CreateRepoRequest, DiscoverReposResponse, RepoResponse, ReposListResponse, UpdateRepoRequest } from '@arboretum/shared'; import type { WorktreeManager } from '../core/worktree-manager.js'; import type { Db } from '../db/index.js'; import { readScanRoots } from '../core/scan-settings.js'; /** Mappe une erreur du manager (statusCode + code) vers une réponse REST normalisée. */ export function sendManagerError(reply: FastifyReply, err: unknown): FastifyReply { const e = err as { statusCode?: number; code?: string; message?: string }; return reply.status(e.statusCode ?? 500).send({ error: { code: e.code ?? 'INTERNAL', message: e.message ?? 'Internal error' } }); } export function registerRepoRoutes(app: FastifyInstance, wt: WorktreeManager, db: Db): void { app.get('/api/v1/repos', async (): Promise => ({ repos: await wt.listRepos() })); // Scan manuel : découvre et auto-enregistre les repos sous les racines configurées (settings). app.post('/api/v1/repos/discover', async (_req, reply) => { try { const res: DiscoverReposResponse = await wt.discoverRepos({ roots: readScanRoots(db) }); return reply.send(res); } catch (err) { return sendManagerError(reply, err); } }); app.post('/api/v1/repos', async (req, reply) => { const body = req.body as Partial | null; if (!body || typeof body.path !== 'string') { return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'path (absolute) is required' } }); } try { const repo = await wt.addRepo({ path: body.path, ...(body.label !== undefined ? { label: body.label } : {}), ...(body.postCreateHooks !== undefined ? { postCreateHooks: body.postCreateHooks } : {}), ...(body.preTrust !== undefined ? { preTrust: body.preTrust } : {}), }); const res: RepoResponse = { repo }; return reply.status(201).send(res); } catch (err) { return sendManagerError(reply, err); } }); app.patch('/api/v1/repos/:id', async (req, reply) => { const { id } = req.params as { id: string }; const body = (req.body as Partial | null) ?? {}; try { const repo = await wt.updateRepo(id, { ...(body.label !== undefined ? { label: body.label } : {}), ...(body.postCreateHooks !== undefined ? { postCreateHooks: body.postCreateHooks } : {}), ...(body.preTrust !== undefined ? { preTrust: body.preTrust } : {}), ...(typeof body.hidden === 'boolean' ? { hidden: body.hidden } : {}), }); const res: RepoResponse = { repo }; return reply.send(res); } catch (err) { return sendManagerError(reply, err); } }); app.delete('/api/v1/repos/:id', async (req, reply) => { const { id } = req.params as { id: string }; if (!wt.removeRepo(id)) { return reply.status(404).send({ error: { code: 'NOT_FOUND', message: 'No repo with this id' } }); } return reply.send({ ok: true }); }); }