import { OpenAPIHono } from "@hono/zod-openapi"; import type { Scope, Workspace } from "@openstatus/db/src/schema"; import { Scalar } from "@scalar/hono-api-reference"; import { cors } from "hono/cors"; import type { RequestIdVariables } from "hono/request-id"; import { handleZodError } from "@/libs/errors"; import { authMiddleware, requireWriteScope } from "@/libs/middlewares"; import { checkApi } from "./check"; import { incidentsApi } from "./incidents"; import { maintenancesApi } from "./maintenances"; import { monitorsApi } from "./monitors"; import { notificationsApi } from "./notifications"; import { pagesApi } from "./pages"; import { pageSubscribersApi } from "./pageSubscribers"; import { statusReportsApi } from "./statusReports"; import { statusReportUpdatesApi } from "./statusReportUpdates"; import { whoamiApi } from "./whoami"; export type Variables = RequestIdVariables & { workspace: Workspace; apiKey: { id: string; createdById?: number; scopes: Scope[] }; }; export const api = new OpenAPIHono<{ Variables: Variables }>({ defaultHook: handleZodError, }); api.use("/openapi", cors()); api.openAPIRegistry.registerComponent("securitySchemes", "ApiKeyAuth", { type: "apiKey", in: "header", name: "x-openstatus-key", "x-openstatus-key": "string", }); // this is a fix for the memory leak if (process.env.NODE_ENV === "production") { api.get("/openapi", (c) => c.redirect("https://api.openstatus.dev/openapi-v1.json"), ); } else { api.doc("/openapi", { openapi: "3.0.0", info: { version: "1.0.0", title: "OpenStatus API", contact: { email: "ping@openstatus.dev", url: "https://www.openstatus.dev", }, description: "This version is deprecated please use v2 API: Read more about the new API in the documentation: https://www.openstatus.dev/docs/reference/api", }, tags: [ { name: "monitor", description: "Monitor related endpoints", "x-displayName": "Monitor", }, { name: "page", description: "Page related endpoints", "x-displayName": "Page", }, { name: "status_report", description: "Status report related endpoints", "x-displayName": "Status Report", }, { name: "status_report_update", description: "Status report update related endpoints", "x-displayName": "Status Report Update", }, { name: "incident", description: "Incident related endpoints", "x-displayName": "Incident", }, { name: "maintenance", description: "Maintenance related endpoints", "x-displayName": "Maintenance", }, { name: "notification", description: "Notification related endpoints", "x-displayName": "Notification", }, { name: "page_subscriber", description: "Page subscriber related endpoints", "x-displayName": "Page Subscriber", }, { name: "check", description: "Check related endpoints", "x-displayName": "Check", }, { name: "whoami", description: "WhoAmI related endpoints", "x-displayName": "WhoAmI", }, ], security: [ { ApiKeyAuth: [], }, ], }); } api.get( "/", Scalar({ url: "/openapi-v1.json", servers: [ { url: "https://api.openstatus.dev/v1", description: "Production server", }, { url: "http://localhost:3000/v1", description: "Dev server", }, ], metaData: { title: "OpenStatus API", description: "Start building with OpenStatus API", ogDescription: "API Reference", ogTitle: "OpenStatus API", ogImage: "https://openstatus.dev/api/og?title=OpenStatus%20API&description=API%20Reference", twitterCard: "summary_large_image", }, }), ); /** * Middlewares */ api.use("/*", authMiddleware); // Primary scope enforcement for V1: routes here use inline Drizzle // queries instead of `@openstatus/services`, so the service-level // `requireScope` won't run. After per-route migration to services, // this stays as defense-in-depth. api.use("/*", requireWriteScope()); /** * Routes */ api.route("/monitor", monitorsApi); api.route("/page", pagesApi); api.route("/status_report", statusReportsApi); api.route("/status_report_update", statusReportUpdatesApi); api.route("/incident", incidentsApi); api.route("/maintenance", maintenancesApi); api.route("/notification", notificationsApi); api.route("/page_subscriber", pageSubscribersApi); api.route("/check", checkApi); api.route("/whoami", whoamiApi);