my own status page
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: remove triage agent

+9 -53
+1 -4
src/db.ts
··· 295 295 export async function updateIncident( 296 296 db: D1Database, 297 297 id: number, 298 - data: { status?: string; triage_report?: string; resolved_at?: number }, 298 + data: { status?: string; resolved_at?: number }, 299 299 ): Promise<void> { 300 300 const sets: string[] = []; 301 301 const values: unknown[] = []; 302 302 if (data.status) { sets.push("status = ?"); values.push(data.status); } 303 - if (data.triage_report !== undefined) { sets.push("triage_report = ?"); values.push(data.triage_report); } 304 303 if (data.resolved_at) { sets.push("resolved_at = ?"); values.push(data.resolved_at); } 305 304 sets.push("updated_at = ?"); 306 305 values.push(Math.floor(Date.now() / 1000)); ··· 352 351 title: row.title as string, 353 352 status: row.status as string, 354 353 severity: row.severity as string, 355 - triage_report: row.triage_report as string | null, 356 354 github_repo: row.github_repo as string | null, 357 355 github_issue_number: row.github_issue_number as number | null, 358 356 started_at: row.started_at as number, ··· 418 416 title: row.title as string, 419 417 status: row.status as string, 420 418 severity: row.severity as string, 421 - triage_report: row.triage_report as string | null, 422 419 github_repo: row.github_repo as string | null, 423 420 github_issue_number: row.github_issue_number as number | null, 424 421 started_at: row.started_at as number,
+2 -2
src/github.ts
··· 165 165 const kvKey = `gh_sync:${incident.id}:last`; 166 166 const lastSeen = await kv.get(kvKey); 167 167 const comments = await fetchComments(token, parsed.owner, parsed.repo, incident.github_issue_number, lastSeen ?? undefined); 168 - const human = comments.filter((c) => c.user.type !== "Bot" && !c.body.startsWith("Automated incident detected") && !c.body.startsWith("## Triage Report") && !c.body.startsWith("Service recovered automatically")); 168 + const human = comments.filter((c) => c.user.type !== "Bot" && !c.body.startsWith("Automated incident detected") && !c.body.startsWith("Service recovered automatically")); 169 169 170 170 // Use the last human comment as the resolve message, or fall back to generic 171 171 const resolveMsg = human.length > 0 ? human[human.length - 1].body : "Issue closed on GitHub"; ··· 192 192 const comments = await fetchComments(token, parsed.owner, parsed.repo, incident.github_issue_number, lastSeen ?? undefined); 193 193 194 194 // Filter to human comments only (skip bots and our own posts) 195 - const human = comments.filter((c) => c.user.type !== "Bot" && !c.body.startsWith("Automated incident detected") && !c.body.startsWith("## Triage Report") && !c.body.startsWith("Service recovered automatically")); 195 + const human = comments.filter((c) => c.user.type !== "Bot" && !c.body.startsWith("Automated incident detected") && !c.body.startsWith("Service recovered automatically")); 196 196 197 197 for (const comment of human) { 198 198 await addIncidentUpdate(db, incident.id, incident.status, comment.body);
+2 -21
src/index.ts
··· 89 89 ]); 90 90 91 91 const checks = Object.values(manifest).flatMap((machine) => { 92 - const triageUrl = machine.triage_url; 93 92 return machine.services 94 93 .filter((svc) => svc.health_url) 95 94 .map(async (svc) => { ··· 102 101 103 102 if (isDown) { 104 103 // Track consecutive failures in KV for flap prevention 105 - const failKey = `triage:${svc.name}:failures`; 104 + const failKey = `incident:${svc.name}:failures`; 106 105 const current = parseInt((await env.KV.get(failKey)) ?? "0"); 107 106 const failures = current + 1; 108 107 await env.KV.put(failKey, String(failures), { expirationTtl: 1800 }); ··· 137 136 } catch (_) {} // best effort 138 137 } 139 138 } 140 - 141 - // Fire webhook to triage agent (non-blocking) 142 - if (triageUrl && env.TRIAGE_AUTH_TOKEN) { 143 - fetch(triageUrl, { 144 - method: "POST", 145 - headers: { 146 - "Content-Type": "application/json", 147 - Authorization: `Bearer ${env.TRIAGE_AUTH_TOKEN}`, 148 - }, 149 - body: JSON.stringify({ 150 - incident_id: id, 151 - service_id: svc.name, 152 - service_name: svc.name, 153 - health_url: svc.health_url, 154 - callback_url: `https://infra.dunkirk.sh/api/incidents/${id}`, 155 - }), 156 - }).catch(() => {}); // fire and forget 157 - } 158 139 } 159 140 } 160 141 } 161 142 } else { 162 143 // Service is up — clear failure counter (only if one exists, to avoid unnecessary KV delete ops) 163 - const failKey = `triage:${svc.name}:failures`; 144 + const failKey = `incident:${svc.name}:failures`; 164 145 if (await env.KV.get(failKey)) { 165 146 await env.KV.delete(failKey); 166 147 }
+4 -23
src/routes/incidents.ts
··· 9 9 } from "../db"; 10 10 import { commentOnIssue, parseRepo } from "../github"; 11 11 12 - function authCheck(request: Request, env: Env): boolean { 13 - const auth = request.headers.get("Authorization"); 14 - if (!auth || !env.TRIAGE_AUTH_TOKEN) return false; 15 - return auth === `Bearer ${env.TRIAGE_AUTH_TOKEN}`; 16 - } 17 - 18 12 export async function handleIncidentRoute( 19 13 request: Request, 20 14 env: Env, ··· 30 24 31 25 // POST /api/incidents 32 26 if (path === "/api/incidents" && request.method === "POST") { 33 - if (!authCheck(request, env)) { 34 - return Response.json({ error: "unauthorized" }, { status: 401 }); 35 - } 36 27 const body = await request.json<{ service_id: string; title: string; severity: "critical" | "major" | "minor" }>(); 37 28 if (!body.service_id || !body.title || !body.severity) { 38 29 return Response.json({ error: "missing fields" }, { status: 400 }); ··· 51 42 52 43 // PATCH /api/incidents/:id 53 44 if (singleMatch && request.method === "PATCH") { 54 - if (!authCheck(request, env)) { 55 - return Response.json({ error: "unauthorized" }, { status: 401 }); 56 - } 57 45 const id = parseInt(singleMatch[1]); 58 - const body = await request.json<{ status?: string; triage_report?: string; summary?: string }>(); 59 - const updateData: { status?: string; triage_report?: string; resolved_at?: number } = {}; 46 + const body = await request.json<{ status?: string; summary?: string }>(); 47 + const updateData: { status?: string; resolved_at?: number } = {}; 60 48 if (body.status) updateData.status = body.status; 61 - if (body.triage_report !== undefined) updateData.triage_report = body.triage_report; 62 49 if (body.status === "resolved") updateData.resolved_at = Math.floor(Date.now() / 1000); 63 50 await updateIncident(env.DB, id, updateData); 64 51 if (body.status) { 65 - const timelineMsg = body.summary ?? body.triage_report ?? `Status changed to ${body.status}`; 52 + const timelineMsg = body.summary ?? `Status changed to ${body.status}`; 66 53 await addIncidentUpdate(env.DB, id, body.status, timelineMsg); 67 54 } 68 55 69 - // Sync triage report to GitHub issue 70 56 const incident = await getIncident(env.DB, id); 71 57 if (env.GITHUB_TOKEN && incident?.github_repo && incident.github_issue_number) { 72 58 const parsed = parseRepo(`https://github.com/${incident.github_repo}`); 73 59 if (parsed) { 74 - if (body.triage_report) { 75 - commentOnIssue(env.GITHUB_TOKEN, parsed.owner, parsed.repo, incident.github_issue_number, `## Triage Report\n\n${body.triage_report}`).catch(() => {}); 76 - } else if (body.status) { 60 + if (body.status) { 77 61 commentOnIssue(env.GITHUB_TOKEN, parsed.owner, parsed.repo, incident.github_issue_number, `Status changed to **${body.status}**`).catch(() => {}); 78 62 } 79 63 } ··· 85 69 // POST /api/incidents/:id/updates 86 70 const updatesMatch = path.match(/^\/api\/incidents\/(\d+)\/updates$/); 87 71 if (updatesMatch && request.method === "POST") { 88 - if (!authCheck(request, env)) { 89 - return Response.json({ error: "unauthorized" }, { status: 401 }); 90 - } 91 72 const body = await request.json<{ status: string; message: string }>(); 92 73 if (!body.status || !body.message) { 93 74 return Response.json({ error: "missing fields" }, { status: 400 });
-3
src/types.ts
··· 17 17 hostname: string; 18 18 tailscale_host: string; 19 19 type: "server" | "client"; 20 - triage_url: string | null; 21 20 services: Service[]; 22 21 } 23 22 ··· 38 37 title: string; 39 38 status: "investigating" | "identified" | "resolved"; 40 39 severity: "critical" | "major" | "minor"; 41 - triage_report: string | null; 42 40 github_repo: string | null; 43 41 github_issue_number: number | null; 44 42 started_at: number; ··· 63 61 DB: D1Database; 64 62 KV: KVNamespace; 65 63 TAILSCALE_API_KEY?: string; 66 - TRIAGE_AUTH_TOKEN?: string; 67 64 GITHUB_TOKEN?: string; 68 65 GITHUB_ASSIGN_TOKEN?: string; 69 66 GITHUB_ASSIGNEE?: string;