import Fastify from 'fastify'; import cors from '@fastify/cors'; import helmet from '@fastify/helmet'; import rateLimit from '@fastify/rate-limit'; import jwt from '@fastify/jwt'; import { config } from './utils/config.js'; import { errorHandler } from './utils/errors.js'; import authMiddleware from './middleware/auth.js'; import adminAuthMiddleware from './middleware/admin-auth.js'; import requestLogger from './middleware/request-logger.js'; import auditLogMiddleware from './middleware/audit-log.js'; import { healthRoutes } from './routes/health.js'; import { authRoutes } from './routes/auth.js'; import { quizRoutes } from './routes/quiz.js'; import { progressRoutes } from './routes/progress.js'; import { gamificationRoutes } from './routes/gamification.js'; import { paymentRoutes } from './routes/payment.js'; import { appApiRoutes } from './routes/app-api.js'; import { rewardsRoutes } from './routes/rewards.js'; import { adminRoutes } from './routes/admin/index.js'; async function main(): Promise { const app = Fastify({ logger: { level: config.LOG_LEVEL, transport: config.NODE_ENV === 'development' ? { target: 'pino-pretty', options: { translateTime: 'HH:MM:ss Z', ignore: 'pid,hostname' } } : undefined, }, }); // ── Plugins ────────────────────────────────────────────────────── await app.register(helmet); await app.register(cors, { origin: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'] }); await app.register(rateLimit, { max: 60, timeWindow: '1 minute', }); await app.register(jwt, { secret: config.JWT_SECRET, sign: { expiresIn: config.JWT_EXPIRES_IN }, }); // ── Middleware ──────────────────────────────────────────────────── await app.register(requestLogger); await app.register(authMiddleware); await app.register(adminAuthMiddleware); await app.register(auditLogMiddleware); // ── Error handler ──────────────────────────────────────────────── app.setErrorHandler(errorHandler); // ── Routes ─────────────────────────────────────────────────────── app.register(healthRoutes); // Auth routes: stricter rate limit (10/min) app.register(authRoutes, { prefix: '/v1' }); // Quiz routes: standard rate limit (60/min via global) app.register(quizRoutes, { prefix: '/v1' }); app.register(progressRoutes, { prefix: '/v1' }); app.register(gamificationRoutes, { prefix: '/v1' }); app.register(paymentRoutes, { prefix: '/v1' }); app.register(appApiRoutes, { prefix: '/v1' }); app.register(rewardsRoutes, { prefix: '/v1' }); // Admin routes: higher rate limit (100/min) app.register(adminRoutes, { prefix: '/v1/admin' }); // ── Start server ───────────────────────────────────────────────── try { await app.listen({ port: config.PORT, host: '0.0.0.0' }); app.log.info(`duoqi-api running on port ${config.PORT} [${config.NODE_ENV}]`); } catch (err) { app.log.error(err); process.exit(1); } } main();