import express, { type Express } from "express"; import cors from "cors"; import { existsSync } from "node:fs"; import { resolve } from "node:path"; import pinoHttp from "pino-http"; import session from "express-session"; import ConnectPgSimple from "connect-pg-simple"; import router from "./routes"; import { logger } from "./lib/logger"; import "./types/session.d.ts"; const PgStore = ConnectPgSimple(session); const app: Express = express(); app.set("trust proxy", 1); app.use( pinoHttp({ logger, serializers: { req(req) { return { id: req.id, method: req.method, url: req.url?.split("?")[0], }; }, res(res) { return { statusCode: res.statusCode, }; }, }, }), ); app.use(cors({ origin: true, credentials: true })); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use( session({ store: new PgStore({ conString: process.env.DATABASE_URL, tableName: "sessions", createTableIfMissing: true, }), secret: process.env.SESSION_SECRET || "dev-secret-change-in-production", resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === "production", httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", }, }), ); app.use("/api", router); // Any unmatched /api route should return a JSON 404 instead of falling // through to the SPA catch-all below. app.use("/api", (_req, res) => { res.status(404).json({ error: "Not found" }); }); const staticDir = process.env.STATIC_DIR; if (staticDir && existsSync(staticDir)) { app.use(express.static(staticDir)); app.get("/*any", (_req, res) => { res.sendFile(resolve(staticDir, "index.html")); }); logger.info({ staticDir }, "Serving static files"); } export default app;