This commit is contained in:
Morten Olsen
2025-12-09 20:32:08 +01:00
commit 2cfd54c344
54 changed files with 8794 additions and 0 deletions

109
packages/server/src/api.ts Normal file
View File

@@ -0,0 +1,109 @@
import fastifyCors from '@fastify/cors';
import fastifySwagger from '@fastify/swagger';
import fastify from 'fastify';
import {
hasZodFastifySchemaValidationErrors,
isResponseSerializationError,
jsonSchemaTransform,
serializerCompiler,
validatorCompiler,
type ZodTypeProvider,
} from 'fastify-type-provider-zod';
import { Services } from './utils/utils.services.ts';
import { systemEndpoints } from './endpoints/system/system.ts';
import { WarmupService } from './services/warmup/warmup.ts';
import { documentEndpoints } from './endpoints/documents/documents.ts';
import { documentFilterEndpoints } from './endpoints/document-filters/document-filters.ts';
import { documentChunkFilterEndpoints } from './endpoints/document-chunk-filters/document-chunk-filters.ts';
class BaseError extends Error {
public statusCode: number;
constructor(message: string, statusCode = 500) {
super(message);
this.name = this.constructor.name;
this.statusCode = statusCode;
}
}
const createApi = async (services: Services = new Services()) => {
const app = fastify().withTypeProvider<ZodTypeProvider>();
app.setValidatorCompiler(validatorCompiler);
app.setSerializerCompiler(serializerCompiler);
app.decorate('services', services);
app.register(fastifyCors);
app.register(fastifySwagger, {
openapi: {
info: {
title: 'My API',
version: '1.0.0',
},
},
transform: jsonSchemaTransform,
});
await app.register(import('@scalar/fastify-api-reference'), {
routePrefix: '/docs',
});
app.setErrorHandler((err, req, reply) => {
console.error(err);
if (hasZodFastifySchemaValidationErrors(err)) {
return reply.code(400).send({
error: 'Response Validation Error',
message: "Request doesn't match the schema",
statusCode: 400,
details: {
issues: err.validation,
method: req.method,
url: req.url,
},
});
}
if (isResponseSerializationError(err)) {
return reply.code(500).send({
error: 'Internal Server Error',
message: "Response doesn't match the schema",
statusCode: 500,
details: {
issues: err.cause.issues,
method: err.method,
url: err.url,
},
});
}
if (err instanceof BaseError) {
return reply.code(err.statusCode ?? 500).send({
error: err.name,
message: err.message,
statusCode: err.statusCode,
});
}
return reply.code(500).send({
error: 'Internal Server Error',
message: err instanceof Error ? err.message : 'An unknown error occurred',
statusCode: 500,
});
});
app.addHook('onReady', async () => {
const warmupService = app.services.get(WarmupService);
await warmupService.ensure();
});
await app.register(systemEndpoints, { prefix: '/system' });
await app.register(documentEndpoints, { prefix: '/documents' });
await app.register(documentFilterEndpoints, { prefix: '/document-filters' });
await app.register(documentChunkFilterEndpoints, { prefix: '/document-chunk-filters' });
await app.ready();
app.swagger();
return app;
};
export { createApi };