diff --git a/CHANGELOG.md b/CHANGELOG.md index e86d7c0..b8b5ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,31 @@ ## Changelog -### What was just completed (2026-02-19 session 60) +### What was just completed (2026-02-20 session 61) +- **v0.19.0 — Deployed App Removal + Missing Field Injection:** + + Two new features: "Eltávolítás" (Remove) action for deployed stacks and automatic missing deploy field injection on template updates. + + **Feature A — Deployed App Removal ("Eltávolítás"):** + - `delete.go`: Added `RemoveStack()` — removes deployed (non-orphaned) stack: `docker compose down --volumes`, optional HDD data cleanup, optional backup data cleanup (DB dumps + cross-drive rsync), removes `app.yaml` only (template files preserved for redeploy); stack reverts to "Nincs telepítve" state + - `delete.go`: Added `GetStackBackupData()` — returns backup path info (DB dump dir + cross-drive rsync dir) with sizes and existence status + - `delete.go`: Added `RemoveResponse`, `BackupDataResponse` structs, `buildPathInfo()` helper + - `router.go`: Added `POST /api/stacks/{name}/remove` endpoint — accepts `{remove_hdd_data, remove_backups}`, computes backup paths via `AppDBDumpPath()`/`AppSecondaryRsyncPath()`, cleans cross-drive config on success + - `router.go`: Added `GET /api/stacks/{name}/backup-data` endpoint — returns backup data paths with sizes + - `crossdrive.go`: Made `getAppDrivePath` → `GetAppDrivePath` (public) for use by router + - `stacks.html`: Added "Eltávolítás" button for stopped, deployed, non-orphaned, non-protected stacks + - `dashboard.html`: Same button in compact card layout + - `layout.html`: Added `removeStack()` modal — fetches HDD + backup data in parallel, 3-section layout (always removed / HDD data with checkbox / backup data with checkbox), reimport warning for preserved HDD data, restic retention note + - `layout.html`: Added `confirmRemoveStack()` — POST to `/remove`, shows result summary with removed/preserved paths + + **Feature B — Missing Deploy Field Injection:** + - `deploy.go`: Added `InjectMissingFields(stackNames)` — iterates deployed stacks, compares `.felhom.yml` deploy_fields against `app.yaml` env vars, auto-generates values for missing `secret` (using generator spec) and `domain` fields, saves updated `app.yaml` + - `deploy.go`: Added `base64key` generator type — produces `base64:` (for Laravel APP_KEY and similar) + - `deploy.go`: Added `containsStr()` helper + - `manager.go`: Added `DeployedStackNames()` — returns names of all deployed stacks + - `sync.go`: Added `postSyncHook func(updated []string)` field to `Syncer`; `New()` accepts optional hook; hook called in `doSync()` after rescan with names of updated stacks + - `main.go`: Wired injection on startup (all deployed stacks) and after sync (updated stacks only) + +### v0.18.0 (2026-02-19 session 60) - **v0.18.0 — Drive Migration & Tier 2 Restic Deprecation:** Full drive replacement workflow with decommissioned state, enhanced per-app migration with backup awareness, and deprecation of restic as a Tier 2 cross-drive backup method (rsync only). diff --git a/controller/README.md b/controller/README.md index dc25239..4ab883e 100644 --- a/controller/README.md +++ b/controller/README.md @@ -4,7 +4,7 @@ A single, lightweight Go container that replaces Portainer + scattered systemd scripts with a unified, Hungarian-language web dashboard for managing Docker Compose stacks, backups, storage, monitoring, and notifications on customer hardware. -**Current version: v0.16.0** +**Current version: v0.19.0** --- @@ -111,12 +111,13 @@ The app catalog lives in a separate Git repository. The controller: - **Never overwrites** `app.yaml` or `.env` (user secrets are safe) - Uses SHA-256 content hashing — only writes files that actually changed - Triggers stack rescan after sync so the dashboard updates immediately +- **Post-sync hook**: auto-injects missing deploy fields (new secrets, domains) into existing `app.yaml` for stacks whose templates were updated (see Missing Field Injection below) - Manual sync via "Sablonok frissitese" button or `POST /api/sync` #### First-Time Deploy Flow 1. Customer sees app card with "Telepites" button -2. Deploy page shows auto-filled fields (domain), auto-generated secrets (DB passwords, hex keys), and user-configurable inputs (admin password, language, storage path) +2. Deploy page shows auto-filled fields (domain), auto-generated secrets (DB passwords, hex keys, base64 keys), and user-configurable inputs (admin password, language, storage path) 3. `checkBeforeDeploy()` JS guard fetches live state first (prevents double-deploy from another tab) 4. **Memory validation** checks `mem_request` against available RAM: - `usable_memory = total_ram - reserved_memory_mb` (default 384MB reserved) @@ -143,12 +144,31 @@ The `/apps/{slug}` page renders hero section, screenshots, setup guide, and opti | Stop | `docker compose stop` (blocked for protected stacks) | | Restart | `docker compose restart` | | Update | `docker compose pull` + `docker compose up -d` | -| Delete | `docker compose down --rmi local --volumes` + optional HDD data cleanup | +| Remove | `docker compose down --volumes` + remove `app.yaml` + optional HDD/backup cleanup; template preserved for redeploy | +| Delete | `docker compose down --rmi local --volumes` + optional HDD data cleanup (orphaned stacks only) | -**Protected stacks** (traefik, cloudflared, felhom-controller) cannot be stopped or deleted from the UI. Restart is allowed. +**Remove vs Delete**: "Eltávolítás" (Remove) is for deployed catalog stacks — it reverts the stack to "Nincs telepítve" state while keeping the template for easy redeployment. "Törlés" (Delete) is for orphaned stacks — it removes the entire stack directory including templates. Both require stopping the stack first. + +**Remove modal** shows three sections: (1) always-removed items (Docker volumes, app.yaml, cross-drive schedule), (2) optional HDD data deletion with reimport warning, (3) optional backup data deletion (DB dumps + cross-drive rsync) with restic retention note. + +**Protected stacks** (traefik, cloudflared, felhom-controller) cannot be stopped, removed, or deleted from the UI. Restart is allowed. **Orphan detection**: Deployed stacks with no matching catalog template are marked as orphaned with an "Elavult" badge and can be safely deleted. +#### Missing Field Injection (`deploy.go`) + +When app templates are updated (e.g., a new `APP_KEY` secret is added to `.felhom.yml`), existing deployed apps need the new field in their `app.yaml`. The controller handles this automatically: + +- **On startup**: `InjectMissingFields()` runs for all deployed stacks +- **After sync**: the post-sync hook runs for stacks whose templates were updated +- For each deployed stack, compares `.felhom.yml` `deploy_fields` against `app.yaml` env vars +- Missing `secret` fields: auto-generated using the field's generator spec (`password:N`, `hex:N`, `base64key:N`) +- Missing `domain` fields: filled with the customer's configured domain +- Other field types (e.g., `text`, `select`): logged as warning for manual configuration +- Locked fields are added to the locked list automatically + +**Generator types**: `password:N` (alphanumeric), `hex:N` (hex-encoded random bytes), `base64key:N` (`base64:` + N random bytes base64-encoded, for Laravel APP_KEY etc.), `static:VALUE` (literal value). + #### Container State Display | State | Color | Label | Meaning | @@ -813,8 +833,8 @@ controller/ │ ├── stacks/ │ │ ├── manager.go # Stack scanning, compose ops, container status │ │ ├── metadata.go # Parse .felhom.yml app metadata -│ │ ├── deploy.go # First-deploy: secret gen, app.yaml, compose up -│ │ └── delete.go # Stack deletion + HDD data cleanup +│ │ ├── deploy.go # First-deploy: secret gen, app.yaml, compose up; missing field injection +│ │ └── delete.go # Stack deletion/removal + HDD/backup data cleanup │ ├── sync/sync.go # Git sync: clone/pull app catalog, content-hash copy │ ├── storage/ │ │ ├── scan.go, scan_linux.go # Disk detection via lsblk + blkid @@ -935,7 +955,7 @@ Auto-managed by the controller. Contains password hash overrides, notification p ### Per-app config (`app.yaml`) -Auto-generated during deployment. Contains env vars, locked fields list, deploy timestamp. Secret fields are locked (read-only after first deploy). +Auto-generated during deployment. Contains env vars, locked fields list, deploy timestamp. Secret fields are locked (read-only after first deploy). Missing fields from updated templates are auto-injected on startup and after sync (see Missing Field Injection). --- @@ -977,7 +997,9 @@ All daily jobs use Europe/Budapest timezone. Skip-if-running prevents concurrent | POST | `/api/stacks/{name}/optional-config` | Update optional env vars | | GET | `/api/stacks/{name}/logs` | Container logs (`?raw=1` for plain text) | | GET | `/api/stacks/{name}/hdd-data` | HDD data paths + sizes | -| DELETE | `/api/stacks/{name}` | Delete stack | +| GET | `/api/stacks/{name}/backup-data` | Backup data paths + sizes (DB dumps, cross-drive rsync) | +| POST | `/api/stacks/{name}/remove` | Remove deployed stack (revert to "not deployed") | +| DELETE | `/api/stacks/{name}` | Delete orphaned stack | | POST | `/api/sync` | Trigger catalog sync | | GET | `/api/system/info` | System info + sync status |