From aca3b8680a84a03efb1231c84506039bf447f502 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 17 Feb 2026 09:04:28 +0100 Subject: [PATCH] v0.9.0: Storage paths registry, per-app HDD_PATH resolution, storage management UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix backup toggles not appearing (read each app's own HDD_PATH from app.yaml) - Storage paths registry in settings.json with auto-discovery from deployed apps - Settings page "Adattárolók" section with disk usage, add/remove/default/schedulable - Deploy page path field as dropdown of registered storage paths - Health check storage monitoring (mount point, disk usage alerts) - Mount-point validation utilities (Linux syscall + cross-platform stubs) - Controller docker-compose mount changed to /mnt:/mnt:rw for multi-storage Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 99 +++++++++ CONTEXT.md | 43 ++-- controller/cmd/controller/main.go | 76 ++++++- controller/configs/controller.yaml.example | 2 +- controller/docker-compose.yml | 4 +- controller/internal/backup/appdata.go | 2 +- controller/internal/backup/backup.go | 2 +- controller/internal/monitor/healthcheck.go | 40 +++- controller/internal/report/builder.go | 10 +- controller/internal/settings/settings.go | 172 +++++++++++++++ controller/internal/system/mounts_linux.go | 92 ++++++++ controller/internal/system/mounts_other.go | 49 +++++ controller/internal/web/handlers.go | 196 +++++++++++++++++- controller/internal/web/server.go | 16 ++ controller/internal/web/templates/deploy.html | 16 ++ .../internal/web/templates/settings.html | 98 +++++++++ controller/internal/web/templates/style.css | 79 +++++++ 17 files changed, 963 insertions(+), 33 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 controller/internal/system/mounts_linux.go create mode 100644 controller/internal/system/mounts_other.go diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0586b29 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,99 @@ +# Changelog + +## v0.9.0 — Storage Paths Foundation & Backup Toggle Fix (2026-02-17) + +### Fixed +- Per-app backup toggles not appearing on backup page (root cause: missing global `hdd_path` in controller.yaml made `ParseComposeHDDMounts` return nil) +- Each app's HDD_PATH is now read from its own `app.yaml` env section instead of relying on a global config value + +### Added +- **Storage paths registry** — multiple external storage paths managed in `settings.json` with auto-discovery from deployed apps on startup +- **Settings page "Adattárolók" section** — view, add, remove, set default, toggle schedulable for storage paths. Disk usage bars, app counts, mount status badges +- **Deploy page storage dropdown** — `path` field type shows registered schedulable paths as dropdown instead of free-text input +- **Health check storage monitoring** — checks path accessibility, mount-point validation (warns if data would write to SSD), disk usage alerts at 90%/95% +- **Mount-point validation** — `IsMountPoint()`, `IsWritable()`, `PathsOverlap()`, `GetDiskUsage()` (Linux via syscall, stubs for other platforms) + +### Changed +- Controller docker-compose mount changed from `${HDD_PATH}:${HDD_PATH}:ro` to `/mnt:/mnt:rw` for multi-storage + restore support +- `RunHealthCheck()` now accepts storage paths parameter for per-path monitoring +- `BuildReport()` accepts storage paths for accurate system info in hub reports +- Removed unused `hddPath` parameter from `DiscoverAppData()` signature + +## v0.8.0 — Storage Overview, Per-App Backup Toggles & Limited Restore (2026-02-16) + +- Storage overview on backup page (SSD/HDD progress bars + repo stats) +- Restic password visibility with show/copy for disaster recovery +- App data discovery (`DiscoverAppData()` with `StackDataProvider` interface) +- Per-app backup toggles (HDD data paths per app) +- Dynamic backup paths based on enabled apps +- Limited app restore from restic snapshots +- Flash messages on backup page + +## v0.7.2 — Fix Notification Preferences Sync (2026-02-16) + +- Hub: `POST /api/v1/preferences` endpoint +- Hub: Notification section on customer detail page +- Controller: `SyncPreferences` method for hub sync +- Controller: Sync on settings save + startup + +## v0.7.1 — Monitoring Warnings, Dashboard Alerts & Notification System (2026-02-16) + +- Monitoring page "Távoli monitoring" section +- Dashboard alert banners (error/warning/info) +- Hub notification relay (Resend email) +- Controller-side notifier with cooldown tracking +- Notification preferences UI on settings page + +## v0.7.0 — Authentication, Persistence & Settings Page (2026-02-16) + +- `settings.json` persistence layer +- Password change via settings page +- Session management improvements +- DB validation persistence across restarts + +## v0.6.3 — Bug fixes (2026-02-16) + +- `--hdd-path` validation in docker-setup.sh +- `window.event` deprecation fix +- Page title separator fix +- `nextPruneLabel()` Sunday fix + +## v0.6.0 — Healthcheck + Central Push + Hub Dashboard (2026-02-16) + +- Heartbeat + backup integrity pings +- Central hub reporting (report builder + pusher) +- Hub service with SQLite store + dark theme dashboard + +## v0.5.0 — Monitoring Page with Metrics Store (2026-02-16) + +- SQLite metrics store with WAL mode +- Background metrics collector (60s interval) +- System + container charts (Chart.js 4.4.7) +- Per-container detail views + +## v0.4.5 — Dedicated Backup Page (2026-02-16) + +- Full backup system visibility +- DB dump engine + restic integration +- Snapshot history + validation +- Manual backup trigger + +## v0.4.0 — Monitoring & Health + Backups (2026-02-15) + +- Central job scheduler +- CPU/temperature/load monitoring +- Healthchecks.io integration +- Database dump + restic backup engine + +## v0.3.0 — Templates + Server Split (2026-02-15) + +- go:embed template migration +- Server decomposition (auth, handlers, funcmap) +- Domain rename (dashboard → felhom) + +## v0.2.x — Initial Development (2026-02-13 to 2026-02-15) + +- Core stack management (deploy, start, stop, restart, update) +- Dashboard, stacks page, deploy flow, logs +- System info, memory validation +- Git sync, app catalog, delete flow diff --git a/CONTEXT.md b/CONTEXT.md index bbab884..1336873 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,7 +7,7 @@ > > Ask Claude Code: "Please update CONTEXT.md with what we did today" -Last updated: 2026-02-16 (session 25) +Last updated: 2026-02-17 (session 26) --- @@ -22,7 +22,7 @@ Last updated: 2026-02-16 (session 25) ## Current project state ### felhom-controller (this repo) -- **Version:** v0.8.0 +- **Version:** v0.9.0 - **Phase 1:** ✅ COMPLETE — Stack Manager + Deploy Flow - **Phase 2:** ✅ COMPLETE — Monitoring & Health (scheduler, CPU/temp, healthchecks.io pings) - **Phase 3:** ✅ COMPLETE — Backups (DB dumps, restic integration, manual trigger, **dedicated backup page**) @@ -34,7 +34,22 @@ Last updated: 2026-02-16 (session 25) - **Running on:** demo-felhom (N100 mini PC) at 192.168.0.162:8080 - **All Phase 1-5 features working:** deploy, start/stop/restart/update, logs, health-aware states, auth, monitoring, backups, backup detail page, system monitoring page, settings page -### What was just completed (2026-02-16 session 25) +### What was just completed (2026-02-17 session 26) +- **v0.9.0 — Phase A: Storage Paths Foundation & Backup Toggle Fix:** + - **Root cause:** Per-app backup toggles (v0.8.0) didn't appear because `controller.yaml` had no `paths.hdd_path` set → `ParseComposeHDDMounts` returned nil. Even with global hdd_path, apps with different HDD_PATH values wouldn't match. + - **Core fix: Per-app HDD_PATH resolution** — `stackAdapter.GetStackHDDMounts()` now reads each app's own `HDD_PATH` from its `app.yaml` env section (Priority 1), falling back to all registered storage paths (Priority 2). Removed dependency on global `cfg.Paths.HDDPath`. + - **Storage paths registry** (`settings.json`) — new `StoragePath` struct with Path, Label, IsDefault, Schedulable, AddedAt. Thread-safe CRUD methods in `settings.go` (Get/Add/Remove/SetDefault/SetSchedulable). Multiple external storage paths supported. + - **Auto-discovery** — On startup, `discoverHDDPaths()` scans deployed apps' `app.yaml` for `HDD_PATH` values. `AutoDiscoverStoragePaths()` registers discovered paths with inferred labels. Legacy `cfg.Paths.HDDPath` used as fallback. + - **Mount-point validation** — New `mounts_linux.go` (build-tagged): `IsMountPoint()` via `syscall.Stat_t.Dev` comparison, `IsWritable()`, `PathsOverlap()`, `GetDiskUsage()` via `syscall.Statfs`. Non-Linux stubs in `mounts_other.go`. + - **Settings page "Adattárolók" section** — Lists registered paths with label, path, disk usage bar, app count, badges (default/active/unmounted). Actions: set default, toggle schedulable, remove (with guards). Expandable "Új adattároló hozzáadása" form with 5-step validation (exists, mount point, writable, no overlap, no duplicate). + - **Deploy page storage dropdown** — `path` field type renders as ` + {{range $.StoragePaths}} + + {{end}} + + {{else}} + + Nincs regisztrált adattároló — adja meg kézzel az útvonalat + {{end}} {{else}} + +
+

Adattárolók

+

Külső meghajtók kezelése alkalmazásadatok tárolásához.

+ + {{if .StorageError}}
{{.StorageError}}
{{end}} + + {{if .StoragePaths}} +
+ {{range .StoragePaths}} +
+
+
+ {{.Label}} + {{.Path}} +
+
+ {{if .IsDefault}}Alapértelmezett{{end}} + {{if .Schedulable}}Aktív{{else}}Inaktív{{end}} + {{if not .IsMounted}}Nincs csatolva!{{end}} +
+
+
+ {{if .DiskInfo}} +
+
+ {{.DiskInfo.UsedHuman}} / {{.DiskInfo.TotalHuman}} +
+
+
+
+
+ {{end}} +
+ {{.AppCount}} alkalmazás használja +
+
+
+ {{if not .IsDefault}} +
+ + +
+ {{end}} + {{if .Schedulable}} +
+ + + +
+ {{else}} +
+ + + +
+ {{end}} + {{if and (not .IsDefault) (eq .AppCount 0)}} +
+ + +
+ {{end}} +
+
+ {{end}} +
+ {{else}} +
+ Nincs regisztrált adattároló. Adjon hozzá egyet az alábbi űrlappal. +
+ {{end}} + +
+ Új adattároló hozzáadása +
+
+ + + Pl. /mnt/hdd_1 — a meghajtónak már csatolva kell lennie +
+
+ + +
+ + +
+
+
+

Jelszó módosítás

diff --git a/controller/internal/web/templates/style.css b/controller/internal/web/templates/style.css index 5d9021c..9a39851 100644 --- a/controller/internal/web/templates/style.css +++ b/controller/internal/web/templates/style.css @@ -1976,6 +1976,85 @@ a.stat-card:hover { border-color: rgba(218, 54, 51, 0.3); } +/* --- Settings page: Storage paths --- */ +.storage-paths-list { + display: flex; + flex-direction: column; + gap: .75rem; +} +.storage-path-item { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 1rem; +} +.storage-path-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; + margin-bottom: .5rem; +} +.storage-path-info { + display: flex; + flex-direction: column; + gap: .15rem; +} +.storage-path-label { + font-weight: 600; + font-size: .95rem; +} +.storage-path-path { + font-family: 'JetBrains Mono', monospace; + font-size: .8rem; + color: var(--text-muted); +} +.storage-path-badges { + display: flex; + gap: .35rem; + flex-shrink: 0; + flex-wrap: wrap; +} +.storage-path-details { + margin: .5rem 0; +} +.storage-path-disk { + margin-bottom: .5rem; +} +.storage-path-meta { + font-size: .8rem; + color: var(--text-muted); +} +.storage-path-actions { + display: flex; + gap: .5rem; + margin-top: .75rem; + flex-wrap: wrap; +} +.btn-xs { + padding: .2rem .5rem; + font-size: .75rem; + border-radius: 6px; +} +.btn-danger-outline { + background: transparent; + border: 1px solid rgba(218, 54, 51, 0.5); + color: var(--red); +} +.btn-danger-outline:hover { + background: var(--red-bg); + border-color: var(--red); +} +.storage-add-details { + margin-top: .5rem; +} +.storage-add-details[open] summary { + margin-bottom: 1rem; +} +.storage-add-form { + margin-top: .75rem; +} + /* Responsive */ @media(max-width: 768px) { .sidebar { width: 100%; height: auto; position: relative; border-right: none; border-bottom: 1px solid var(--border-color); }