# Cloudflare Domain Manager Self-hosted service for managing domains, DNS records, and SSL certificates via Cloudflare API. ## Features - Domain and DNS record management (hybrid sync with Cloudflare) - Domain groups (local / vpn / external) and service tags - Service groups with common FQDN and DNS-based load balancing (Round Robin / Failover / Weighted) - Health checks (TCP / HTTP) with automatic DNS reconciliation on status change - SSL certificate expiry monitoring - React UI with TanStack Router & Query - Single Docker container deployment ## Quick start ```bash cp .env.example .env # Edit CLOUDFLARE_API_TOKEN, JWT_SECRET docker compose up -d ``` Open http://localhost:8080 — default login `admin` / `admin` (dev only). ## Stack - **Backend:** Node.js 22, Fastify 5, Drizzle ORM, SQLite (`apps/api`) - **Shared:** Zod schemas (`packages/shared`), DB layer (`packages/db`) - **Frontend:** pnpm monorepo (`apps/web` + `packages/ui`), React, Vite, TanStack Router/Query/Table, shadcn/ui (base-nova), Recharts - **CI:** Gitea Actions (Gitflow) ## Development ```bash pnpm install pnpm --filter @cfdm/shared build pnpm --filter @cfdm/db build # API + SQLite (:8080) pnpm --filter @cfdm/api dev # Frontend (:5173, proxies /api → :8080) pnpm --filter web dev ``` ```bash pnpm turbo build pnpm turbo test ``` shadcn CLI: `cd apps/web && pnpm dlx shadcn@latest add ` ## Documentation See [docs/Home.md](docs/Home.md) and [CONTRIBUTING.md](CONTRIBUTING.md). ## Load balancing & health checks Группа сервисов может иметь общий домен (`service_groups.domain`). На общем домене и на привязках сервиса с несколькими A-записями включается балансировка и health-check: - **Режимы LB:** `round_robin` (по одной A на каждый up-IP), `failover` (A только для up-IP с минимальным приоритетом; бэкапы подключаются при падении primary), `weighted` (веса учитываются в БД; в Cloudflare free отображается как Round Robin, т.к. CF API не допускает дубликаты `(name, type, content)` A-записей — для истинного weighted нужен CF Load Balancer). - **Health-check:** TCP connect или HTTP (настраиваемый порт, путь, ожидаемый статус, интервал, таймаут). Результаты хранятся в `ip_health_status` (`up` / `degraded` / `down` / `unknown`) и опрашиваются UI с polling 10с. - **Reconcile:** при смене статуса IP cron перезаписывает A-записи в Cloudflare, оставляя только активные по политике LB. Реакция = TTL A-записи (`ttl=1` proxied — минимальный). Env для health-check: | Variable | Default | Description | |----------|---------|-------------| | `HEALTH_CHECK_CRON` | `*/30 * * * * *` | Cron выражение для запуска проверок (каждые 30с) | | `HEALTH_DEGRADED_FAILURES` | `1` | Порог последовательных ошибок → статус `degraded` | | `HEALTH_DOWN_FAILURES` | `2` | Порог последовательных ошибок → статус `down` (IP убирается из DNS) | | `HEALTH_LATENCY_WARN_MS` | `1000` | Латентность выше порога → `degraded` даже при успешном connect |