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