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;