63 lines
2.3 KiB
TypeScript
63 lines
2.3 KiB
TypeScript
import 'dotenv/config';
|
|
import express from 'express';
|
|
import type { Response, NextFunction } from 'express';
|
|
import cors from 'cors';
|
|
import { z } from 'zod';
|
|
import swaggerUi from 'swagger-ui-express';
|
|
import { prisma } from './src/utils/prisma.js';
|
|
import { createOpenAPIDocument } from './src/openapi/registry.js';
|
|
import requestsRouter from './src/routes/requests.routes.js';
|
|
import itemsRouter from './src/routes/items.routes.js';
|
|
import catalogRouter from './src/routes/catalog.routes.js';
|
|
|
|
// ---------- App ----------
|
|
const app = express();
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
|
|
// ---------- Health Check ----------
|
|
app.get('/health', (_req, res) => res.json({ ok: true, time: new Date().toISOString() }));
|
|
|
|
// ---------- Routes ----------
|
|
app.use('/api/consumable-temp-requests', requestsRouter);
|
|
app.use('/api/consumable-temp-items', itemsRouter);
|
|
app.use('/api/consumable-temp-catalog', catalogRouter);
|
|
|
|
// ---------- OpenAPI Documentation ----------
|
|
const openApiDocument = createOpenAPIDocument();
|
|
|
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiDocument, {
|
|
customSiteTitle: '智能配送 API 文档',
|
|
customCss: '.swagger-ui .topbar { display: none }',
|
|
swaggerOptions: {
|
|
persistAuthorization: true,
|
|
displayRequestDuration: true
|
|
}
|
|
}));
|
|
|
|
app.get('/openapi.json', (_req, res) => {
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(JSON.stringify(openApiDocument, null, 2));
|
|
});
|
|
|
|
// ---------- Error Handler ----------
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
app.use((err: any, _req: any, res: Response, _next: NextFunction) => {
|
|
if (err instanceof z.ZodError) {
|
|
return res.status(400).json({ error: 'VALIDATION_ERROR', details: err.flatten() });
|
|
}
|
|
if (err.message === 'NOT_FOUND') return res.status(404).json({ error: 'Not Found' });
|
|
console.error('Unhandled Error:', err);
|
|
res.status(500).json({ error: 'INTERNAL_ERROR' });
|
|
});
|
|
|
|
// ---------- Start Server ----------
|
|
const PORT = Number(process.env.PORT) || 3000;
|
|
app.listen(PORT, () => {
|
|
console.log(`Consumable Temp Request API listening on :${PORT}`);
|
|
});
|
|
|
|
// ---------- Graceful Shutdown ----------
|
|
process.on('SIGINT', async () => { await prisma.$disconnect(); process.exit(0); });
|
|
process.on('SIGTERM', async () => { await prisma.$disconnect(); process.exit(0); });
|