88 lines
3.4 KiB
TypeScript
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();
|