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.
7.8 kB ยท 234 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235import { and, asc, db as defaultDb, eq, or, sql } from "@openstatus/db";import type { ApiConfigType, ExternalService } from "@openstatus/db/src/schema";import { externalServiceComponent } from "@openstatus/db/src/schema";
import { defaultTb } from "../context";import { getExternalServiceBySlug } from "../external-service";import type { GlobalReadContext } from "../external-service/internal";import { retryRead } from "../retry";
export type ExternalComponentListItem = { id: number; externalServiceId: number; upstreamComponentId: string; slug: string; name: string; description: string | null; groupName: string | null; position: number; indicator: string; status: string; firstSeenAt: Date; lastFetchedAt: number | null;};
// A component absent from upstream for longer than this is hidden from the page// without being deleted (ADR-0007): the next tick that re-sees it brings it back.const COMPONENT_STALE_MS = 24 * 60 * 60 * 1000;
const TB_BATCH_SIZE = 100;
// must stay in sync with fetchers that implement `fetchComponents` in @openstatus/status-fetcherexport const COMPONENT_SUPPORTED_API_CONFIG_TYPES = new Set<ApiConfigType>([ "atlassian", "incidentio",]);
export function supportsComponents(type: ApiConfigType | undefined): boolean { return type !== undefined && COMPONENT_SUPPORTED_API_CONFIG_TYPES.has(type);}
type LatestFetchedResult = { errored: boolean; byId: Map<string, number>;};
async function fetchLatestByIds(ids: string[]): Promise<LatestFetchedResult> { if (ids.length === 0) return { errored: false, byId: new Map() }; // Without a Tinybird token, `defaultTb` is a NoopTinybird that returns empty // data โ which would mark every component as stale and hide the page. // Treat the absence of credentials as "TB unavailable" so the page renders // (degrade open) in tests, local dev, and misconfigured prod environments. if (!process.env.TINY_BIRD_API_KEY) { return { errored: true, byId: new Map() }; } const byId = new Map<string, number>(); try { for (let i = 0; i < ids.length; i += TB_BATCH_SIZE) { const chunk = ids.slice(i, i + TB_BATCH_SIZE); const { data } = await defaultTb.externalStatusComponentLatest({ component_ids: chunk, }); for (const row of data) { byId.set(row.component_id, row.last_fetched_at); } } return { errored: false, byId }; } catch (err) { console.warn( "[external-service components] TB latest-fetched lookup failed:", err, ); return { errored: true, byId: new Map() }; }}
export async function listExternalComponentsByServiceId(args: { ctx?: GlobalReadContext; externalServiceId: number; now?: Date;}): Promise<ExternalComponentListItem[]> { const { ctx, externalServiceId } = args; const db = ctx?.db ?? defaultDb;
const rows = await retryRead(() => db .select({ id: externalServiceComponent.id, externalServiceId: externalServiceComponent.externalServiceId, upstreamComponentId: externalServiceComponent.upstreamComponentId, slug: externalServiceComponent.slug, name: externalServiceComponent.name, description: externalServiceComponent.description, groupName: externalServiceComponent.groupName, position: externalServiceComponent.position, indicator: externalServiceComponent.indicator, status: externalServiceComponent.status, firstSeenAt: externalServiceComponent.firstSeenAt, }) .from(externalServiceComponent) .where(eq(externalServiceComponent.externalServiceId, externalServiceId)) .orderBy( asc(externalServiceComponent.position), asc(externalServiceComponent.name), ) .all(), );
if (rows.length === 0) return [];
const { errored, byId } = await fetchLatestByIds( rows.map((r) => String(r.id)), );
const cutoff = (args.now ?? new Date()).getTime() - COMPONENT_STALE_MS;
const out: ExternalComponentListItem[] = []; for (const row of rows) { const lastFetchedAt = byId.get(String(row.id)) ?? null; if (!errored && (lastFetchedAt === null || lastFetchedAt < cutoff)) { continue; } out.push({ ...row, lastFetchedAt }); } return out;}
export type ListComponentsBySlugResult = { service: { id: number; apiConfigType?: ApiConfigType } | null; supported: boolean; components: ExternalComponentListItem[];};
export async function listExternalComponentsBySlug(args: { ctx?: GlobalReadContext; slug: string; now?: Date;}): Promise<ListComponentsBySlugResult> { const { ctx, slug } = args; const service = await getExternalServiceBySlug({ ctx, slug }); if (!service) { return { service: null, supported: false, components: [] }; }
const apiConfigType = service.apiConfig?.type; if (!supportsComponents(apiConfigType)) { return { service: { id: service.id, apiConfigType }, supported: false, components: [], }; }
const components = await listExternalComponentsByServiceId({ ctx, externalServiceId: service.id, now: args.now, });
return { service: { id: service.id, apiConfigType }, supported: true, components, };}
export type ExternalComponentDetail = ExternalComponentListItem & { aliases: string[]; stale: boolean;};
export type GetComponentBySlugResult = { service: ExternalService | null; component: ExternalComponentDetail | null;};
// Resolves a component within a service by its current slug OR a past alias,// WITHOUT the staleness filter โ old/renamed URLs still resolve (the page renders// last-known + noindex when `stale`). Returns the service so the page can do the// canonical-slug 308 checks for both segments.export async function getExternalComponentBySlug(args: { ctx?: GlobalReadContext; serviceSlug: string; componentSlug: string; now?: Date;}): Promise<GetComponentBySlugResult> { const { ctx, serviceSlug, componentSlug } = args; const db = ctx?.db ?? defaultDb;
const service = await getExternalServiceBySlug({ ctx, slug: serviceSlug }); if (!service) return { service: null, component: null };
const rows = await retryRead(() => db .select({ id: externalServiceComponent.id, externalServiceId: externalServiceComponent.externalServiceId, upstreamComponentId: externalServiceComponent.upstreamComponentId, slug: externalServiceComponent.slug, aliases: externalServiceComponent.aliases, name: externalServiceComponent.name, description: externalServiceComponent.description, groupName: externalServiceComponent.groupName, position: externalServiceComponent.position, indicator: externalServiceComponent.indicator, status: externalServiceComponent.status, firstSeenAt: externalServiceComponent.firstSeenAt, }) .from(externalServiceComponent) .where( and( eq(externalServiceComponent.externalServiceId, service.id), or( eq(externalServiceComponent.slug, componentSlug), sql`EXISTS (SELECT 1 FROM json_each(${externalServiceComponent.aliases}) WHERE value = ${componentSlug})`, ), ), ) .limit(1) .all(), );
const row = rows[0]; if (!row) return { service, component: null };
const { errored, byId } = await fetchLatestByIds([String(row.id)]); const lastFetchedAt = byId.get(String(row.id)) ?? null; const cutoff = (args.now ?? new Date()).getTime() - COMPONENT_STALE_MS; const stale = !errored && (lastFetchedAt === null || lastFetchedAt < cutoff);
return { service, component: { ...row, lastFetchedAt, stale }, };}