Absorbs the separate control-panel git repo as a subdirectory. Instances and backups directories excluded via .gitignore. Bunker Admin
606 lines
27 KiB
Markdown
606 lines
27 KiB
Markdown
# Changemaker Control Panel (CCP) — Development Plan
|
|
|
|
A multi-tenant management system for provisioning, monitoring, and operating multiple Changemaker Lite instances from a single dashboard.
|
|
|
|
---
|
|
|
|
## Vision
|
|
|
|
CCP replaces manual `git clone` / `docker compose up` / `.env` editing with a web UI that can:
|
|
- One-click provision a new Changemaker Lite instance (database, containers, config, tunnel)
|
|
- Monitor instance health across the fleet
|
|
- Start/stop/restart instances and individual services
|
|
- Back up and restore instance data
|
|
- Maintain a full audit trail of operator actions
|
|
- Manage Pangolin tunnels for production exposure
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────┐
|
|
│ CCP Admin GUI (5100) │ React + Vite + Ant Design
|
|
│ Dark theme, SPA │ Zustand auth store
|
|
└────────────┬─────────────┘
|
|
│ /api/* proxy
|
|
┌────────────▼─────────────┐
|
|
│ CCP API (5000) │ Express + TypeScript
|
|
│ JWT auth, RBAC │ Prisma ORM → PostgreSQL
|
|
│ Docker socket access │ Winston logger
|
|
└────────────┬─────────────┘
|
|
│
|
|
┌───────────┬───────────┼───────────┬──────────┐
|
|
▼ ▼ ▼ ▼ ▼
|
|
ccp-postgres ccp-redis Docker Socket /opt/ccp/ /var/backups/
|
|
(port 5480) (port 6399) (.sock) instances ccp-instances
|
|
```
|
|
|
|
### Stack
|
|
|
|
| Layer | Technology | Notes |
|
|
|-------|-----------|-------|
|
|
| API | Express 4, TypeScript 5, Node 20 | `express-async-errors` for async route handling |
|
|
| ORM | Prisma 6 + PostgreSQL 16 | 10 models, mapped table names |
|
|
| Auth | JWT (jsonwebtoken) + bcryptjs | 15min access / 7d refresh, atomic rotation |
|
|
| Encryption | AES-256-GCM (Node crypto) | Secrets at rest in `encrypted_secrets` column |
|
|
| Frontend | React 19, Vite 6, Ant Design 5 | Dark theme, Zustand state, axios interceptors |
|
|
| Docker | Docker CLI + socket API | compose up/down/ps/exec/logs via shell + HTTP socket |
|
|
| Templates | Handlebars | .env, docker-compose.yml, nginx configs rendered per-instance |
|
|
| Logging | Winston | JSON in production, colorized in development |
|
|
| Config | Zod schema validation | Fails fast on startup with clear error messages |
|
|
|
|
### Key Design Decisions
|
|
|
|
1. **Docker CLI over Docker SDK** — Shell out to `docker compose` commands rather than using dockerode. Simpler, matches what operators would run manually, and `docker compose` handles all the orchestration logic.
|
|
|
|
2. **Shared PostgreSQL** — All CCP data in one database; each CML instance gets its own isolated PostgreSQL container with unique ports and passwords.
|
|
|
|
3. **Port Range Allocation** — Four non-overlapping port ranges prevent conflicts:
|
|
- API: 14000-14999
|
|
- Admin: 13000-13999
|
|
- PostgreSQL: 15400-15499
|
|
- Nginx: 10000-10999
|
|
|
|
4. **Async Provisioning** — Instance creation returns immediately; provisioning runs fire-and-forget with progress tracked via `status`/`statusMessage` fields. Frontend polls for updates.
|
|
|
|
5. **Template-Based Config** — Handlebars templates render per-instance docker-compose.yml, .env, nginx configs, and Pangolin resources. This avoids complex string manipulation and keeps configs readable.
|
|
|
|
---
|
|
|
|
## Database Schema (Prisma)
|
|
|
|
```
|
|
CcpUser ──< CcpRefreshToken
|
|
│
|
|
├──< AuditLog
|
|
│
|
|
Instance ──< PortAllocation
|
|
│
|
|
├──< HealthCheck
|
|
├──< Backup
|
|
└──< AuditLog
|
|
|
|
CcpSetting (key-value)
|
|
```
|
|
|
|
### Models
|
|
|
|
| Model | Purpose | Key Fields |
|
|
|-------|---------|------------|
|
|
| **CcpUser** | Control panel operators | email, password (bcrypt), role (SUPER_ADMIN/OPERATOR/VIEWER) |
|
|
| **CcpRefreshToken** | JWT refresh token storage | token (SHA-256 hash), expiresAt |
|
|
| **Instance** | Managed CML instance | slug, domain, basePath, composeProject, status, portConfig (JSON), encryptedSecrets, feature flags |
|
|
| **PortAllocation** | Port registry | port (unique), service name, instanceId |
|
|
| **HealthCheck** | Periodic health snapshots | status (HEALTHY/DEGRADED/UNHEALTHY/UNKNOWN), serviceStatus (JSON), totalServices, healthyServices, responseTimeMs |
|
|
| **Backup** | Backup records | status (PENDING→IN_PROGRESS→COMPLETED/FAILED), archivePath, sizeBytes, manifest (JSON) |
|
|
| **AuditLog** | Action trail | action (18 types), userId, instanceId, details (JSON), ipAddress |
|
|
| **CcpSetting** | Global key-value config | key (PK), value (JSON) |
|
|
|
|
### Audit Actions (18 types)
|
|
|
|
```
|
|
INSTANCE_CREATE, INSTANCE_UPDATE, INSTANCE_DELETE,
|
|
INSTANCE_START, INSTANCE_STOP, INSTANCE_RESTART, INSTANCE_UPGRADE,
|
|
BACKUP_CREATE, BACKUP_DELETE,
|
|
PANGOLIN_SETUP, PANGOLIN_SYNC,
|
|
USER_LOGIN, USER_CREATE, USER_UPDATE, USER_DELETE,
|
|
SETTINGS_UPDATE
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Authentication
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| POST | `/api/auth/login` | Public | Login, returns JWT pair |
|
|
| POST | `/api/auth/refresh` | Public | Rotate refresh token |
|
|
| POST | `/api/auth/logout` | Public | Revoke refresh token |
|
|
| GET | `/api/auth/me` | Authenticated | Current user profile |
|
|
|
|
### Instances — CRUD
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/instances` | Authenticated | List all instances |
|
|
| GET | `/api/instances/:id` | Authenticated | Instance detail (includes health + backups) |
|
|
| POST | `/api/instances` | SUPER_ADMIN, OPERATOR | Create instance (triggers async provisioning) |
|
|
| PUT | `/api/instances/:id` | SUPER_ADMIN, OPERATOR | Update instance config |
|
|
| DELETE | `/api/instances/:id` | SUPER_ADMIN | Delete instance (stops containers, removes files) |
|
|
| GET | `/api/instances/:id/secrets` | SUPER_ADMIN | Decrypt and return instance secrets |
|
|
|
|
### Instances — Lifecycle
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| POST | `/api/instances/:id/provision` | SUPER_ADMIN, OPERATOR | Retry provisioning |
|
|
| POST | `/api/instances/:id/start` | SUPER_ADMIN, OPERATOR | Start all containers |
|
|
| POST | `/api/instances/:id/stop` | SUPER_ADMIN, OPERATOR | Stop all containers |
|
|
| POST | `/api/instances/:id/restart` | SUPER_ADMIN, OPERATOR | Restart (optionally `?service=api`) |
|
|
|
|
### Instances — Services & Logs
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/instances/:id/services` | Authenticated | Container status via `docker compose ps` |
|
|
| GET | `/api/instances/:id/logs` | Authenticated | Logs (`?service=api&tail=200&since=1h`) |
|
|
|
|
### Instances — Health
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| POST | `/api/instances/:id/health-check` | SUPER_ADMIN, OPERATOR | Trigger manual health check |
|
|
| GET | `/api/instances/:id/health-history` | Authenticated | Paginated health check history |
|
|
|
|
### Instances — Backups
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| POST | `/api/instances/:id/backup` | SUPER_ADMIN, OPERATOR | Create backup (async) |
|
|
| GET | `/api/instances/:id/backups` | Authenticated | List instance backups |
|
|
|
|
### Backups (cross-instance)
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/backups` | Authenticated | List all backups (`?instanceId=...&page=1&limit=50`) |
|
|
| DELETE | `/api/backups/:id` | SUPER_ADMIN | Delete backup (file + record) |
|
|
| GET | `/api/backups/:id/download` | SUPER_ADMIN | Stream backup archive |
|
|
|
|
### Health (CCP-level)
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/health` | Public | CCP API health check |
|
|
| GET | `/api/health/overview` | Authenticated | All instances with latest health |
|
|
|
|
### Audit
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/audit` | Authenticated | Filtered, paginated audit log (`?action=...&instanceId=...&userId=...&from=...&to=...`) |
|
|
|
|
### Settings
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| GET | `/api/settings` | Authenticated | All settings as key-value map |
|
|
| PUT | `/api/settings/:key` | SUPER_ADMIN | Upsert a setting value |
|
|
|
|
---
|
|
|
|
## Frontend Pages
|
|
|
|
| Route | Page | Description |
|
|
|-------|------|-------------|
|
|
| `/login` | LoginPage | Email/password form |
|
|
| `/app` | DashboardPage | Stats (total, running, healthy, degraded, stopped, errors) + instance cards |
|
|
| `/app/instances` | InstanceListPage | Table view with search/filter |
|
|
| `/app/instances/new` | CreateWizardPage | 5-step wizard (info, features, email, tunnel, review) |
|
|
| `/app/instances/:id` | InstanceDetailPage | Tabs: Overview, Services, Logs, Backups, Tunnel |
|
|
| `/app/backups` | BackupsPage | Cross-instance backup list with stats |
|
|
| `/app/audit` | AuditLogPage | Filterable audit log + detail drawer |
|
|
| `/app/settings` | SettingsPage | Port ranges, Pangolin config, defaults |
|
|
|
|
### Sidebar Navigation
|
|
|
|
1. Dashboard (home)
|
|
2. Instances (list)
|
|
3. Backups (cross-instance)
|
|
4. Audit Log (activity trail)
|
|
5. Settings (CCP config)
|
|
|
|
---
|
|
|
|
## Provisioning Flow
|
|
|
|
When `POST /api/instances` is called, the system:
|
|
|
|
```
|
|
1. Validate uniqueness (slug + domain)
|
|
2. Allocate 4 ports from ranges
|
|
3. Generate 14 secrets (passwords, JWT keys, encryption keys)
|
|
4. Create Instance record (status: PROVISIONING)
|
|
5. [async] Create directory: /opt/ccp/instances/{slug}/changemaker.lite/
|
|
6. [async] rsync CML source (excluding node_modules, .git, .env, .claude)
|
|
7. [async] Decrypt secrets → build Handlebars context
|
|
8. [async] Render 7 templates: docker-compose.yml, .env, nginx configs, Pangolin, Prometheus
|
|
9. [async] Copy static files (nginx.conf)
|
|
10. [async] docker compose pull (non-fatal if images cached)
|
|
11. [async] docker compose build
|
|
12. [async] Start infrastructure: v2-postgres + redis-changemaker
|
|
13. [async] Wait for healthy (Docker healthcheck polling)
|
|
14. [async] Start API → run prisma migrate deploy → prisma db seed
|
|
15. [async] docker compose up (all services)
|
|
16. [async] Wait for HTTP health (localhost:{api_port}/api/health)
|
|
17. [async] Set status: RUNNING
|
|
```
|
|
|
|
Frontend polls `GET /api/instances/:id` every 3 seconds during provisioning to show progress.
|
|
|
|
---
|
|
|
|
## Health Check System
|
|
|
|
### How It Works
|
|
|
|
1. **Scheduler** starts on API boot (default: every 5 minutes, configurable via `HEALTH_CHECK_INTERVAL_MS`)
|
|
2. For each RUNNING instance, runs `docker compose ps --format json`
|
|
3. Parses container states and health check results
|
|
4. Determines overall status:
|
|
- **HEALTHY** — all containers running, all health checks passing
|
|
- **DEGRADED** — some containers running but not all, or some health checks failing
|
|
- **UNHEALTHY** — majority of containers down or failing health checks
|
|
- **UNKNOWN** — no containers found or compose project doesn't exist
|
|
5. Stores `HealthCheck` record with per-service status JSON, response time
|
|
6. Updates `instance.lastHealthCheck` timestamp
|
|
|
|
### Manual Trigger
|
|
|
|
`POST /api/instances/:id/health-check` runs an immediate check (SUPER_ADMIN/OPERATOR only).
|
|
|
|
---
|
|
|
|
## Backup System
|
|
|
|
### What Gets Backed Up
|
|
|
|
1. **PostgreSQL dump** — `pg_dump` inside the instance's v2-postgres container
|
|
2. **Uploads archive** — tar.gz of the uploads directory (if it exists)
|
|
3. **Manifest** — JSON file with file names, sizes, SHA-256 hashes
|
|
|
|
### Backup Flow
|
|
|
|
```
|
|
1. Validate instance is RUNNING
|
|
2. Create Backup record (status: PENDING)
|
|
3. [async] Set status: IN_PROGRESS
|
|
4. [async] mkdir /var/backups/ccp-instances/{slug}/{timestamp}/
|
|
5. [async] docker compose exec v2-postgres pg_dump → v2-postgres.sql → gzip
|
|
6. [async] tar -czf uploads.tar.gz (if uploads/ exists)
|
|
7. [async] Write manifest.json with file inventory + SHA-256 hashes
|
|
8. [async] tar -czf final archive → /var/backups/ccp-instances/{slug}/backup-{slug}-{timestamp}.tar.gz
|
|
9. [async] Cleanup temp directory
|
|
10. [async] Update Backup record (COMPLETED, archivePath, sizeBytes, manifest)
|
|
11. [async] Write audit log
|
|
```
|
|
|
|
### Retention
|
|
|
|
`cleanupOldBackups(retentionDays)` deletes archives + records older than the configured retention period (default: 30 days).
|
|
|
|
---
|
|
|
|
## Phased Implementation
|
|
|
|
### Phase 1: Foundation (COMPLETE)
|
|
**Goal:** Skeleton that boots — database, auth, project structure.
|
|
|
|
**Delivered:**
|
|
- Prisma schema with all 10 models (User, Instance, HealthCheck, Backup, AuditLog, etc.)
|
|
- JWT authentication (access + refresh tokens with atomic rotation)
|
|
- Role-based access control (SUPER_ADMIN, OPERATOR, VIEWER)
|
|
- Zod-validated environment configuration
|
|
- AES-256-GCM encryption for instance secrets
|
|
- React admin shell with dark theme, sidebar navigation, protected routes
|
|
- Zustand auth store with token persistence + refresh interceptor
|
|
- Login page, Dashboard placeholder, Settings page
|
|
- Docker Compose orchestration (PostgreSQL, Redis, API, Admin)
|
|
- Placeholder pages wired for Audit Log, Backups
|
|
|
|
### Phase 2: Docker Lifecycle (COMPLETE)
|
|
**Goal:** Create and manage running CML instances.
|
|
|
|
**Delivered:**
|
|
- Instance CRUD with slug/domain uniqueness validation
|
|
- Port allocation across 4 ranges (API, Admin, PostgreSQL, Nginx)
|
|
- Secret generation (14 secrets: postgres, redis, JWT, encryption, admin passwords)
|
|
- Handlebars template engine rendering 7 config files per instance
|
|
- 13-step async provisioning (copy source → render config → pull → build → migrate → seed → start)
|
|
- Lifecycle operations: start, stop, restart (whole stack or individual service)
|
|
- Container status via `docker compose ps --format json`
|
|
- Log viewing via `docker compose logs` with service/tail/since filters
|
|
- 5-step Create Instance wizard (basic info → features → email → tunnel → review)
|
|
- Instance detail page with tabs (Overview, Services, Logs, Backups, Tunnel)
|
|
- Service health grid with per-container restart/log-view actions
|
|
- Provisioning progress indicator (polls every 3s)
|
|
- Audit logging on all lifecycle operations (7 action types)
|
|
|
|
### Phase 3: Observability + Backups (COMPLETE)
|
|
**Goal:** Visibility into instance health, operator activity trail, and data protection.
|
|
|
|
**Delivered:**
|
|
|
|
#### Part A — Audit Log
|
|
- Audit service with filtered queries (action, instance, user, date range) + pagination
|
|
- `GET /api/audit` endpoint with query parameter filtering
|
|
- IP address capture on all audit log entries (existing + new)
|
|
- `USER_LOGIN` audit event on successful authentication
|
|
- `SETTINGS_UPDATE` audit event on settings changes
|
|
- Full AuditLogPage: filterable table, action-colored tags, detail drawer with JSON inspector, server-side pagination, 30s auto-refresh toggle
|
|
|
|
#### Part B — Health Checks
|
|
- Health service with `checkInstanceHealth()` analyzing Docker container states
|
|
- Overall status determination (HEALTHY/DEGRADED/UNHEALTHY/UNKNOWN) from per-container state
|
|
- Scheduled health checker (default 5-minute interval, configurable, 0 to disable)
|
|
- `POST /api/instances/:id/health-check` for manual checks
|
|
- `GET /api/instances/:id/health-history` for paginated history
|
|
- Health card in Instance Detail overview with "Check Now" button + history table
|
|
- Dashboard stat cards: Healthy and Degraded instance counts from `/api/health/overview`
|
|
|
|
#### Part C — Backups
|
|
- Backup service: pg_dump via Docker exec, uploads tar.gz, SHA-256 manifest, final archive
|
|
- Async backup creation with PENDING → IN_PROGRESS → COMPLETED/FAILED progression
|
|
- `POST /api/instances/:id/backup` — create backup
|
|
- `GET /api/instances/:id/backups` — instance-scoped backup list
|
|
- `GET /api/backups` — cross-instance backup list with pagination
|
|
- `DELETE /api/backups/:id` — delete backup (file + DB record)
|
|
- `GET /api/backups/:id/download` — stream backup archive for download
|
|
- BackupsPage: cross-instance table, instance filter, stats (count/size/last), "Backup All Running"
|
|
- Enhanced Instance Detail backups tab: create/download/delete actions, status polling
|
|
- Backup storage volume mount in docker-compose.yml
|
|
- Old backup cleanup utility (configurable retention days)
|
|
|
|
### Phase 4: Pangolin Integration (PLANNED)
|
|
**Goal:** Automated tunnel setup for exposing instances to the internet.
|
|
|
|
**Scope:**
|
|
- Pangolin API client (site creation, resource management, Newt credentials)
|
|
- Automated tunnel setup endpoint (`POST /api/instances/:id/setup-tunnel`)
|
|
- Per-instance Newt container management (start/stop with stored credentials)
|
|
- Resource sync for all instance subdomains (app, api, media, docs, etc.)
|
|
- Tunnel status monitoring in Instance Detail tunnel tab
|
|
- Bulk tunnel setup for fleet-wide deployment
|
|
|
|
### Phase 5: Upgrades + Git Integration (PLANNED)
|
|
**Goal:** Rolling upgrades and version management.
|
|
|
|
**Scope:**
|
|
- Git pull + branch checkout for instance source code
|
|
- Database migration execution (prisma migrate deploy)
|
|
- Docker image rebuild + rolling restart
|
|
- Upgrade progress tracking (similar to provisioning)
|
|
- Rollback capability (pre-upgrade backup + restore)
|
|
- Version display on instance cards and detail pages
|
|
- `INSTANCE_UPGRADE` audit event
|
|
|
|
### Phase 6: User Management + RBAC (PLANNED)
|
|
**Goal:** Multi-operator support with granular permissions.
|
|
|
|
**Scope:**
|
|
- User CRUD pages (create, edit, delete operators)
|
|
- Role assignment and management
|
|
- Per-instance access control (operator can only manage assigned instances)
|
|
- Invitation flow (email invite with temporary password)
|
|
- Password change and reset functionality
|
|
- Session management (view/revoke active sessions)
|
|
|
|
### Phase 7: Monitoring Dashboard (PLANNED)
|
|
**Goal:** Fleet-wide observability with trends and alerting.
|
|
|
|
**Scope:**
|
|
- Health trend charts (uptime over time)
|
|
- Resource utilization (CPU, memory, disk via Docker stats)
|
|
- Alert rules (instance down > N minutes, disk usage > threshold)
|
|
- Notification channels (email, webhook)
|
|
- Backup compliance monitoring (last backup age alerts)
|
|
- Fleet summary dashboard with at-a-glance status
|
|
|
|
### Phase 8: Instance Configuration UI (PLANNED)
|
|
**Goal:** Edit instance configuration without SSH.
|
|
|
|
**Scope:**
|
|
- Feature flag toggles (media, listmonk, gancio, monitoring)
|
|
- SMTP configuration management
|
|
- .env variable editor (safe subset)
|
|
- Docker Compose service scaling
|
|
- Configuration diff preview before applying
|
|
- Auto-restart on config change
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `NODE_ENV` | `development` | Environment mode |
|
|
| `PORT` | `5000` | API server port |
|
|
| `DATABASE_URL` | — | PostgreSQL connection string |
|
|
| `REDIS_URL` | `redis://localhost:6399` | Redis connection string |
|
|
| `JWT_ACCESS_SECRET` | — | JWT signing key (min 32 chars) |
|
|
| `JWT_REFRESH_SECRET` | — | Refresh token signing key (min 32 chars) |
|
|
| `JWT_ACCESS_EXPIRES_IN` | `15m` | Access token lifetime |
|
|
| `JWT_REFRESH_EXPIRES_IN` | `7d` | Refresh token lifetime |
|
|
| `ENCRYPTION_KEY` | — | AES-256 key for secrets at rest (min 32 hex chars) |
|
|
| `INITIAL_ADMIN_EMAIL` | `admin@example.com` | Bootstrap admin email |
|
|
| `INITIAL_ADMIN_PASSWORD` | `ChangeMe2025!!` | Bootstrap admin password (min 12 chars) |
|
|
| `CORS_ORIGINS` | `http://localhost:5100` | Allowed origins (comma-separated) |
|
|
| `INSTANCES_BASE_PATH` | `/opt/ccp/instances` | Where instance directories live |
|
|
| `CML_SOURCE_PATH` | `/home/bunker-admin/changemaker.lite` | CML source to clone from |
|
|
| `CML_GIT_REPO` | — | Git repo URL (if cloning remotely) |
|
|
| `CML_GIT_BRANCH` | `v2` | Default branch for new instances |
|
|
| `PORT_RANGE_API_START` | `14000` | API port range start |
|
|
| `PORT_RANGE_API_END` | `14999` | API port range end |
|
|
| `PORT_RANGE_ADMIN_START` | `13000` | Admin port range start |
|
|
| `PORT_RANGE_ADMIN_END` | `13999` | Admin port range end |
|
|
| `PORT_RANGE_POSTGRES_START` | `15400` | PostgreSQL port range start |
|
|
| `PORT_RANGE_POSTGRES_END` | `15499` | PostgreSQL port range end |
|
|
| `PORT_RANGE_NGINX_START` | `10000` | Nginx port range start |
|
|
| `PORT_RANGE_NGINX_END` | `10999` | Nginx port range end |
|
|
| `PANGOLIN_API_URL` | — | Pangolin API base URL |
|
|
| `PANGOLIN_API_KEY` | — | Pangolin API key |
|
|
| `PANGOLIN_ORG_ID` | — | Pangolin organization ID |
|
|
| `HEALTH_CHECK_INTERVAL_MS` | `300000` | Health check interval (0 to disable) |
|
|
| `BACKUP_STORAGE_PATH` | `/var/backups/ccp-instances` | Backup archive storage directory |
|
|
| `BACKUP_RETENTION_DAYS` | `30` | Auto-cleanup threshold |
|
|
|
|
---
|
|
|
|
## Docker Services
|
|
|
|
```yaml
|
|
services:
|
|
ccp-postgres: # PostgreSQL 16 Alpine — CCP database (port 5480)
|
|
ccp-redis: # Redis 7 Alpine — rate limiting, caching (port 6399)
|
|
ccp-api: # Node 20 + Docker CLI — API server (port 5000)
|
|
ccp-admin: # Nginx + React SPA — admin GUI (port 5100)
|
|
```
|
|
|
|
### Volume Mounts (ccp-api)
|
|
|
|
| Host | Container | Mode | Purpose |
|
|
|------|-----------|------|---------|
|
|
| `./api` | `/app` | rw | Source code (dev) |
|
|
| `./templates` | `/app/templates` | ro | Handlebars templates |
|
|
| `/var/run/docker.sock` | `/var/run/docker.sock` | — | Docker CLI access |
|
|
| `$INSTANCES_BASE_PATH` | Same | rw | Instance directories |
|
|
| `$CML_SOURCE_PATH` | Same | ro | CML source for provisioning |
|
|
| `$BACKUP_STORAGE_PATH` | Same | rw | Backup archives |
|
|
|
|
---
|
|
|
|
## File Inventory
|
|
|
|
### API (`api/`)
|
|
```
|
|
src/
|
|
├── server.ts # Express app + route mounting + health scheduler start
|
|
├── config/
|
|
│ ├── env.ts # Zod env validation (30+ vars)
|
|
│ └── redis.ts # Redis client config
|
|
├── middleware/
|
|
│ ├── auth.ts # authenticate + requireRole middleware
|
|
│ ├── error-handler.ts # AppError class + global error handler
|
|
│ └── validate.ts # Zod request body validation
|
|
├── modules/
|
|
│ ├── auth/
|
|
│ │ ├── auth.routes.ts # POST login/refresh/logout, GET /me
|
|
│ │ ├── auth.schemas.ts # Zod schemas for auth payloads
|
|
│ │ └── auth.service.ts # JWT generation, bcrypt verify, token rotation
|
|
│ ├── instances/
|
|
│ │ ├── instances.routes.ts # CRUD + lifecycle + services + logs + health + backups
|
|
│ │ ├── instances.schemas.ts # Zod create/update validation
|
|
│ │ ├── instances.service.ts # Business logic + audit logging with IP capture
|
|
│ │ └── provisioner.ts # 13-step async provisioning orchestration
|
|
│ ├── audit/
|
|
│ │ ├── audit.routes.ts # GET /api/audit with filters + pagination
|
|
│ │ └── audit.service.ts # Prisma query builder for audit logs
|
|
│ ├── backups/
|
|
│ │ └── backup.routes.ts # GET list, DELETE, GET download
|
|
│ ├── health/
|
|
│ │ └── health.routes.ts # Public health + authenticated overview
|
|
│ └── settings/
|
|
│ └── settings.routes.ts # GET all, PUT :key (+ audit logging)
|
|
├── services/
|
|
│ ├── docker.service.ts # Docker CLI wrapper (ps, exec, logs, up, down, etc.)
|
|
│ ├── health.service.ts # Health check logic + 5-minute scheduler
|
|
│ ├── backup.service.ts # pg_dump + tar + manifest + cleanup
|
|
│ ├── port-allocator.ts # Port range management
|
|
│ ├── secret-generator.ts # CML-compatible credential generation
|
|
│ └── template-engine.ts # Handlebars rendering for 7 config files
|
|
└── utils/
|
|
├── encryption.ts # AES-256-GCM encrypt/decrypt
|
|
└── logger.ts # Winston config
|
|
```
|
|
|
|
### Admin (`admin/`)
|
|
```
|
|
src/
|
|
├── App.tsx # Route definitions + dark theme config
|
|
├── main.tsx # React entry
|
|
├── components/
|
|
│ ├── AppLayout.tsx # Sidebar nav + header + user dropdown
|
|
│ ├── InstanceCard.tsx # Card with status, health bar, features
|
|
│ ├── ServiceHealthGrid.tsx # Container state table with actions
|
|
│ ├── LogViewer.tsx # Log display with service filter
|
|
│ └── ProtectedRoute.tsx # Auth guard wrapper
|
|
├── pages/
|
|
│ ├── LoginPage.tsx # Auth form
|
|
│ ├── DashboardPage.tsx # Stats + health overview + instance cards
|
|
│ ├── InstanceListPage.tsx # Instance table
|
|
│ ├── CreateWizardPage.tsx # 5-step provisioning wizard
|
|
│ ├── InstanceDetailPage.tsx # Tabbed detail (overview/services/logs/backups/tunnel)
|
|
│ ├── BackupsPage.tsx # Cross-instance backup manager
|
|
│ ├── AuditLogPage.tsx # Filterable audit trail
|
|
│ └── SettingsPage.tsx # CCP configuration
|
|
├── stores/
|
|
│ └── auth.store.ts # Zustand auth + localStorage persistence
|
|
├── lib/
|
|
│ └── api.ts # Axios + auth interceptors + token refresh
|
|
└── types/
|
|
└── api.ts # TypeScript interfaces for all API models
|
|
```
|
|
|
|
### Templates (`templates/`)
|
|
```
|
|
docker-compose.yml.hbs # Full CML docker-compose with ports/secrets/features
|
|
env.hbs # Instance .env file
|
|
nginx/
|
|
├── nginx.conf # Global nginx config (static copy)
|
|
└── conf.d/
|
|
├── default.conf.hbs # Subdomain routing
|
|
├── api.conf.hbs # API reverse proxy
|
|
└── services.conf.hbs # Service proxies
|
|
configs/
|
|
├── pangolin/resources.yml.hbs # Tunnel resource definitions
|
|
└── prometheus/prometheus.yml.hbs # Monitoring scrape targets
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# 1. Clone and enter the CCP directory
|
|
cd changemaker.lite/changemaker-control-panel
|
|
|
|
# 2. Copy environment file
|
|
cp .env.example .env
|
|
# Edit .env: set strong passwords for JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, ENCRYPTION_KEY
|
|
|
|
# 3. Start CCP
|
|
docker compose up -d
|
|
|
|
# 4. Run migrations + seed
|
|
docker compose exec ccp-api npx prisma migrate deploy
|
|
docker compose exec ccp-api npx prisma db seed
|
|
|
|
# 5. Access admin GUI
|
|
open http://localhost:5100
|
|
# Login with INITIAL_ADMIN_EMAIL / INITIAL_ADMIN_PASSWORD from .env
|
|
```
|
|
|
|
---
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# API development (hot reload)
|
|
cd api && npm install && npx tsx src/server.ts
|
|
|
|
# Admin development (Vite dev server)
|
|
cd admin && npm install && npm run dev
|
|
|
|
# Type checking
|
|
cd api && npx tsc --noEmit
|
|
cd admin && npx tsc --noEmit
|
|
|
|
# Database operations
|
|
cd api && npx prisma migrate dev # Create/apply migrations
|
|
cd api && npx prisma studio # Browse database GUI
|
|
cd api && npx prisma db seed # Re-seed admin user
|
|
```
|