88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
import express from "express";
|
|
import helmet from "helmet";
|
|
import cors from "cors";
|
|
import { pinoHttp } from "pino-http";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import fs from "node:fs";
|
|
|
|
import { config } from "./config.js";
|
|
import { logger } from "./logger.js";
|
|
import { authRouter } from "./auth/routes.js";
|
|
import { datumRouter } from "./datum/routes.js";
|
|
import { errorHandler } from "./errors.js";
|
|
|
|
export function buildApp() {
|
|
const app = express();
|
|
app.disable("x-powered-by");
|
|
app.set("trust proxy", 1);
|
|
|
|
app.use(
|
|
helmet({
|
|
contentSecurityPolicy: {
|
|
useDefaults: true,
|
|
directives: {
|
|
"default-src": ["'self'"],
|
|
"script-src": ["'self'"],
|
|
"style-src": ["'self'", "'unsafe-inline'"],
|
|
"img-src": ["'self'", "data:"],
|
|
"connect-src": ["'self'"],
|
|
"font-src": ["'self'", "data:"],
|
|
"frame-ancestors": ["'none'"],
|
|
"upgrade-insecure-requests": null,
|
|
},
|
|
},
|
|
crossOriginEmbedderPolicy: false,
|
|
crossOriginOpenerPolicy: false,
|
|
originAgentCluster: false,
|
|
}),
|
|
);
|
|
|
|
if (config.corsOrigin) {
|
|
app.use(cors({ origin: config.corsOrigin, credentials: false }));
|
|
}
|
|
|
|
app.use(express.json({ limit: "32kb" }));
|
|
app.use(
|
|
pinoHttp({
|
|
logger,
|
|
autoLogging: {
|
|
ignore: (req) =>
|
|
req.url === "/healthz" ||
|
|
req.url === "/favicon.ico" ||
|
|
req.url.startsWith("/assets/"),
|
|
},
|
|
}),
|
|
);
|
|
|
|
app.get("/healthz", (_req, res) => {
|
|
res.json({ ok: true });
|
|
});
|
|
app.use("/api/auth", authRouter);
|
|
app.use("/api/datum", datumRouter);
|
|
|
|
const staticDir = config.staticDir
|
|
? config.staticDir
|
|
: resolveDefaultStaticDir();
|
|
|
|
if (staticDir && fs.existsSync(staticDir)) {
|
|
logger.info({ staticDir }, "serving web assets");
|
|
app.use(express.static(staticDir, { index: false, maxAge: "1h" }));
|
|
app.get(/.*/, (_req, res, next) => {
|
|
const indexFile = path.join(staticDir, "index.html");
|
|
if (!fs.existsSync(indexFile)) return next();
|
|
res.sendFile(indexFile);
|
|
});
|
|
} else {
|
|
logger.warn({ staticDir }, "web assets directory not found");
|
|
}
|
|
|
|
app.use(errorHandler);
|
|
return app;
|
|
}
|
|
|
|
function resolveDefaultStaticDir(): string {
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
return path.resolve(here, "../../web/dist");
|
|
}
|