Files
Checkmate/CLAUDE.md
T
gorkem-bwl 944ee74a1d feat(status-page): four selectable themes for the public status page
Adds four fully-designed visual themes for the public status page
(refined | modern | bold | editorial) with a light/dark mode selector,
admin preview in a mock browser frame, and a shared theme provider
that keeps the component tree unified while letting each theme own
its visual identity.

## Selectable themes
- Refined — clean, flat cards, soft shadows, colored status dot.
- Modern — flat surfaces with a pulsing status dot and staggered
  fade-in cards.
- Bold — conic-gradient logo, 34px hero title with a check disc,
  subtle dual-radial glow on dark mode, accent-green hover border.
- Editorial — serif headings, narrow newspaper column, 'Service
  Report' eyebrow + title, flat rectangular badges and dateline.

## Architecture
- Single `StatusPageThemeProvider` hosts CSS custom-property tokens
  per theme and resolves light/dark based on admin-forced mode,
  falling back to `prefers-color-scheme` with a matchMedia listener.
- Each theme owns only its `*StatusPage.tsx` + scoped CSS file.
  The heatmap, histogram, and infrastructure renderers are shared
  across all four themes (parameterized by a `classPrefix`) to avoid
  the 12-file duplication the initial draft had.
- Renderer lookup table in `Status/index.tsx` replaces a 4-way
  ternary chain; unknown themes fall through to the legacy renderer.
- `resolveOverallStatus`, `statusBadgeKey`, `monoFirstChar`, and
  `monitorBadgeTone` are extracted to `themes/shared/overallStatus.tsx`.

## Admin UX
- `ThemePicker` uses the project's `RadioWithDescription` and
  `Select` components — one radio card per theme plus an Auto/
  Light-only/Dark-only mode dropdown.
- `BrowserFrame` wraps the preview in the admin panel with a mock
  chrome (traffic lights + URL bar) that adopts the status page's
  theme tokens. Edge-to-edge fill with no gray bleed.
- Public route renders the theme directly on the viewport with the
  document body painted to the theme background, reverted on unmount.

## Performance & polish
- Histogram's derived stats (padded array, max, avg, peak) are
  memoized on `checks`; `max` is reused for `peak` instead of
  computing twice.
- Editorial's dateline is wrapped in `useMemo` so the 10s poll
  doesn't rerender "April 23, 2026" as a changing string.
- Provider's `setSystemMode` reconciliation uses the updater form
  to avoid an extra render when the value is unchanged.
- `monoFirstChar` no longer uses a non-null assertion.

## Form plumbing
- `useStatusPageForm` defaults include `theme` + `themeMode`.
- `Validation/statusPage.ts` accepts both as optional zod enums.
- `Pages/StatusPage/Create/index.tsx` appends both to FormData on
  create and update.

## CLAUDE.md
- Dev port note: local client runs on 10001 (5173 is used by a
  different local project); server `CLIENT_HOST` is set to match.
2026-04-23 22:51:40 -04:00

8.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Checkmate is an open-source uptime and infrastructure monitoring application. It monitors server hardware, uptime, response times, and incidents with real-time alerts. The companion agent Capture provides infrastructure metrics (CPU, RAM, disk, temperature).

Development Commands

Client (React/Vite)

cd client
npm install
npm run dev -- --port 10001 --strictPort   # Local dev port is 10001 (5173 is used by another project on this machine)
npm run build            # TypeScript check + production build
npm run lint             # ESLint (strict, max-warnings 0)
npm run format           # Prettier formatting
npm run format-check     # Check formatting

Server .env on this machine is configured with CLIENT_HOST="http://localhost:10001" to match the client dev port. If you change the client port, keep .env in sync.

Server (Node.js/Express)

cd server
npm install
npm run dev              # Start with hot-reload (nodemon + tsx) at http://localhost:52345
npm run build            # TypeScript compile + path alias resolution
npm run test             # Run Mocha tests with c8 coverage
npm run lint             # ESLint v9
npm run lint-fix         # Auto-fix lint issues
npm run format           # Prettier formatting

Docker Development

cd docker/dev
./build_images.sh
docker run -d -p 27017:27017 -v uptime_mongo_data:/data/db --name uptime_database_mongo mongo:6.0

Environment Setup

Server .env (minimum required)

CLIENT_HOST="http://localhost:5173"
JWT_SECRET="my_secret_key_change_this"
DB_CONNECTION_STRING="mongodb://localhost:27017/uptime_db"
TOKEN_TTL="99d"
ORIGIN="localhost"
LOG_LEVEL="debug"

Client .env

VITE_APP_API_BASE_URL="http://localhost:52345/api/v1"
VITE_APP_LOG_LEVEL="debug"

Architecture

Monorepo Structure

  • /client - React 18 + TypeScript + Vite + MUI frontend
  • /server - Node.js 20+ + Express + TypeScript backend
  • /docker - Multi-environment Docker configs (dev, staging, prod, arm, mono)

Backend Layers

