Skip to content

Latest commit

 

History

History
188 lines (151 loc) · 9.3 KB

File metadata and controls

188 lines (151 loc) · 9.3 KB

Tooling

  • Stack: SvelteKit 2, Svelte 5, TypeScript, Tailwind CSS 4
  • Package manager: bun
  • Test command: bun test (bun:test)
  • Lint command: bun lint
  • Format command: bun format
  • Check command: bun check
  • PWA support: @vite-pwa/sveltekit

Build & Deploy

  • Build: bun run build (SvelteKit + custom worker bundling via esbuild)
  • Deploy: bun run deploy (applies D1 migrations + wrangler deploy)
  • Output directory: dist/ (static assets + _worker.js)
  • Cron: Integrated scheduled() handler, runs every minute
  • Deploy button: One-click deployment from GitHub supported

Project Context

  • Name: pingflare - Cloudflare-native uptime monitoring solution
  • Goal: Monitor ~30 services on Hetzner dedicated server from external Cloudflare infrastructure
  • Inspiration: Uptime Kuma but serverless on Cloudflare ecosystem
  • Deployed at: https://pingflare.pages.dev
  • Current state: MVP complete with HTTP/Script monitoring, dashboard, authentication

Cloudflare Resources

  • D1 Database: pingflare-db (binding: DB)
  • Workers Project: pingflare (with Static Assets)
  • ASSETS binding: Fetcher for static file serving

Directory Structure

  • src/lib/components/ - Svelte components (MonitorCard, MonitorForm, ScriptBuilder, ScriptEditor, NotificationChannelCard, NotificationChannelForm, etc.)
  • src/lib/components/ui/ - UI primitives (Button, Input, Select, Textarea, Card, Alert, Badge, Spinner, IconButton)
  • src/lib/components/layout/ - Layout components (AppShell, Header, BottomNav, PageHeader, Container)
  • src/lib/utils/ - Utility functions (format.ts)
  • src/lib/server/checkers/ - Health check implementations (script.ts executor, index.ts orchestrator)
  • src/lib/server/db/ - Database operations (monitors.ts, auth.ts, notifications.ts)
  • src/lib/server/notifications/ - Notification senders (slack.ts, discord.ts, webhook.ts, webpush.ts, index.ts)
  • src/lib/types/ - TypeScript interfaces (monitor.ts, auth.ts, script.ts, notification.ts)
  • src/routes/ - SvelteKit pages (dashboard, /login, /setup, /settings, /monitors/new, /monitors/[id]/edit, /notifications)
  • src/routes/api/ - REST API endpoints
  • src/hooks.server.ts - Auth middleware, route protection
  • src/worker.ts - Custom worker entry (not used directly, template for build)
  • scripts/build-worker.js - Post-build worker bundling script
  • migrations/ - D1 SQL migration (single consolidated file: 0001_schema.sql)

Database Schema

  • monitor_groups: id, name, slug, description, is_public, display_order, created_at
  • monitors: id, name, group_id (FK), interval_seconds, timeout_ms, active, script (JSON DSL), created_at, updated_at
  • checks: id, monitor_id (FK), status, response_time_ms, status_code, error_message, checked_at, checked_from
  • incidents: id, title, status, group_id (FK), created_at, resolved_at
  • incident_updates: id, incident_id (FK), status, message, created_at
  • daily_status: monitor_id, date, total_checks, up_checks, down_checks, degraded_checks, downtime_minutes
  • notification_channels: id, type (webhook/slack/discord/webpush), name, config (JSON), active, created_at
  • monitor_notifications: monitor_id, channel_id, notify_on (CSV), downtime_threshold_s (junction table)
  • push_subscriptions: id, endpoint (unique), p256dh, auth, user_agent, created_at
  • users: id, name, email (unique), password_hash, role, created_at, updated_at, last_login_at
  • sessions: id, user_id (FK), expires_at, created_at
  • app_settings: key, value, updated_at

API Endpoints

  • GET/POST /api/monitors - List/create monitors
  • GET/PUT/DELETE /api/monitors/[id] - Single monitor CRUD
  • GET /api/cron - Trigger health checks (manual or cron)
  • GET /api/status - Status endpoint (public, queries D1 directly)
  • GET/POST /api/groups - List/create monitor groups
  • GET/PUT/DELETE /api/groups/[id] - Single group CRUD
  • GET/POST /api/incidents - List/create incidents (requires group_id)
  • GET/POST/DELETE /api/incidents/[id] - Single incident operations
  • GET /api/auth/status - Check setup state and current user
  • POST /api/auth/setup - Create initial admin (name, email, password)
  • POST /api/auth/login - Email/password authentication
  • POST /api/auth/logout - Delete session
  • PUT /api/auth/profile - Update user name
  • PUT /api/auth/password - Change password
  • GET/POST /api/notification-channels - List/create notification channels
  • GET/PUT/DELETE /api/notification-channels/[id] - Single channel CRUD
  • POST /api/notification-channels/[id]/test - Send test notification
  • GET /api/push/vapid-key - Get VAPID public key for browser subscription
  • POST/DELETE /api/push/subscribe - Manage push subscriptions
  • GET /api/monitors/[id]/notifications - Get monitor's notification subscriptions

