duoqi-api/src/index.ts

88 lines
3.4 KiB
TypeScript

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<void> {
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();