import "dotenv/config"; import cors from "cors"; import express, { NextFunction, Request, Response } from "express"; import helmet from "helmet"; import morgan from "morgan"; import path from "node:path"; import fs from "node:fs"; import { responseMiddleware } from "./middlewares/response.middleware"; import router from "./routes/index.route"; import { HttpError } from "./utils/httpError"; const app = express(); app.set("trust proxy", true); app.use(helmet()); app.use(cors({ origin: true })); app.use(express.json({ limit: "1mb" })); app.use(express.urlencoded({ extended: true })); app.use(morgan(process.env.NODE_ENV === "production" ? "combined" : "dev")); app.use(responseMiddleware); app.use(router); const distPublic = path.join(__dirname, "public"); const frontDist = path.join(process.cwd(), "frontEnd", "dist"); const staticDir = fs.existsSync(distPublic) ? distPublic : frontDist; if (fs.existsSync(staticDir)) { app.use(express.static(staticDir)); } // SPA fallback for non-API GET requests app.use((req: Request, res: Response, next: NextFunction) => { if (req.method !== "GET") return next(); if (req.path.startsWith("/api")) return next(); if (fs.existsSync(path.join(staticDir, "index.html"))) { res.sendFile(path.join(staticDir, "index.html")); return; } next(); }); app.use((req: Request, res: Response) => { res.status(404).json({ error: { code: "NOT_FOUND", message: `Route not found: ${req.method} ${req.path}` } }); }); app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => { if (err instanceof HttpError) { res.status(err.status).json({ error: { code: err.code, message: err.message, details: err.details ?? null } }); return; } res.status(500).json({ error: { code: "INTERNAL_SERVER_ERROR", message: "Unexpected error" } }); }); const PORT = normalizePort(process.env.PORT ?? "3000"); if (require.main === module) { app.listen(PORT, () => { console.log(`服务已运行: http://localhost:${PORT}`); }); } export default app; function normalizePort(value: string): number { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed) || parsed <= 0) return 3000; return parsed; }