Openstatus
www.openstatus.dev
1import { AsyncLocalStorage } from "node:async_hooks";
2// import * as Sentry from "@sentry/node";
3import { sentry } from "@hono/sentry";
4import {
5 configureSync,
6 getConsoleSink,
7 getLogger,
8 jsonLinesFormatter,
9 withContext,
10} from "@logtape/logtape";
11// import { getSentrySink } from "@logtape/sentry";
12import { Hono } from "hono";
13import { showRoutes } from "hono/dev";
14import { requestId } from "hono/request-id";
15// import { logger } from "hono/logger";
16import { checkerRoute } from "./checker";
17import { cronRouter } from "./cron";
18import { env } from "./env";
19
20const { NODE_ENV, PORT } = env();
21
22configureSync({
23 sinks: {
24 console: getConsoleSink({ formatter: jsonLinesFormatter }),
25 // sentry: getSentrySink(),
26 },
27 loggers: [
28 {
29 category: "workflow",
30 lowestLevel: "debug",
31 sinks: ["console"],
32 },
33 ],
34 contextLocalStorage: new AsyncLocalStorage(),
35});
36
37const logger = getLogger(["workflow"]);
38const app = new Hono({ strict: false });
39
40app.use("*", requestId());
41
42app.use("*", sentry({ dsn: env().SENTRY_DSN }));
43
44app.use("*", async (c, next) => {
45 const requestId = c.get("requestId");
46 const startTime = Date.now();
47
48 await withContext(
49 {
50 requestId,
51 method: c.req.method,
52 url: c.req.url,
53 userAgent: c.req.header("User-Agent"),
54 // ipAddress: c.req.header("CF-Connecting-IP") || c.req.header("X-Forwarded-For")
55 },
56 async () => {
57 logger.info("Request started", {
58 method: c.req.method,
59 url: c.req.url,
60 requestId,
61 });
62
63 await next();
64
65 const duration = Date.now() - startTime;
66 logger.info("Request completed", {
67 status: c.res.status,
68 duration,
69 requestId,
70 });
71 },
72 );
73});
74
75app.onError((err, c) => {
76 logger.error("Request error", {
77 error: {
78 name: err.name,
79 message: err.message,
80 stack: err.stack,
81 },
82 method: c.req.method,
83 url: c.req.url,
84 });
85 c.get("sentry").captureException(err);
86
87 return c.json({ error: "Internal server error" }, 500);
88});
89
90app.get("/", (c) => c.text("workflows", 200));
91
92/**
93 * Ping Pong
94 */
95app.get("/ping", (c) => c.json({ ping: "pong" }, 200));
96
97/**
98 * Cron Routes
99 */
100app.route("/cron", cronRouter);
101
102app.route("/", checkerRoute);
103
104if (NODE_ENV === "development") {
105 showRoutes(app, { verbose: true, colorize: true });
106}
107
108console.log(`Starting server on port ${PORT}`);
109
110const server = { port: PORT, fetch: app.fetch };
111
112export default server;