Monitor websites uptime using Cloudflare Workers
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144import { createApiRouter, type AppDependencies } from '../app.js';import type { ApiConfig } from '../env.js';import { UrlPolicyError } from '../security.js';import { isPostgresUniqueViolation, type Database } from '@uptime/cloudflare/postgres';
import type { CloudflareEnv } from '@uptime/cloudflare';
/** * Test harness that drives the Worker's router directly with the PostgreSQL * `Database` adapter, so tests exercise the real migration and queries. */
export interface TestHarnessOptions extends AppDependencies { db: Database; config?: Partial<ApiConfig>;}
export interface TestApp { fetch(request: Request): Promise<Response>; config: ApiConfig;}
export const defaultTestConfig: ApiConfig = { adminEmail: 'admin@example.com', adminPasswordHash: '', sessionSecret: 'a'.repeat(32), sessionTtlSeconds: 604_800, sessionCookieSecure: false, sessionCookieSameSite: 'strict', enabledRegionIds: [ 'us-east', 'us-west', 'canada-central', 'eu-west', 'eu-north', 'eu-south', 'asia', 'asia-east', 'asia-south', ], enabledRegions: [], allowedOrigins: [], credentialEncryptionSecret: 'c'.repeat(32), rateLimitDisabled: true, environment: 'test',};
export function buildTestApp(options: TestHarnessOptions): TestApp { const merged = { ...defaultTestConfig, ...options.config }; const config: ApiConfig = { ...merged, enabledRegions: merged.enabledRegionIds.map((id) => ({ id, label: id, continentId: 'north-america', placementRegion: 'aws:us-east-1', approximateAnchor: id, workerName: `uptime-probe-${id}`, wranglerConfigBasename: `wrangler.${id}.toml`, chartSeriesToken: '--region-series-1', })) as ApiConfig['enabledRegions'], }; const router = createApiRouter(options); const env = { config, db: options.db, now: options.now ?? (() => new Date()), log: options.log ?? { warn: () => undefined }, }; return { config, async fetch(request: Request): Promise<Response> { const url = new URL(request.url); const method = request.method.toUpperCase() as | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'; const matched = router.match(method, url.pathname); if (!matched) { if (router.hasPath(url.pathname)) { return errorJson(405, 'method_not_allowed', 'Method not allowed'); } return errorJson(404, 'not_found', 'Route was not found'); } try { return await matched.handler({ request, env, params: matched.params, url }); } catch (error) { return errorToResponse(error); } }, };}
function errorJson( status: number, code: string, message: string, fieldErrors?: Record<string, string[]>,): Response { return new Response( JSON.stringify({ error: { code, message, ...(fieldErrors ? { fieldErrors } : {}) } }), { status, headers: { 'content-type': 'application/json' } }, );}
function errorToResponse(error: unknown): Response { const candidate = error as { status?: number; code?: string; message?: string; fieldErrors?: Record<string, string[]>; issues?: { path: (string | number)[]; message: string }[]; }; if (typeof candidate.status === 'number' && candidate.code) { return errorJson( candidate.status, candidate.code, candidate.message ?? 'Error', candidate.fieldErrors, ); } if (Array.isArray(candidate.issues)) { const fieldErrors: Record<string, string[]> = {}; for (const issue of candidate.issues) { const key = issue.path.join('.') || 'body'; (fieldErrors[key] ??= []).push(issue.message); } return errorJson(400, 'validation_error', 'Request validation failed', fieldErrors); } if (error instanceof UrlPolicyError) { return errorJson(400, error.code, error.message); } if (isPostgresUniqueViolation(error)) { return errorJson(409, 'slug_conflict', 'A unique constraint was violated'); } console.error(error); return errorJson(500, 'internal_error', 'Unexpected server error');}
export type { CloudflareEnv };