import express, { type NextFunction, type Request, type Response, } from "express"; import { attachGraphQLClient } from "./middleware/graphql"; import commentRouter from "./routes/comment"; import userRouter from "./routes/user"; import authorRouter from "./routes/author"; import workRouter from "./routes/work"; import tagRouter from "./routes/tag"; import collectionRouter from "./routes/collection"; import blogRouter from "./routes/blog"; import statsRouter from "./routes/stats"; import searchRouter from "./routes/search"; import filterRouter from "./routes/filter"; import { log, serveStatic, setupVite } from "./vite"; import { createServer } from "node:http"; const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: false })); // Attach per-request GraphQL client app.use(attachGraphQLClient); app.use((req, res, next) => { const start = Date.now(); const path = req.path; let capturedJsonResponse: unknown; const originalResJson = res.json; res.json = (bodyJson, ...args) => { capturedJsonResponse = bodyJson; return originalResJson.apply(res, [bodyJson, ...args]); }; res.on("finish", () => { const duration = Date.now() - start; if (path.startsWith("/api")) { let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`; if (capturedJsonResponse) { logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`; } if (logLine.length > 80) { logLine = `${logLine.slice(0, 79)}…`; } log(logLine); } }); next(); }); (async () => { // Domain routers (incremental migration from monolith) app.use("/api/users", userRouter); app.use("/api/authors", authorRouter); app.use("/api/works", workRouter); app.use("/api/tags", tagRouter); app.use("/api/comments", commentRouter); app.use("/api/collections", collectionRouter); app.use("/api/blog", blogRouter); app.use("/api/stats", statsRouter); app.use("/api/search", searchRouter); app.use("/api/filter", filterRouter); // comments router already mounted earlier at /api/comments const server = createServer(app); app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => { const fallbackStatus = 500; interface StatusCarrier { status?: number; statusCode?: number; } const status = typeof err === "object" && err ? ((err as StatusCarrier).status ?? (err as StatusCarrier).statusCode ?? fallbackStatus) : fallbackStatus; const message = err instanceof Error ? err.message : "Internal Server Error"; res.status(status).json({ message }); throw err; }); // importantly only setup vite in development and after // setting up all the other routes so the catch-all route // doesn't interfere with the other routes if (app.get("env") === "development") { await setupVite(app, server); } else { serveStatic(app); } // ALWAYS serve the app on port 5000 // this serves both the API and the client. // It is the only port that is not firewalled. const port = 5000; server.listen(port, "0.0.0.0", () => { log(`serving on port ${port}`); }); })();