···77import { publicRoute } from "./public";
88import { api } from "./v1";
991010-const app = new Hono();
1010+const app = new Hono({ strict: false });
1111app.use("*", sentry({ dsn: process.env.SENTRY_DSN }));
12121313/**
+99
apps/server/src/v1/monitor.ts
···77 monitorMethods,
88 monitorPeriodicity,
99} from "@openstatus/db/src/schema";
1010+import { getMonitorList, Tinybird } from "@openstatus/tinybird";
1111+import { Redis } from "@openstatus/upstash";
10121313+import { env } from "../env";
1114import type { Variables } from "./index";
1215import { ErrorSchema } from "./shared";
1616+1717+const tb = new Tinybird({ token: env.TINY_BIRD_API_KEY });
1818+const redis = Redis.fromEnv();
13191420const ParamsSchema = z.object({
1521 id: z
···462468463469 await db.delete(monitor).where(eq(monitor.id, monitorId)).run();
464470 return c.jsonT({ message: "Deleted" });
471471+});
472472+473473+const dailyStatsSchema = z.object({
474474+ ok: z.number().int().openapi({
475475+ description: "The number of ok responses",
476476+ }),
477477+ count: z
478478+ .number()
479479+ .int()
480480+ .openapi({ description: "The total number of request" }),
481481+ avgLatency: z.number().int().openapi({ description: "The average latency" }),
482482+ day: z.string().openapi({ description: "the date of the event" }),
483483+});
484484+485485+const dailyStatsSchemaArray = z
486486+ .array(dailyStatsSchema)
487487+ .openapi({ description: "The daily stats" });
488488+489489+const getMonitorStats = createRoute({
490490+ method: "get",
491491+ tags: ["monitor"],
492492+ description: "Get monitor daily summary",
493493+ path: "/:id/summary",
494494+ request: {
495495+ params: ParamsSchema,
496496+ },
497497+ responses: {
498498+ 200: {
499499+ content: {
500500+ "application/json": {
501501+ schema: z.object({
502502+ data: dailyStatsSchemaArray,
503503+ }),
504504+ },
505505+ },
506506+ description: "All the historical metrics",
507507+ },
508508+ 404: {
509509+ content: {
510510+ "application/json": {
511511+ schema: ErrorSchema,
512512+ },
513513+ },
514514+ description: "Not found",
515515+ },
516516+ 401: {
517517+ content: {
518518+ "application/json": {
519519+ schema: ErrorSchema,
520520+ },
521521+ },
522522+ description: "Returns an error",
523523+ },
524524+ },
525525+});
526526+monitorApi.openapi(getMonitorStats, async (c) => {
527527+ const workspaceId = Number(c.get("workspaceId"));
528528+ const { id } = c.req.valid("param");
529529+530530+ const monitorId = Number(id);
531531+ const _monitor = await db
532532+ .select()
533533+ .from(monitor)
534534+ .where(eq(monitor.id, monitorId))
535535+ .get();
536536+537537+ if (!_monitor) return c.jsonT({ code: 404, message: "Not Found" });
538538+539539+ if (workspaceId !== _monitor.workspaceId)
540540+ return c.jsonT({ code: 401, message: "Unauthorized" });
541541+542542+ const cache = await redis.get<z.infer<typeof dailyStatsSchemaArray>>(
543543+ `${monitorId}-daily-stats`,
544544+ );
545545+546546+ if (cache) {
547547+ console.log("fetching from cache");
548548+ return c.jsonT({
549549+ data: cache,
550550+ });
551551+ }
552552+553553+ console.log("fetching from tinybird");
554554+ const res = await getMonitorList(tb)({
555555+ monitorId: String(monitorId),
556556+ limit: 30,
557557+ // return data in utc
558558+ timezone: "Etc/UTC",
559559+ });
560560+561561+ await redis.set(`${monitorId}-daily-stats`, res.data, { ex: 600 });
562562+563563+ return c.jsonT({ data: res.data });
465564});
466565467566export { monitorApi };
+3-1
apps/web/src/lib/tracker.ts
···8080 for (const date of dateSequence) {
8181 const timestamp = date.getTime();
8282 const cronTimestamp =
8383- dataIndex < data.length ? data[dataIndex].day.getTime() : undefined;
8383+ dataIndex < data.length
8484+ ? new Date(data[dataIndex].day).getTime()
8585+ : undefined;
84868587 if (
8688 cronTimestamp &&
+4-1
packages/tinybird/src/validation.ts
···6060 count: z.number().int(),
6161 ok: z.number().int(),
6262 avgLatency: z.number().int(),
6363- day: z.coerce.date(),
6363+ day: z.string().transform((val) => {
6464+ // That's a hack because clickhouse return the date in UTC but in shitty format (2021-09-01 00:00:00)
6565+ return new Date(`${val} GMT`).toISOString();
6666+ }),
6467});
65686669/**