diff --git a/src/server.ts b/src/server.ts index 8833a04..92a182b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,6 +2,7 @@ import Fastify from 'fastify'; import cors from '@fastify/cors'; import cookie from '@fastify/cookie'; import rateLimit from '@fastify/rate-limit'; +import path from 'path'; import { config } from './config/env'; import { securityHeaders } from './middleware/security'; @@ -129,6 +130,23 @@ async function start() { }); } + // --- Frontend: static file serving (production) --- + if (config.isProduction) { + const fastifyStatic = (await import('@fastify/static')).default; + await app.register(fastifyStatic, { + root: path.join(__dirname, '..', 'dist-client'), + prefix: '/', + wildcard: false, + }); + + app.setNotFoundHandler((request, reply) => { + if (request.url.startsWith('/api/')) { + return reply.status(404).send({ success: false, error: 'Not found' }); + } + return reply.sendFile('index.html'); + }); + } + // --- Start --- const port = config.isProduction ? config.port : 3000; try { @@ -138,6 +156,23 @@ async function start() { app.log.error(err); process.exit(1); } + + const shutdown = async (signal: string) => { + app.log.info(`${signal} received, shutting down gracefully...`); + try { + await app.close(); + const { default: prisma } = await import('./config/database'); + await prisma.$disconnect(); + app.log.info('Server shut down successfully'); + process.exit(0); + } catch (err) { + app.log.error(err, 'Error during shutdown'); + process.exit(1); + } + }; + + process.on('SIGTERM', () => shutdown('SIGTERM')); + process.on('SIGINT', () => shutdown('SIGINT')); } start();