import * as path from 'path'; import { FastifyInstance, FastifyError } from 'fastify'; import AutoLoad from '@fastify/autoload'; import { AuditService } from './services/audit.service'; import { AuditAction, AuditCategory } from '@library/shared-types'; /* eslint-disable-next-line */ export interface AppOptions {} export async function app(fastify: FastifyInstance, opts: AppOptions) { // Add global error handler for security event logging fastify.setErrorHandler(async (error: FastifyError, request, reply) => { // Log CSRF validation failures if (error.code === 'FST_CSRF_INVALID_TOKEN' || error.code === 'FST_CSRF_MISSING_SECRET') { await AuditService.log({ action: AuditAction.csrfValidationFailed, category: AuditCategory.security, details: `CSRF validation failed: ${error.message}, URL: ${request.url}`, success: false, }, request).catch(() => { // Ignore logging errors }); } // Log unauthorized access attempts (exclude /api/auth/me as 401s there are expected during token refresh) if ((error.statusCode === 401 || error.statusCode === 403) && request.url !== '/api/auth/me') { await AuditService.log({ action: AuditAction.unauthorizedAccess, category: AuditCategory.security, details: `Unauthorized access attempt: ${error.message}, URL: ${request.url}`, success: false, }, request).catch(() => { // Ignore logging errors }); } // Send the error response (don't leak internal details for server errors) const statusCode = error.statusCode ?? 500; reply.status(statusCode).send({ statusCode, error: statusCode >= 500 ? "Internal Server Error" : error.name, message: statusCode >= 500 ? "An unexpected error occurred" : error.message, }); }); // This loads all plugins defined in plugins // those should be support plugins that are reused // through your application fastify.register(AutoLoad, { dir: path.join(__dirname, 'plugins'), options: { ...opts }, }); // This loads all plugins defined in routes // define your routes in one of these fastify.register(AutoLoad, { dir: path.join(__dirname, 'routes'), options: { ...opts, prefix: '/api' }, ignorePattern: /root\.ts$/, }); // Register root route without prefix fastify.register(AutoLoad, { dir: path.join(__dirname, 'routes'), options: { ...opts }, matchFilter: /root\.ts$/, }); }