Openstatus www.openstatus.dev

๐Ÿ› fix smart user (#732)

authored by

Thibault Le Ouay and committed by
GitHub
ac3462f5 4d5b23ea

+20 -171
-159
apps/web/src/app/api/checker/regions/_checker.ts
··· 1 - import { Receiver } from "@upstash/qstash"; 2 - 3 - import { db, eq, schema } from "@openstatus/db"; 4 - import type { MonitorStatus } from "@openstatus/db/src/schema"; 5 - import { 6 - publishPingResponse, 7 - tbIngestPingResponse, 8 - } from "@openstatus/tinybird"; 9 - 10 - import { env } from "@/env"; 11 - import type { Payload } from "../schema"; 12 - import { payloadSchema } from "../schema"; 13 - 14 - export const monitorSchema = tbIngestPingResponse.pick({ 15 - url: true, 16 - cronTimestamp: true, 17 - }); 18 - 19 - const monitor = async ( 20 - payload: Payload, 21 - statusCode: number, 22 - region: string, 23 - latency: number, 24 - ) => { 25 - const { monitorId, cronTimestamp, url, workspaceId } = payload; 26 - 27 - await publishPingResponse({ 28 - timestamp: Date.now(), 29 - statusCode, 30 - monitorId, 31 - cronTimestamp, 32 - url, 33 - workspaceId, 34 - latency, 35 - region, 36 - }); 37 - }; 38 - 39 - export const checker = async (request: Request, region: string) => { 40 - const r = new Receiver({ 41 - currentSigningKey: env.QSTASH_CURRENT_SIGNING_KEY, 42 - nextSigningKey: env.QSTASH_NEXT_SIGNING_KEY, 43 - }); 44 - 45 - const jsonData = await request.json(); 46 - 47 - const isValid = r.verify({ 48 - signature: request?.headers?.get("Upstash-Signature") || "", 49 - body: JSON.stringify(jsonData), 50 - }); 51 - if (!isValid) { 52 - throw new Error("Could not parse request"); 53 - } 54 - 55 - const result = payloadSchema.safeParse(jsonData); 56 - 57 - if (!result.success) { 58 - console.error(result.error); 59 - throw new Error("Invalid response body"); 60 - } 61 - 62 - try { 63 - const startTime = performance.now(); 64 - const res = await ping(result.data); 65 - const endTime = performance.now(); 66 - const latency = endTime - startTime; 67 - await monitor(result.data, res.status, region, latency); 68 - if (res.ok) { 69 - if (result.data?.status === "error") { 70 - await updateMonitorStatus({ 71 - monitorId: result.data.monitorId, 72 - status: "active", 73 - }); 74 - } 75 - } 76 - if (!res.ok) { 77 - if (result.data?.status !== "error") { 78 - await triggerAlerting({ monitorId: result.data.monitorId }); 79 - await updateMonitorStatus({ 80 - monitorId: result.data.monitorId, 81 - status: "error", 82 - }); 83 - } 84 - } 85 - } catch (e) { 86 - console.error(e); 87 - // if on the third retry we still get an error, we should report it 88 - if (request.headers.get("Upstash-Retried") === "2") { 89 - await monitor(result.data, 500, region, -1); 90 - if (result.data?.status !== "error") { 91 - await triggerAlerting({ monitorId: result.data.monitorId }); 92 - await updateMonitorStatus({ 93 - monitorId: result.data.monitorId, 94 - status: "error", 95 - }); 96 - } 97 - // Here we do the alerting} 98 - } 99 - } 100 - }; 101 - 102 - export const ping = async ( 103 - data: Pick<Payload, "headers" | "body" | "method" | "url">, 104 - ) => { 105 - const headers = 106 - data?.headers?.reduce((o, v) => { 107 - if (v.key.trim() === "") return o; // removes empty keys from the header 108 - return { ...o, [v.key]: v.value }; 109 - }, {}) || {}; 110 - 111 - const res = await fetch(data?.url, { 112 - method: data?.method, 113 - cache: "no-store", 114 - headers: { 115 - "OpenStatus-Ping": "true", 116 - ...headers, 117 - }, 118 - // Avoid having "TypeError: Request with a GET or HEAD method cannot have a body." error 119 - ...(data.method === "POST" && { body: data?.body }), 120 - }); 121 - 122 - return res; 123 - }; 124 - 125 - const triggerAlerting = async ({ monitorId }: { monitorId: string }) => { 126 - const notifications = await db 127 - .select() 128 - .from(schema.notificationsToMonitors) 129 - .innerJoin( 130 - schema.notification, 131 - eq(schema.notification.id, schema.notificationsToMonitors.notificationId), 132 - ) 133 - .innerJoin( 134 - schema.monitor, 135 - eq(schema.monitor.id, schema.notificationsToMonitors.monitorId), 136 - ) 137 - .where(eq(schema.monitor.id, Number(monitorId))) 138 - .all(); 139 - for (const notif of notifications) { 140 - // await providerToFunction[notif.notification.provider]({ 141 - // monitor: selectMonitorSchema.parse(notif.monitor), 142 - // notification: selectNotificationSchema.parse(notif.notification), 143 - // }); 144 - } 145 - }; 146 - 147 - const updateMonitorStatus = async ({ 148 - monitorId, 149 - status, 150 - }: { 151 - monitorId: string; 152 - status: MonitorStatus; 153 - }) => { 154 - await db 155 - .update(schema.monitor) 156 - .set({ status }) 157 - .where(eq(schema.monitor.id, Number(monitorId))) 158 - .run(); 159 - };
+20 -12
apps/web/src/app/api/checker/test/route.ts
··· 15 15 } 16 16 17 17 export async function POST(request: Request) { 18 - const json = await request.json(); 19 - const _valid = payloadSchema 20 - .pick({ url: true, method: true, headers: true, body: true }) 21 - .merge(z.object({ region: monitorFlyRegionSchema.default("ams") })) 22 - .safeParse(json); 23 - 24 - if (!_valid.success) { 25 - return NextResponse.json({ success: false }, { status: 400 }); 26 - } 18 + try { 19 + const json = await request.json(); 20 + const _valid = payloadSchema 21 + .pick({ url: true, method: true, headers: true, body: true }) 22 + .merge(z.object({ region: monitorFlyRegionSchema.default("ams") })) 23 + .safeParse(json); 27 24 28 - const { url, region, method, headers, body } = _valid.data; 25 + if (!_valid.success) { 26 + return NextResponse.json({ success: false }, { status: 400 }); 27 + } 29 28 30 - const res = await checkRegion(url, region, { method, headers, body }); 29 + const { url, region, method, headers, body } = _valid.data; 30 + // ๐Ÿง‘โ€๐Ÿ’ป for the smart one who want to create a loop hole 31 + if (url === "https://www.openstatus.dev/api/checker/test") { 32 + return NextResponse.json({ success: true }, { status: 200 }); 33 + } 34 + const res = await checkRegion(url, region, { method, headers, body }); 31 35 32 - return NextResponse.json(res); 36 + return NextResponse.json(res); 37 + } catch (e) { 38 + console.error(e); 39 + return NextResponse.json({ success: false }, { status: 400 }); 40 + } 33 41 }