Openstatus
www.openstatus.dev
1import { getSentry } from "@hono/sentry";
2import { monitorPeriodicitySchema } from "@openstatus/db/src/schema/constants";
3import { Hono } from "hono";
4import { env } from "../env";
5import { sendCheckerTasks } from "./checker";
6import { sendFollowUpEmails } from "./emails";
7import {
8 LaunchMonitorWorkflow,
9 Step3Days,
10 Step14Days,
11 StepPaused,
12 workflowStepSchema,
13} from "./monitor";
14
15const app = new Hono({ strict: false });
16
17app.use("*", async (c, next) => {
18 if (c.req.header("authorization") !== env().CRON_SECRET) {
19 return c.text("Unauthorized", 401);
20 }
21
22 return next();
23});
24
25app.get("/checker/:period", async (c) => {
26 const period = c.req.param("period");
27
28 const schema = monitorPeriodicitySchema.safeParse(period);
29
30 if (!schema.success) {
31 return c.json({ error: schema.error.issues?.[0].message }, 400);
32 }
33 const sentry = getSentry(c);
34 const checkInId = sentry.captureCheckIn({
35 monitorSlug: period,
36 status: "in_progress",
37 });
38 try {
39 await sendCheckerTasks(schema.data, c);
40 sentry.captureCheckIn({
41 checkInId,
42 monitorSlug: period,
43 status: "ok",
44 });
45 return c.json({ success: schema.data }, 200);
46 } catch (e) {
47 console.error(e);
48 sentry.captureMessage(`Error in /checker/${period} cron: ${e}`, "error");
49 sentry.captureCheckIn({
50 checkInId,
51 monitorSlug: period,
52 status: "error",
53 });
54 return c.text("Internal Server Error", 500);
55 }
56});
57
58app.get("/emails/follow-up", async (c) => {
59 try {
60 await sendFollowUpEmails();
61 return c.json({ success: true }, 200);
62 } catch (e) {
63 console.error(e);
64 return c.text("Internal Server Error", 500);
65 }
66});
67
68app.get("/monitors", async (c) => {
69 await LaunchMonitorWorkflow();
70 return c.json({ success: true }, 200);
71});
72
73app.get("/monitors/:step", async (c) => {
74 const step = c.req.param("step");
75 const schema = workflowStepSchema.safeParse(step);
76
77 const userId = c.req.query("userId");
78 const initialRun = c.req.query("initialRun");
79 if (!schema.success) {
80 return c.json({ error: schema.error.issues?.[0].message }, 400);
81 }
82
83 if (!userId) {
84 getSentry(c).captureMessage(
85 "userId is missing in /monitors/:step cron",
86 "error",
87 );
88 return c.json({ error: "userId is required" }, 400);
89 }
90 if (!initialRun) {
91 getSentry(c).captureMessage(
92 "initalRun is missing in /monitors/:step cron",
93 "error",
94 );
95 return c.json({ error: "initialRun is required" }, 400);
96 }
97
98 switch (schema.data) {
99 case "14days":
100 // We send the first email
101 await Step14Days(Number(userId), Number(initialRun));
102 break;
103 case "3days":
104 await Step3Days(Number(userId), Number(initialRun));
105 // 3 days before we send the second email
106 break;
107 case "paused":
108 // Let's pause the monitor
109 await StepPaused(Number(userId), Number(initialRun));
110 break;
111 default:
112 throw new Error("Invalid step");
113 }
114 // Swith on step
115 // and do the right action
116 //
117 return c.json({ success: true }, 200);
118});
119
120export { app as cronRouter };