120 lines
3.3 KiB
TypeScript
120 lines
3.3 KiB
TypeScript
import fastifyCors from '@fastify/cors';
|
|
import fastifySwagger from '@fastify/swagger';
|
|
import fastify from 'fastify';
|
|
import {
|
|
hasZodFastifySchemaValidationErrors,
|
|
isResponseSerializationError,
|
|
jsonSchemaTransform,
|
|
jsonSchemaTransformObject,
|
|
serializerCompiler,
|
|
validatorCompiler,
|
|
type ZodTypeProvider,
|
|
} from 'fastify-type-provider-zod';
|
|
import scalar from '@scalar/fastify-api-reference';
|
|
import { StashRuntime } from '@morten-olsen/stash-runtime';
|
|
|
|
import { systemEndpoints } from './endpoints/system/system.js';
|
|
import { documentEndpoints } from './endpoints/documents/documents.js';
|
|
import { documentFilterEndpoints } from './endpoints/document-filters/document-filters.js';
|
|
import { documentChunkFilterEndpoints } from './endpoints/document-chunk-filters/document-chunk-filters.js';
|
|
|
|
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 (runtime: StashRuntime = new StashRuntime()) => {
|
|
const app = fastify().withTypeProvider<ZodTypeProvider>();
|
|
app.setValidatorCompiler(validatorCompiler);
|
|
app.setSerializerCompiler(serializerCompiler);
|
|
|
|
app.decorate('runtime', runtime);
|
|
|
|
app.register(fastifyCors);
|
|
app.register(fastifySwagger, {
|
|
openapi: {
|
|
info: {
|
|
title: 'My API',
|
|
version: '1.0.0',
|
|
},
|
|
},
|
|
transform: jsonSchemaTransform,
|
|
transformObject: jsonSchemaTransformObject,
|
|
});
|
|
|
|
await app.register(scalar, {
|
|
routePrefix: '/docs',
|
|
configuration: {
|
|
pageTitle: 'Foo',
|
|
title: 'Hello World!',
|
|
telemetry: false,
|
|
hideClientButton: true,
|
|
theme: 'laserwave',
|
|
persistAuth: true,
|
|
orderRequiredPropertiesFirst: false,
|
|
},
|
|
});
|
|
|
|
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 () => {
|
|
app.runtime.warmup.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 };
|