Openstatus
www.openstatus.dev
1import type { Variables } from "@/types";
2import { getLogger } from "@logtape/logtape";
3import {
4 type EventProps,
5 parseInputToProps,
6 setupAnalytics,
7} from "@openstatus/analytics";
8import type { Context, Next } from "hono";
9
10const logger = getLogger("api-server");
11
12export function trackMiddleware(event: EventProps, eventProps?: string[]) {
13 return async (c: Context<{ Variables: Variables }, "/*">, next: Next) => {
14 await next();
15
16 // REMINDER: only track the event if the request was successful
17 const isValid = c.res.status.toString().startsWith("2") && !c.error;
18
19 if (isValid) {
20 // We have checked the request to be valid already
21 let json: unknown;
22 if (c.req.raw.bodyUsed) {
23 try {
24 json = await c.req.json();
25 } catch {
26 json = {};
27 }
28 }
29 const additionalProps = parseInputToProps(json, eventProps);
30 const workspace = c.get("workspace");
31
32 setupAnalytics({
33 userId: `api_${workspace.id}`,
34 workspaceId: `${workspace.id}`,
35 plan: workspace.plan,
36 location: c.req.raw.headers.get("x-forwarded-for") ?? undefined,
37 userAgent: c.req.raw.headers.get("user-agent") ?? undefined,
38 })
39 .then((analytics) => analytics.track({ ...event, additionalProps }))
40 .catch(() => {
41 logger.warn(
42 "Failed to send analytics event {event} for workspace {workspaceId}",
43 { event: event.name, workspaceId: workspace.id },
44 );
45 });
46 }
47 };
48}