mirror of
https://github.com/MrUnknownDE/cloudflare-prometheus-exporter.git
synced 2026-04-28 02:23:45 +02:00
Cloudflare Prometheus Exporter
This commit is contained in:
128
src/worker.tsx
Normal file
128
src/worker.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { Hono } from "hono";
|
||||
import { LandingPage } from "./components/LandingPage";
|
||||
import { AccountMetricCoordinator } from "./durable-objects/AccountMetricCoordinator";
|
||||
import { MetricCoordinator } from "./durable-objects/MetricCoordinator";
|
||||
import { MetricExporter } from "./durable-objects/MetricExporter";
|
||||
import { type AppConfig, parseConfig } from "./lib/config";
|
||||
import { checkHealth, healthResponse } from "./lib/health";
|
||||
import { configFromEnv, createLogger } from "./lib/logger";
|
||||
import {
|
||||
ConfigKeySchema,
|
||||
getConfig,
|
||||
getConfigKey,
|
||||
getEnvDefaults,
|
||||
resetAllConfig,
|
||||
resetConfigKey,
|
||||
setConfigKey,
|
||||
} from "./lib/runtime-config";
|
||||
|
||||
export { MetricCoordinator, AccountMetricCoordinator, MetricExporter };
|
||||
|
||||
type Variables = { config: AppConfig };
|
||||
|
||||
const app = new Hono<{ Bindings: Env; Variables: Variables }>();
|
||||
|
||||
// Parse config middleware
|
||||
app.use("*", async (c, next) => {
|
||||
c.set("config", parseConfig(c.env));
|
||||
await next();
|
||||
});
|
||||
|
||||
// Disable guards
|
||||
app.use("*", async (c, next) => {
|
||||
const path = c.req.path;
|
||||
if (c.var.config.disableUi && path === "/") {
|
||||
return c.text("Not Found", 404);
|
||||
}
|
||||
if (c.var.config.disableConfigApi && path.startsWith("/config")) {
|
||||
return c.text("Not Found", 404);
|
||||
}
|
||||
await next();
|
||||
});
|
||||
|
||||
// Dynamic metrics path middleware (runs before routing)
|
||||
app.get(env.METRICS_PATH, async (c) => {
|
||||
const logger = createLogger("worker", configFromEnv(c.env)).withContext({
|
||||
request_id: crypto.randomUUID(),
|
||||
});
|
||||
logger.info("Metrics request received");
|
||||
|
||||
try {
|
||||
const coordinator = await MetricCoordinator.get(c.env);
|
||||
const output = await coordinator.export();
|
||||
logger.info("Metrics exported successfully");
|
||||
return c.text(output, 200, {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.error("Failed to collect metrics", { error: message });
|
||||
return c.text(`Error collecting metrics: ${message}`, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.get("/", (c) => c.html(<LandingPage config={c.var.config} />));
|
||||
|
||||
app.get("/health", async (c) => {
|
||||
const health = await checkHealth(c.env);
|
||||
return healthResponse(health);
|
||||
});
|
||||
|
||||
// Config API routes
|
||||
app.get("/config", async (c) => {
|
||||
const config = await getConfig(c.env);
|
||||
return c.json(config);
|
||||
});
|
||||
|
||||
app.get("/config/defaults", (c) => {
|
||||
const defaults = getEnvDefaults(c.env);
|
||||
return c.json(defaults);
|
||||
});
|
||||
|
||||
app.get("/config/:key", async (c) => {
|
||||
const keyResult = ConfigKeySchema.safeParse(c.req.param("key"));
|
||||
if (!keyResult.success) {
|
||||
return c.json({ error: "Invalid config key" }, 400);
|
||||
}
|
||||
const value = await getConfigKey(c.env, keyResult.data);
|
||||
return c.json({ key: keyResult.data, value });
|
||||
});
|
||||
|
||||
app.put("/config/:key", async (c) => {
|
||||
const keyResult = ConfigKeySchema.safeParse(c.req.param("key"));
|
||||
if (!keyResult.success) {
|
||||
return c.json({ error: "Invalid config key" }, 400);
|
||||
}
|
||||
const body = await c.req.json<{ value: unknown }>().catch(() => null);
|
||||
if (!body || !("value" in body)) {
|
||||
return c.json({ error: "Request body must contain 'value'" }, 400);
|
||||
}
|
||||
const result = await setConfigKey(c.env, keyResult.data, body.value);
|
||||
if (!result.success) {
|
||||
return c.json(
|
||||
{ error: "Invalid value", details: result.error.issues },
|
||||
400,
|
||||
);
|
||||
}
|
||||
return c.json(result.config);
|
||||
});
|
||||
|
||||
app.delete("/config/:key", async (c) => {
|
||||
const keyResult = ConfigKeySchema.safeParse(c.req.param("key"));
|
||||
if (!keyResult.success) {
|
||||
return c.json({ error: "Invalid config key" }, 400);
|
||||
}
|
||||
const config = await resetConfigKey(c.env, keyResult.data);
|
||||
return c.json(config);
|
||||
});
|
||||
|
||||
app.delete("/config", async (c) => {
|
||||
const config = await resetAllConfig(c.env);
|
||||
return c.json(config);
|
||||
});
|
||||
|
||||
app.notFound((c) => c.text("Not Found", 404));
|
||||
|
||||
export default app;
|
||||
Reference in New Issue
Block a user