import Fastify, { FastifyRequest, FastifyReply } from 'fastify' import jwt from '@fastify/jwt' import cookie from '@fastify/cookie' import cors from '@fastify/cors' import helmet from '@fastify/helmet' import rateLimit from '@fastify/rate-limit' import { config } from './config' import './types' // side-effect: augments fastify types import wsRoutes from './routes/ws' import authRoutes from './routes/auth' import hotelsRoutes from './routes/hotels' import roomsRoutes from './routes/rooms' import bookingsRoutes from './routes/bookings' import housekeepingRoutes from './routes/housekeeping' import channelsRoutes from './routes/channels' import usersRoutes from './routes/users' import netupRoutes from './routes/netup' import guestsRoutes from './routes/guests' import bookingGuestsRoutes from './routes/booking-guests' import hotelSettingsRoutes from './routes/hotel-settings' import rentalRoutes from './routes/rental' import categoriesRoutes from './routes/categories' import tariffsRoutes from './routes/tariffs' export async function buildApp() { const fastify = Fastify({ logger: { level: config.nodeEnv === 'production' ? 'warn' : 'info', ...(config.nodeEnv !== 'production' && { transport: { target: 'pino-pretty', options: { colorize: true } }, }), }, bodyLimit: 20 * 1024 * 1024, // 20MB — for base64 photo uploads }) // ── Security ─────────────────────────────────────────────────────────────── await fastify.register(helmet, { contentSecurityPolicy: false }) await fastify.register(cors, { origin: config.cors.origins, credentials: true, methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'], }) await fastify.register(rateLimit, { max: 200, timeWindow: '1 minute', }) // ── Cookies & JWT ────────────────────────────────────────────────────────── await fastify.register(cookie) await fastify.register(jwt, { secret: config.jwt.secret, }) // Authenticate decorator used as onRequest hook in routes fastify.decorate( 'authenticate', async (request: FastifyRequest, reply: FastifyReply) => { try { await request.jwtVerify() } catch (err) { reply.code(401).send({ error: 'Unauthorized' }) } }, ) // ── Health check ─────────────────────────────────────────────────────────── fastify.get('/health', async () => ({ status: 'ok', ts: new Date().toISOString() })) // ── Routes ───────────────────────────────────────────────────────────────── await fastify.register(wsRoutes) await fastify.register(authRoutes) await fastify.register(hotelsRoutes) await fastify.register(roomsRoutes) await fastify.register(bookingsRoutes) await fastify.register(housekeepingRoutes) await fastify.register(channelsRoutes) await fastify.register(usersRoutes) await fastify.register(netupRoutes) await fastify.register(guestsRoutes) await fastify.register(bookingGuestsRoutes) await fastify.register(hotelSettingsRoutes) await fastify.register(rentalRoutes) await fastify.register(categoriesRoutes) await fastify.register(tariffsRoutes) return fastify }