Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
3.7 kB ยท 123 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124import { and, eq } from "@openstatus/db";import { page, statusReport, statusReportUpdate,} from "@openstatus/db/src/schema";
import { emitAudit } from "../audit";import { requireScope } from "../auth";import { type ServiceContext, withTransaction } from "../context";import { ConflictError, NotFoundError } from "../errors";import { linkIncidentStatusReport } from "../incident/link-status-report";import type { StatusReport, StatusReportUpdate } from "../types";import { insertUpdateComponentImpacts, updatePageComponentAssociations, validatePageComponentIds, withComponentImpacts, withPageComponentIds,} from "./internal";import { CreateStatusReportInput } from "./schemas";
export type CreateStatusReportResult = { statusReport: StatusReport; initialUpdate: StatusReportUpdate;};
export async function createStatusReport(args: { ctx: ServiceContext; input: CreateStatusReportInput;}): Promise<CreateStatusReportResult> { const { ctx } = args; requireScope(ctx, "write"); const input = CreateStatusReportInput.parse(args.input);
return withTransaction(ctx, async (tx) => { const page_ = await tx .select({ id: page.id }) .from(page) .where( and(eq(page.id, input.pageId), eq(page.workspaceId, ctx.workspace.id)), ) .get(); if (!page_) throw new NotFoundError("page", input.pageId);
const componentImpacts = input.componentImpacts ?? []; // membership = union of the explicit set and the impact-named components const validated = await validatePageComponentIds({ tx, workspaceId: ctx.workspace.id, pageComponentIds: [ ...input.pageComponentIds, ...componentImpacts.map((ci) => ci.pageComponentId), ], });
if (validated.pageId !== null && validated.pageId !== input.pageId) { throw new ConflictError( `pageId ${input.pageId} does not match the page (${validated.pageId}) of the selected components.`, ); }
const newReport = await tx .insert(statusReport) .values({ workspaceId: ctx.workspace.id, pageId: input.pageId, title: input.title, status: input.status, }) .returning() .get();
await updatePageComponentAssociations({ tx, statusReportId: newReport.id, componentIds: validated.componentIds, });
const initialUpdate = await tx .insert(statusReportUpdate) .values({ statusReportId: newReport.id, status: input.status, date: input.date, message: input.message, }) .returning() .get();
await insertUpdateComponentImpacts({ tx, statusReportUpdateId: initialUpdate.id, componentImpacts, });
// membership + impacts live in join tables โ surface them in the // snapshots so the audit diff/UI sees them alongside the entity fields await emitAudit(tx, ctx, { action: "status_report.create", entityType: "status_report", entityId: newReport.id, after: withPageComponentIds(newReport, validated.componentIds), });
await emitAudit(tx, ctx, { action: "status_report_update.create", entityType: "status_report_update", entityId: initialUpdate.id, after: withComponentImpacts(initialUpdate, componentImpacts), metadata: { statusReportId: newReport.id }, });
if (input.incidentId !== undefined) { await linkIncidentStatusReport({ ctx: { ...ctx, db: tx }, input: { id: input.incidentId, statusReportId: newReport.id }, }); }
return { statusReport: newReport, initialUpdate }; });}