Authentication

  • Session-based auth with cookies (7-day expiry)
  • SHA-256 password hashing via Web Crypto API
  • User fields: name, email (login), password
  • Role-based permissions: admin/editor/viewer (defined in src/lib/types/auth.ts)
  • First visit redirects to /setup, subsequent visits to /login
  • Settings page at /settings for profile and password management
  • API auth pattern: each handler checks if (!locals.user) return json({ error: 'Unauthorized' }, { status: 401 })
  • Public API: /api/public/, /api/auth/
  • Protected API: all other endpoints require authenticated session
  • /api/cron: requires CRON_SECRET env var, validates X-Cron-Secret header (internal scheduled handler only)
  • /api/status: requires auth (returns all monitors, use /api/public/status for public data)
  • CRON_SECRET: required secret for cron, set via wrangler secret put CRON_SECRET

Monitor Types

  • All monitors use JSON DSL script format (no separate http/tcp types)
  • tcp/dns: Planned (schema exists)
  • push: Passive monitors (different flow)

Script Checker (JSON DSL)

  • All monitors use JSON-based DSL for health checks
  • UI: Visual Builder (ScriptBuilder.svelte) or Code mode (ScriptEditor.svelte) with toggle
  • Supports chained requests: GET, POST, PUT, DELETE, PATCH, HEAD
  • Variable extraction: "extract": { "token": "json.access_token" }
  • Variable interpolation: ${varName} in URLs, headers, body
  • Assertions: equals, notEquals, contains, notContains, matches (regex), greaterThan, lessThan, greaterOrEqual, lessOrEqual, minLength, maxLength, hasLength, hasKey, exists
  • Assertion severity: each assertion has optional "severity": "degraded" | "down" (default: degraded)
  • Path notation: json.user.name, json.items[0].id, status, responseTime, body, headers.content-type
  • responseTime: per-step response time in ms, use for performance assertions (e.g., { "check": "responseTime", "lessThan": 500 })
  • Status determination: request fails = down, assertion with severity=down fails = down, assertion with severity=degraded fails = degraded, all pass = up
  • Validation function: validateScript() in src/lib/server/checkers/script.ts
  • Shared types: src/lib/types/script.ts (ScriptDSL, ScriptStep, Assertion, AssertionSeverity, HttpMethod)

Scheduler (Integrated)

  • Cron handler integrated in main Worker (scheduled() export)
  • Triggers /api/cron internally every minute via cron trigger
  • Configured in wrangler.toml [triggers] section
  • No separate deployment needed

Svelte 5 Patterns

  • $state() for reactive state variables
  • $derived() for computed values (e.g., upCount, downCount)
  • $effect() for side effects (auto-refresh, cleanup)
  • $props() for component props with destructuring
  • Snippet type for component children (Modal)
  • @render for rendering snippets

Coding Patterns

  • Checker orchestrator: src/lib/server/checkers/index.ts routes by monitor type
  • Dependency injection: db passed to all functions (testable)
  • Promise.allSettled() for parallel checks (tolerates individual failures)
  • DTO pattern: userToPublic() removes password_hash
  • Dynamic SQL in updateMonitor() for partial updates

Future Cloudflare Products

  • Durable Objects: Real-time WebSocket for live updates (Phase 2)
  • Queues: Async notification processing (Phase 2)

Architectural Constraints

  • Cron triggers max 25 monitors due to 50 subrequest limit per invocation
  • Cloudflare Workers blocks eval/new Function (script checker uses JSON DSL, not JS eval)
  • Workers can make outbound TCP connections via connect() API from cloudflare:sockets
  • Free tier: 100k requests/day, 10ms CPU/request

UI Component Library

  • Button: variants (primary/secondary/danger/ghost), sizes (sm/md/lg), loading state, href support
  • Input: types (text/email/password/number/url), label, error, helper text
  • Select: dropdown with label and error
  • Textarea: with label, error, monospace option
  • Card: container with optional header/footer snippets, padding sizes
  • Alert: variants (error/success/warning/info), dismissible
  • Badge: status indicators, maps MonitorStatus to colors
  • Spinner: loading indicator, sizes (sm/md/lg)
  • IconButton: icons (edit/delete/add/chevron-up/chevron-down/close/back/menu/check/refresh)

Layout Components

  • AppShell: combines Header (desktop) + content + BottomNav (mobile)
  • Header: desktop header with logo, nav links, user menu, logout
  • BottomNav: fixed bottom nav for mobile with 4 items (Dashboard/Add/Alerts/Settings)
  • PageHeader: title, optional subtitle, back button, actions snippet
  • Container: max-width wrapper with sizes (sm/md/lg/xl/full)

Not Yet Implemented

  • Email and Telegram notification channels
  • TCP/DNS checkers (scaffolded)
  • Downtime threshold enforcement per channel (schema ready, cron not fully integrated)

User Preferences

  • Never include "Claude" or "Claude Code" or co author tags in any git commit messages.