diff options
Diffstat (limited to 'packages/api')
| -rw-r--r-- | packages/api/index.ts | 5 | ||||
| -rw-r--r-- | packages/api/middlewares/prometheusAuth.ts | 33 | ||||
| -rw-r--r-- | packages/api/package.json | 1 | ||||
| -rw-r--r-- | packages/api/routes/metrics.ts | 16 |
4 files changed, 54 insertions, 1 deletions
diff --git a/packages/api/index.ts b/packages/api/index.ts index 2eb22d8f..4103e033 100644 --- a/packages/api/index.ts +++ b/packages/api/index.ts @@ -10,6 +10,7 @@ import bookmarks from "./routes/bookmarks"; import health from "./routes/health"; import highlights from "./routes/highlights"; import lists from "./routes/lists"; +import metrics, { registerMetrics } from "./routes/metrics"; import publicRoute from "./routes/public"; import rss from "./routes/rss"; import tags from "./routes/tags"; @@ -37,6 +38,7 @@ const app = new Hono<{ }>() .use(logger()) .use(poweredBy()) + .use("*", registerMetrics) .use(async (c, next) => { // Ensure that the ctx is set if (!c.var.ctx) { @@ -49,6 +51,7 @@ const app = new Hono<{ .route("/trpc", trpc) .route("/v1", v1) .route("/assets", assets) - .route("/public", publicRoute); + .route("/public", publicRoute) + .route("/metrics", metrics); export default app; diff --git a/packages/api/middlewares/prometheusAuth.ts b/packages/api/middlewares/prometheusAuth.ts new file mode 100644 index 00000000..bf35608f --- /dev/null +++ b/packages/api/middlewares/prometheusAuth.ts @@ -0,0 +1,33 @@ +import { createMiddleware } from "hono/factory"; +import { HTTPException } from "hono/http-exception"; + +import serverConfig from "@karakeep/shared/config"; + +export const prometheusAuthMiddleware = createMiddleware(async (c, next) => { + const { metricsToken } = serverConfig.prometheus; + + // If no token is configured, deny access (safe default) + if (!metricsToken) { + throw new HTTPException(404, { + message: "Not Found", + }); + } + + const auth = c.req.header("Authorization"); + + if (!auth || !auth.startsWith("Bearer ")) { + throw new HTTPException(401, { + message: "Unauthorized", + }); + } + + const token = auth.slice(7); // Remove "Bearer " prefix + + if (token !== metricsToken) { + throw new HTTPException(401, { + message: "Unauthorized", + }); + } + + await next(); +}); diff --git a/packages/api/package.json b/packages/api/package.json index 54656e64..18d70501 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -13,6 +13,7 @@ "test": "vitest" }, "dependencies": { + "@hono/prometheus": "^1.0.2", "@hono/trpc-server": "^0.4.0", "@hono/zod-validator": "^0.5.0", "@karakeep/db": "workspace:*", diff --git a/packages/api/routes/metrics.ts b/packages/api/routes/metrics.ts new file mode 100644 index 00000000..90eff5b9 --- /dev/null +++ b/packages/api/routes/metrics.ts @@ -0,0 +1,16 @@ +// Import stats to register Prometheus metrics +import "@karakeep/trpc/stats"; + +import { prometheus } from "@hono/prometheus"; +import { Hono } from "hono"; +import { register } from "prom-client"; + +import { prometheusAuthMiddleware } from "../middlewares/prometheusAuth"; + +export const { printMetrics, registerMetrics } = prometheus({ + registry: register, +}); + +const app = new Hono().get("/", prometheusAuthMiddleware, printMetrics); + +export default app; |
