src/app.tsmodified
import express from "express";
import swaggerUi from "swagger-ui-express";
import swaggerJsdoc from "swagger-jsdoc";
import version from "../package.json";
import { verifyToken, createLoggingContext, addUserToLoggingContext, addOriginalUrlToLoggingContext } from "./middleware/middleware";
import healthcheckRoutes from "./api/healthcheck/healthcheck.routes";
import devicesRouter from "./api/v2/devices/devices.routes";
import devicesPublicRouter from "./api/v2/devices/devices.public.routes";
import issuesSeverityV2Routes from "./api/v2/issues/issue.severity.routes";
import cdeRoutes from "./api/v2/cde/cde.routes";
import logsRoutes from "./api/v2/logs/logs.routes";
import outageMessageRouter from "./api/banners/outage.messages.routes";
import outageMessagePublicRouter from "./api/banners/outage.messages.public.routes";
import cors from "cors";
import cookie from "cookie-parser";
import bodyParser from "body-parser";
import dotenv from "dotenv";
import { default as swaggerComponents } from "./swagger.components.schemas.json";

import disciplinesRouter from "./api/v2/disciplines/default.disciplines.routes";
import tenantRouter from "./api/v2/tenants/tenant.routes";
import timezoneRouter from "./api/v2/timezones/timezone.routes";
import projectV2Routes from "./api/v2/api.v2.routes";
import currenciesRouter from "./api/v2/currencies/currencies.routes";
import portfoliosRouter from "./api/v2/portfolios/portfolios.routes";
import systemsRouter from "./api/v2/systems/systems.routes";
import { ENV_CONFIG } from "../config/environment.config";
import { initMetrics } from "./util/metrics";

dotenv.config();

const app = express();
const port = 8088;
// express-prom-bundle's bundled types don't match how this middleware is used below
// (promClient access, app.use() overload) — require() keeps it untyped instead of breaking the build.
// eslint-disable-next-line @typescript-eslint/no-require-imports
const promBundle = require("express-prom-bundle");

const metricsMiddleware = promBundle({
    includeMethod: true,
    includePath: true,
    includeStatusCode: true,
    includeUp: true,
    customLabels: {
        environment: process.env.ENVIRONMENT,
        project_name: "platform-api",
    },
});

// Initialize custom metrics system with the prom-client registry
initMetrics(metricsMiddleware.promClient.register);

// OpenAPI version: deployed image tag when PLATFORM_API_VERSION is set, else package.json (local dev).
const swaggerApiVersion = ENV_CONFIG.api.platformApiVersion || version.version;

// Swagger definition
const swaggerOptions = {
    definition: {
        openapi: "3.0.0",
        info: {
            title: "XYZ Reality Data Platform API Documentation",
            version: `${swaggerApiVersion}`,
            description: "This is an API to be used for data access to XYZ's data platform service.",
        },
        components: {
            securitySchemes: {
                bearerAuth: {
                    type: "http",
                    scheme: "bearer",
                },
            },
            schemas: swaggerComponents.schemas, // Swagger components schemas
        },
        security: [
            {
                bearerAuth: [],
            },
        ],
    },
    // Paths to files containing Swagger annotations
    apis: ["./src/api/**/*.ts"], // Adjust this path to where your route definitions are
};

const corsOptions = {
    // Define origins that are allowed to access the API
    origin: ["http://localhost:9000", "https://frontend.holosite.dev", "https://staging.xyzreality.com", "https://cloud.xyzreality.com"],
    // Pass authentication credentials to the API
    credentials: true,
};

const swaggerSpecs = swaggerJsdoc(swaggerOptions);

// Establishes global limit for json body size
app.use(bodyParser.json({ limit: ENV_CONFIG.files.jsonUploadMaxSize }));

app.use(cors(corsOptions));
app.use(cookie());
// Serve Swagger documentation on dev environments
if (["local", "dev"].includes(process.env.ENVIRONMENT ?? "")) {
    app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
    app.use("/api/v2/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
}

app.get("/api/v2/swagger.json", (req, res) => {
    res.setHeader("Content-Type", "application/json");
    res.send(swaggerSpecs);
});

app.get("/swagger.json", (req, res) => {
    res.setHeader("Content-Type", "application/json");
    res.send(swaggerSpecs);
});

// Add healthcheck routes
app.use("/healthcheck", healthcheckRoutes);

// Added metrics middleware here as it captures any routes created afterwards.
app.use(metricsMiddleware);

// Add logging context
app.use(createLoggingContext);

// Public device routes (no auth) - must be mounted before verifyToken
app.use("/api/v2/devices", devicesPublicRouter);

// Public outage message route (no auth) - must be mounted before verifyToken
app.use("/api/v2/outage-messages", outageMessagePublicRouter);

// Add verify token service as middleware to all routes
app.use(verifyToken);

app.use(addUserToLoggingContext);
app.use(addOriginalUrlToLoggingContext);

app.use("/api/v2/projects", projectV2Routes);
app.use("/api/v2/devices", devicesRouter);
app.use("/api/v2/disciplines", disciplinesRouter);
app.use("/api/v2/issues", issuesSeverityV2Routes);
app.use("/api/v2/outage-messages", outageMessageRouter);
app.use("/api/v2/cde-sessions", cdeRoutes);
app.use("/api/v2/timezones", timezoneRouter);
app.use("/api/v2/currencies", currenciesRouter);
app.use("/api/v2/tenants", tenantRouter);
app.use("/api/v2/portfolios", portfoliosRouter);
app.use("/api/v2/systems", systemsRouter);
app.use("/api/v2/logs", logsRoutes);

app.listen(port, () => {
    console.log(`App running on http://localhost:${port}`);
});

export default app;