server/src/
├── controllers/     # Route handlers (authController, monitorController, etc.)
├── service/         # Business logic
│   ├── business/    # Core monitoring logic
│   ├── infrastructure/  # Server/system utilities
│   └── system/      # App-level settings
├── db/
│   ├── models/      # Mongoose schemas (Monitor, Check, Incident, User, etc.)
│   ├── migration/   # Database migrations (run on startup)
│   └── modules/     # Database-specific modules
├── middleware/v1/   # verifyJWT, rateLimiter, sanitization, responseHandler
├── routes/v1/       # API route definitions
├── validation/      # Joi input validation schemas
└── repositories/    # Data access layer

Frontend Structure

client/src/
├── Components/      # Reusable UI components
├── Pages/           # Page components (Auth, Uptime, Infrastructure, Incidents, etc.)
├── Features/        # Redux slices (Auth, UI)
├── Hooks/           # Custom React hooks
├── Utils/           # Utilities (NetworkService.js is main API client)
├── Validation/      # Input validation
└── locales/         # i18n translations

API

  • Base URL: /api/v1
  • Documentation: http://localhost:52345/api-docs (Swagger UI)
  • OpenAPI spec: /server/openapi.json

Key Technologies

  • State Management: Redux Toolkit + Redux-Persist
  • Data Fetching: SWR + Axios
  • Database: MongoDB with Mongoose ODM
  • Queue/Cache: Redis + BullMQ + Pulse (cron scheduling)
  • i18n: i18next + react-i18next (translations via PoEditor)

Backend Architecture Patterns

Repository Pattern & Separation of Concerns

The backend enforces a strict three-layer separation between HTTP handling, business logic, and data access:

Request → Controller → Service → Repository → MongoDB (Mongoose)
  • Controllers (/controllers) handle HTTP concerns only: parsing request params, calling the appropriate service, and returning a response via the responseHandler middleware. They contain no business logic.
  • Services (/service/business) contain all business logic: deciding whether an incident should be created, whether a notification should fire, what state a monitor is in, etc.
  • Repositories (/repositories) are the sole layer that talks to MongoDB through Mongoose. They expose clean, reusable query methods (e.g. findByMonitorId, createCheck) so that services never construct raw DB queries directly.

This separation makes each layer independently testable and keeps Mongoose-specific code out of business logic. When adding a new feature, the pattern to follow is: add a repository method for any new DB query, call it from a service, and expose it via a controller route.

Monitoring Flow: From Check to Notification

Background monitoring runs on a scheduled queue, not on the HTTP request cycle. The high-level flow for uptime monitoring is:

Pulse (cron) → BullMQ Job → StatusService
                                 ├── performs HTTP/port/ping check
                                 ├── saves Check via CheckRepository
                                 ├── evaluates monitor state change
                                 │     └── calls IncidentService (create / resolve incident)
                                 └── calls NotificationService (email, Slack, Discord, webhook)
  1. Pulse (cron scheduler) enqueues a job into a BullMQ queue for each active monitor at its configured interval.
  2. A BullMQ worker picks up the job and calls StatusService, which performs the actual check (HTTP request, TCP port probe, ping, etc.).
  3. The result is persisted as a Check document via the repository layer.
  4. StatusService compares the new result against the monitor's previous state. If the monitor transitions from up → down (or down → up), it delegates to IncidentService to open or resolve an Incident document.
  5. On a state change, NotificationService reads the monitor's configured Notification documents and dispatches alerts to all enabled channels (email, Discord, Slack, webhooks).

Queue System (BullMQ + Redis)

Redis serves two roles: job queue storage for BullMQ and ephemeral caching. BullMQ manages concurrency, retries, and backpressure for monitoring jobs, ensuring checks are processed reliably even under load.

  • Each monitor type (HTTP, port, ping, infrastructure) maps to its own queue worker so failures in one type don't block others.
  • Job scheduling interval is driven by the interval field on the Monitor model.
  • Failed jobs are retried with configurable backoff before being moved to a dead-letter state.
  • Redis is also used to cache frequently read data (e.g. aggregated stats) to reduce MongoDB query pressure.

When working on anything related to check scheduling, incident lifecycle, or notifications, trace the flow starting from the relevant BullMQ worker rather than from the controller layer.


Code Conventions

Internationalization

All user-facing strings must use the translation function:

t('your.key')  // Never hardcode UI strings

Branching

  • Always branch from develop (not master)
  • Use descriptive names: feat/add-alerts, fix/login-error
  • PRs target develop branch

Formatting

  • Client: Prettier with printWidth: 90, tabs, double quotes
  • Server: Prettier with printWidth: 150, tabs, double quotes
  • Both use ESLint with strict settings

Testing

Server tests use Mocha + Chai + Sinon:

npm test                    # Run all tests with coverage
npm test -- --grep "pattern"  # Run specific tests

Test files: server/tests/**/*.test.js

Database Models

Key Mongoose models in /server/src/db/models/:

  • Monitor - Monitoring configuration (website, infrastructure, port, etc.)
  • Check - Individual monitoring check results
  • Incident - Downtime incidents
  • User - User accounts
  • Team - Team/workspace management
  • StatusPage - Public status pages
  • Notification - Alert configuration (email, Discord, Slack, webhooks)
  • MaintenanceWindow - Scheduled maintenance periods
  • AppSettings - Global application settings