From c514c0042bcc0286106326407d7a672fd9d0fe1b Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 16 Jun 2026 13:03:08 +0200 Subject: [PATCH] docs: CHANGELOG + README for v0.70.0 (config-apply self-restart, restart button, geo report push, always-report geo, autocomplete fix) Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ controller/README.md | 14 +++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79feb51..0847c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ ## Changelog +### v0.70.0 — config-apply self-restart + geo-restriction UX fixes (2026-06-16) + +Fixes found during live geo testing (rotating the Cloudflare API token). + +- **Config-apply now self-restarts (core fix).** `POST /api/config/apply` previously wrote the new + `controller.yaml` but logged "restart needed" and left stale in-process singletons — the Cloudflare + client is built once at startup, so a rotated CF token kept 403'ing until a manual LXC restart. Now: + if the pushed config is byte-identical to the current one, do nothing (no flap on idempotent + re-push); otherwise write, respond 200 (flushed), then **gracefully self-restart** (`os.Exit(0)` after + ~500ms; the container is `restart: unless-stopped`, so it comes back with fresh config). The exit is + behind an injectable `Restarter` seam (`Router.restart`/`SetRestarter`) for unit testing. Removed the + stale "restart needed" wording and the dead `OnConfigApplied` hook (Phase-1-retired infra-backup push). +- **Manual "Vezérlő újraindítása" button** on the settings page → `POST /api/selfrestart` (auth + CSRF + via the `/api/` mount) using the same helper. Confirm dialog → POST → polls `GET /` every 2s until the + controller answers → reloads. Self-serve restart without rebooting the whole guest. +- **Immediate hub report push on geo change.** A successful geo settings save and a successful manual + geo sync now fire an out-of-band, non-blocking report push (`Router.reportPushNow`), so the hub + reflects the new geo state / clears a stale `last_sync_error` within seconds instead of after the + next ~15-min cycle. (Pattern can extend to other settings later; scoped to geo handlers for now.) +- **Always report `geo_restriction`.** `BuildReport` now always populates the field (Enabled=false, + empty countries when never configured) instead of omitting it when nil — so the hub always renders + the geo section ("Inaktív" when off) rather than hiding it. +- **Country autocomplete fixed.** Root cause (diagnosed live): `filterCountries` populated the list + correctly but revealed it with `style.display = ''`; the `.geo-country-list` CSS default is + `display:none`, so clearing the inline style kept the populated dropdown hidden — no console error, + just an invisible list. Latent since the geo feature's first commit (not the hypothesised JS throw). + Fix: reveal with `display = 'block'`. + ### v0.69.0 — remove dead infra-backup stubs + the unused restic-password report field (2026-06-16) Controller half of the Phase-1 Infra Backup retirement (hub v0.12.0; see diff --git a/controller/README.md b/controller/README.md index a44591a..af2240f 100644 --- a/controller/README.md +++ b/controller/README.md @@ -1215,15 +1215,27 @@ Five sections: Periodic JSON push (default every 15 min) to the central felhom-hub service: - System: hostname, OS, CPU, memory, disk usage, uptime - Containers: running/stopped counts, per-container CPU/memory -- Backup: last run, success, repo stats, snapshot count, restic password (for disaster recovery) +- Backup: last DB-dump run, success (disk-tier backup is the host agent's; restic password is no longer reported — removed v0.69.0) - Health: current status, issues, warnings - Stacks: deployed apps with versions and states - Config hash: SHA256 of `controller.yaml` for Hub-side config comparison +- **Geo-restriction (always present, v0.70.0):** `geo_restriction` is always populated — `Enabled=false` with an empty country list when never configured — so the Hub always renders the geo section ("Inaktív" when off) instead of hiding it. `buildGeoRestrictionReport` in `internal/report/builder.go`. - **App telemetry** (v0.28.0+): Per-stack memory (current/avg/peak) and CPU averages from the last 15 minutes of metrics data, plus log scan results (error/warning counts with deduplicated issues). Only non-protected, deployed stacks are included. Backward-compatible: old Hub versions silently ignore this field. - **Controller telemetry** (v0.32.4+): The controller's own container (`felhom-controller`) is included as a special entry in the `app_telemetry` array. Its memory/CPU metrics come from the same metrics collector, and its log warnings/errors are scanned via `docker logs` using the same pipeline as app containers. This reuses all existing Hub telemetry infrastructure (memory trend charts, known issues, fleet aggregation) with zero Hub-side changes. Bearer token authentication, 3-attempt retry with 5-second backoff. Push status tracked via `PushStatus` struct (LastAttempt, LastSuccess, LastError, consecutive failures) — used by the monitoring page and alert system to show Hub connection health. +**Immediate report push on geo change (v0.70.0):** besides the periodic cycle, a successful geo settings save and a successful manual geo sync fire an **out-of-band, non-blocking** report push (`Router.reportPushNow`, wired in `main.go` to `BuildReport`+`Push` in a goroutine), so the Hub reflects the new geo state / clears a stale `last_sync_error` within seconds instead of after the next ~15-min cycle. Currently scoped to the geo handlers; the same seam can be reused for other settings later. + +#### Config apply + self-restart (`internal/api/router.go`, `internal/api/selfrestart.go`) + +`POST /api/config/apply` (Hub-authed) writes a new `controller.yaml`, but the new config only takes effect on **restart** — singletons such as the Cloudflare client are built once at startup (so a rotated CF API token would otherwise keep failing). Behaviour (v0.70.0): + +- **No-op guard:** if the pushed body is byte-identical to the current file, do nothing — no rewrite, no restart (the Hub may re-push idempotently; never flap on a no-op). +- Otherwise: write `0600`, respond `200` (flushed), then **gracefully self-restart** — `gracefulSelfRestart` waits ~500 ms for the response to flush, then `os.Exit(0)`. The container runs `restart: unless-stopped`, so Docker brings it back with the fresh config, and a startup report is pushed. The exit sits behind an injectable seam (`Router.restart` / `SetRestarter`) for unit testing. + +**Manual restart (v0.70.0):** `POST /api/selfrestart` (session auth + CSRF via the `/api/` mount) runs the same helper — surfaced as the **"Vezérlő újraindítása"** button on the settings page (confirm → POST → poll `GET /` every 2 s → reload), so a customer can recover the controller without rebooting the whole guest. + #### App Telemetry (`internal/metrics/telemetry.go`, `internal/metrics/logscanner.go`, `internal/report/telemetry.go`) Each report push now includes per-app telemetry data: