feat(TRUEREF-0002): implement repository management service and REST API

Add RepositoryService with full CRUD, ID resolution helpers, input
validation, six SvelteKit API routes (GET/POST /api/v1/libs,
GET/PATCH/DELETE /api/v1/libs/:id, POST /api/v1/libs/:id/index), and
37 unit tests covering all service operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Giancarmine Salucci
2026-03-22 17:43:06 +01:00
parent f57b622505
commit 3d1bef5003
8 changed files with 1146 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
/**
* GET /api/v1/libs/:id — get a single repository
* PATCH /api/v1/libs/:id — update repository metadata
* DELETE /api/v1/libs/:id — delete a repository
*/
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { getClient } from '$lib/server/db/client';
import { RepositoryService } from '$lib/server/services/repository.service';
import { handleServiceError } from '$lib/server/utils/validation';
function getService() {
return new RepositoryService(getClient());
}
export const GET: RequestHandler = ({ params }) => {
try {
const service = getService();
const id = decodeURIComponent(params.id);
const repo = service.get(id);
if (!repo) {
return json({ error: 'Repository not found', code: 'NOT_FOUND' }, { status: 404 });
}
const versions = service.getVersions(id);
return json({ ...repo, versions });
} catch (err) {
return handleServiceError(err);
}
};
export const PATCH: RequestHandler = async ({ params, request }) => {
try {
const service = getService();
const id = decodeURIComponent(params.id);
const body = await request.json();
const updated = service.update(id, {
title: body.title,
description: body.description,
branch: body.branch,
githubToken: body.githubToken
});
return json(updated);
} catch (err) {
return handleServiceError(err);
}
};
export const DELETE: RequestHandler = ({ params }) => {
try {
const service = getService();
const id = decodeURIComponent(params.id);
service.remove(id);
return new Response(null, { status: 204 });
} catch (err) {
return handleServiceError(err);
}
};
export const OPTIONS: RequestHandler = () => {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, PATCH, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
};

View File

@@ -0,0 +1,43 @@
/**
* POST /api/v1/libs/:id/index — trigger an indexing job for a repository.
*/
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { getClient } from '$lib/server/db/client';
import { RepositoryService } from '$lib/server/services/repository.service';
import { handleServiceError, NotFoundError } from '$lib/server/utils/validation';
export const POST: RequestHandler = async ({ params, request }) => {
try {
const service = new RepositoryService(getClient());
const id = decodeURIComponent(params.id);
const repo = service.get(id);
if (!repo) throw new NotFoundError(`Repository ${id} not found`);
let versionId: string | undefined;
try {
const body = await request.json();
versionId = body.version ?? undefined;
} catch {
// body is optional
}
const job = service.createIndexingJob(id, versionId);
return json({ job }, { status: 202 });
} catch (err) {
return handleServiceError(err);
}
};
export const OPTIONS: RequestHandler = () => {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
};