Commit Graph

116 Commits

Author SHA1 Message Date
admin 7d69d96cf3 Remove ping_uuids from example config, update architecture diagram
- Comment out ping_uuids section in controller.yaml.example (deprecated)
- Architecture diagram: remove status.felhom.eu, update to Hub event system
- Mark Healthchecks references as deprecated throughout README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:56:12 +01:00
admin e9dcba2473 v0.21.1: Add GET /api/config endpoint for live config content
New endpoint returns raw controller.yaml content (text/yaml) for Hub
live diff and pull operations. Same auth as other config endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:26:47 +01:00
admin 8aebbb8902 feat: Hub monitoring takeover — event push system + config cleanup (v0.21.0)
Replace external Healthchecks.io with Hub-native event system. Controller
now pushes structured events via POST /api/v1/event with typed detail
structs. Hub handles dead man's switch, notification dispatch, and cooldowns.

Phase 5: PushEvent() core method, 21 event types, expanded notification
settings (11 toggles), Hub connection monitoring on dashboard, alerts.
Phase 6: Deprecation log for ping UUIDs, pinger kept for transition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:53:21 +01:00
admin 55abe401ee docs: update controller README and CHANGELOG for v0.20.0
Add config apply endpoint and config hash in reports to REST API
section, roadmap, and changelog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 16:18:26 +01:00
admin 85d1f2f673 feat: add config apply endpoint and config hash in reports
- POST /api/config/apply: accepts YAML body from Hub, validates and
  writes controller.yaml atomically (tmp+rename)
- GET /api/config/hash: returns SHA256 hash of current config file
- Report payload now includes config_hash field for Hub comparison
- Config endpoints use same dual auth as self-update (session OR Bearer)
- config.LoadFromBytes() for validation without file I/O
- config.FileHash() helper for SHA256 computation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 16:13:35 +01:00
admin 2eccac4b6d updated README and CHANGELOG 2026-02-20 11:20:13 +01:00
admin 8130c344cc feat: deployed app removal + missing field injection (v0.19.0)
Add "Eltávolítás" to remove deployed (non-orphaned) stacks — reverts
them to "Nincs telepítve" while preserving templates for redeploy.
Modal offers HDD data and backup data cleanup choices.

Auto-inject missing deploy fields (secrets, domains) into existing
app.yaml when templates are updated via sync or on controller startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:01:21 +01:00
admin 99bf3ca7a8 feat: drive migration & Tier 2 restic deprecation (v0.18.0)
Phase 1: Deprecate restic as Tier 2 method (rsync only), auto-migrate on startup
Phase 2: Enhanced per-app migration with backup awareness, DB dump copy, auto-cleanup
Phase 3: Full drive migration with decommissioned state, rollback support, wizard UI
Phase 4: Hub report includes decommissioned drive state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:49:14 +01:00
admin bdbe170a54 feat: storage watchdog — USB disconnect detection, auto-stop, safe eject, auto-reconnect (v0.17.0)
New storage watchdog monitors registered storage paths every 5s. On disconnect
(3 consecutive probe failures), auto-stops affected apps, lazy-unmounts stale
VFS entries, fires alerts/notifications/hub report. On reconnect (UUID detected),
auto-remounts via fstab, cleans stale restic locks, offers app restart.

Safe disconnect UI for USB drives: confirmation dialog, stop apps, sync, unmount.
Disconnected state visible across all pages (dashboard, settings, backups, monitoring)
with hatched red bars and badges. Backup guards skip disconnected drives.

22 files changed (1 new: monitor/watchdog.go), ~1500 lines added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:42:26 +01:00
admin 80f5cbaa28 fix: move selfupdate routes before hasSuffix stack cases in router
The selfupdate routes were placed after the generic hasSuffix(path, "/update")
stack case, which was catching /selfupdate/update before the specific case
could match it. Moving the selfupdate cases to before all hasSuffix-based
cases fixes the routing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 18:20:52 +01:00
admin 2687506b08 feat: add controller_url to hub reports (v0.16.1)
Controller now includes its external URL in periodic hub reports so the
hub can trigger self-updates remotely via the /api/selfupdate/update endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 18:16:32 +01:00
admin c9a88afcef feat: add controller self-update mechanism (v0.16.0)
New selfupdate package: version parsing, audit state file, updater with
Gitea registry V2 check, docker pull + compose rewrite + compose up flow.

- API: /api/selfupdate/{status,check,update} with session+bearer auth
- UI: Settings "Verzió és frissítés" card with check/install buttons + JS polling
- Scheduler: periodic check (6h default) + optional daily auto-update
- Notifications: success/failure on post-update startup verification
- Alert: info banner when update available
- docker-compose.yml: add directory bind mount for compose file access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 17:33:40 +01:00
admin 4923afa6a7 v0.15.7: Fix backup page storage display & rename system drive label
- Add StorageBars to backupsHandler so all registered storage paths appear
- Update backups.html to use StorageBars loop (replacing single HDDConfigured block)
- Rename "SSD (/)" → "Rendszer (/)" on backup, monitoring, and dashboard pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 16:04:36 +01:00
admin 75ea9d73f0 Fix bugs from BUGHUNT.md: restore race conditions, infra backup, DR wiring, docker-setup.sh, restore.html 2026-02-19 14:06:42 +01:00
admin 6713df2186 v0.15.5: Disaster recovery — Hub-based infra backup, auto-mount, restore UI
Complete DR implementation (TASK2.md Phases 1-4):
- Hub infra-backup push/pull endpoints (controller.yaml, disk layout, stacks)
- Fresh-deployment detection pulls config from Hub, auto-mounts drives by UUID
- Full-page restore UI with drive status, app table, sequential restore
- docker-setup.sh shows DR instructions when customer_id is configured

New files: disk_layout.go, restore_scan.go, restore_app_linux.go,
restore_drives_linux.go, infra_backup.go, infra_pull.go,
handler_restore.go, restore.html

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:16:46 +01:00
admin 5d993b66a2 Major rewrite of scripts/docker-setup.sh (v5.0) 2026-02-19 11:12:39 +01:00
admin 00c668fc92 v0.15.5: Fix startup hub report — Push() returns real errors, startup retries 3x with 15s delay 2026-02-19 10:08:43 +01:00
admin f54d1a23de v0.15.4: Hub disabled notification, PushOnce, ReportingDisabled field 2026-02-19 09:45:40 +01:00
admin 215ba8a83d v0.15.3: Show all storage paths on dashboard/monitoring + fix hub report 2026-02-19 09:06:59 +01:00
admin d372454c18 v0.15.2: Fix snapshot stats and DB validation loss on restart 2026-02-19 08:45:37 +01:00
admin 2befa6877b v0.15.1: Backup page Részletek overhaul with per-drive tier sections
Replace Tároló section with collapsible Részletek containing 3 tiers:
- Tier 1: per-drive restic repo stats with storage labels
- Tier 2: cross-drive items grouped by destination, split by method
- Tier 3: remote backup placeholder
Restore UI now shows tier + drive labels in snapshot dropdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:23:33 +01:00
admin 0c0cacbe7c fix: handle oversized lines in DB dump validation
Replace bufio.Scanner with bufio.Reader.ReadLine() which gracefully
skips lines exceeding the buffer (isPrefix=true) instead of failing.
Fixes validation of Immich's PostgreSQL dump which contains COPY lines
with binary-encoded image data exceeding the 256KB scanner limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 07:58:31 +01:00
admin debab0f38b fix: mount config.yaml at correct path for FileBrowser Quantum
FileBrowser reads config.yaml from its working directory
(/home/filebrowser/), not from the data subdirectory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:21:48 +01:00
admin b06e9bb368 fix: persist FileBrowser database + sync on manual storage add
- Add server.database to generated config.yaml pointing to the
  persistent data volume. Previously the database was at
  /home/filebrowser/database.db (outside the volume) and was lost
  on every container recreation.
- Call syncFileBrowserMounts after manual storage path add, so newly
  registered drives (like sys_drive) also appear in FileBrowser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:19:26 +01:00
admin 91c12c4b8b feat: sync FileBrowser config on startup
Ensures config.yaml and docker-compose.yml are regenerated on
controller startup, so new drives added while the controller was
down still get their FileBrowser sources configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:56:14 +01:00
admin b88c9c76e6 feat: auto-configure FileBrowser sidebar with per-drive sources
Generate config.yaml with a separate source per registered storage path.
Each source uses the drive's label as its display name, making it appear
automatically in FileBrowser's sidebar. The config.yaml is bind-mounted
into the container (read-only) alongside the data volume.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:53:30 +01:00
admin 30110d3fca fix: show error for duplicate folder name + add client-side validation
CreateDirectory now returns an error when the folder already exists
instead of silently succeeding. JS validates folder name format
(alphanumeric + underscore, max 32 chars) before sending the request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:37:54 +01:00
admin 6b7ca566df fix: clean up stale raw mounts before scanning in attach wizard
After an interrupted attach wizard, the raw mount stays behind,
causing the device to appear as "mounted" in scan results. Now the
scan button calls cancel first, which unmounts any stale raw mounts
that have no bind mount in fstab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:30:32 +01:00
admin 5b7f261ba6 docs: add attach existing drive wizard to README
Document the new v0.15.0 attach wizard: bind-mount approach,
API endpoints, file tree, and roadmap entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:22:29 +01:00
admin 1d394e32ad fix: fstab write fails on bind-mounted /host-fstab (EBUSY)
rename() fails with EBUSY on Docker bind-mounted files. Add safeWriteFile()
helper that tries atomic rename first, falls back to direct write. Fixes
both init wizard and attach wizard fstab operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:19:30 +01:00
admin 98834dd7e8 v0.15.0: Attach existing drive wizard (bind mount, no format)
New Settings wizard to attach drives with existing filesystems without
formatting. Mounts partition at staging path, lets user browse and pick
a subfolder, then bind-mounts it at /mnt/<name> with fstab entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:12:02 +01:00
admin 70d503a902 fix(backup): 4 bug fixes from v0.14.1 code review (v0.14.2)
Bug 1 (HIGH): add --exclude _* to rsync --delete so _db/ and _config/
  directories are never deleted between backup runs (crossdrive.go)

Bug 2 (MEDIUM): refactor RunDBDumps/RunBackup/RunFullBackup to use
  acquireRunning/releaseRunning helpers; extract runDBDumpsInternal and
  runBackupInternal so all three public entry points set m.running and
  RunFullBackup no longer deadlocks calling the public methods (backup.go)

Bug 3 (MEDIUM): log [WARN] when GetDiskUsage returns nil in
  ValidateDestination instead of silently skipping space checks (crossdrive.go)

Bug 4 (MEDIUM): add [WARN] on empty SystemDataPath in NewManager; add
  [ERROR] in GetAppDrivePath; guard DumpStackDB against empty/relative paths
  (backup.go)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 19:46:16 +01:00
admin fcd20eb524 Update README.md for v0.14.1: auto Tier 2 + infra backup docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:14:03 +01:00
admin f7518c0529 v0.14.1: Auto Tier 2 for small apps + infra config in cross-drive backup
- Auto-enable daily rsync Tier 2 for apps without HDD mounts when ≥2
  storage paths exist (AutoEnableSmallApps)
- Sync infrastructure config (stacks dir + controller.yaml) to all
  secondary destinations via _infra/ directory (syncInfraConfig)
- Include infra paths in cross-drive restic snapshots
- Add SecondaryInfraPath() helper to paths.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:11:57 +01:00
admin 0e13d42ccd Update README.md for v0.14.0 architecture
- Per-drive backup architecture (restic repos, DB dumps, path helpers)
- Updated Tier 1/2 sections with new drive layout diagrams
- Updated controller.yaml example (system_data_path, no global paths)
- Updated repo layout (add paths.go), build examples, roadmap
- Removed all v0.12.x references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:53:15 +01:00
admin 563c9515d9 v0.14.0: Per-drive backup architecture + storage path overhaul
Major refactor of backup and storage paths:

- Per-drive restic repos at <drive>/backups/primary/restic/
- Per-app DB dumps at <drive>/backups/primary/<app>/db-dumps/
- Remove global BackupDir, DBDumpDir, ResticRepo config fields
- Add SystemDataPath config (fallback for apps without HDD)
- New backup/paths.go with pure path computation helpers
- Add GetStackHDDPath to StackDataProvider interface
- Restic methods now accept repoPath as parameter
- Cross-drive backup uses new secondary path structure
- Rename storage/ to appdata/ in scripts and compose templates
- Update protected HDD paths (storage → appdata + backups)
- Simplify backup UI (remove global path displays)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:47:39 +01:00
admin 05f6095e6b v0.13.1: UI polish fixes round 2 (4 fixes)
- Fix 1: deploy-cross-drive card uses correct CSS vars (--bg-secondary, --border-color)
- Fix 2: Auto-generated env values — badge inline with label, remove copy buttons, muted readonly inputs
- Fix 3: Snapshot table shows 0 instead of n/a; remove unused .col-na CSS
- Fix 4: Disk warnings moved inline under storage bars (Inline alert field, GetInlineAlerts, inline-warning CSS)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 16:09:56 +01:00
admin 90826dec7a v0.13.0: UI polish fixes (8 improvements)
- Fix 1: Dashboard backup card border (verified already correct)
- Fix 2: Show auto-generated env values on deploy page with copy/reveal
- Fix 3: Temperature value pill for better visibility on dashboard
- Fix 4: Rework dashboard backup section (remove manual trigger, add Tier 2 summary)
- Fix 5: Scope HDD warning banner to dashboard and monitoring pages only
- Fix 6: Move Tárhely section up in monitoring page
- Fix 7: Snapshot table clarity (HOZZÁADOTT header, n/a instead of -)
- Fix 8: Restructure Tároló section into tiered storage view

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 13:30:21 +01:00
admin 3f2557fe26 v0.12.9: Tier 2 for all apps + status dot update
- Tier 2 cross-drive backup now configurable for all apps (not just HDD apps)
- Non-HDD apps (Mealie, Gokapi) can back up config + DB to secondary drive
- Status dot: removed "auto" gray — all apps start yellow, green = 2+ tiers OK
- Backup page: Tier 2 row always shown, Tier 3 placeholder added
- Deploy page: cross-drive config visible for all deployed apps
- Meta badges: non-HDD apps show "Konfig" or "Konfig + DB" instead of "Auto"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 12:11:27 +01:00
admin f9c0338894 Tier 2 for All Apps + Status Dot Update (v0.12.9) 2026-02-18 12:07:34 +01:00
admin 4a9aea647b v0.12.8: complete cross-drive backup + per-tier UI
- Cross-drive now copies DB dumps (_db/) and config (_config/) alongside user data
- restic cross-drive includes config dir + full DB dump dir
- UI: per-tier rows (1. mentés / 2. mentés) instead of per-layer (DB/Konfig/Data)
- UI: BackupContents label shows what each tier protects (DB + Konfig + Adatok)
- UI: rsync backups show browsable indicator (📁)
- Cleanup: removed unused filterSnapshotsByPaths + pathCovers from router.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 11:37:48 +01:00
admin b65ab612f0 Complete Cross-Drive Backup + Per-Tier UI (v0.12.8) 2026-02-18 11:32:11 +01:00
admin e4433f07b4 Post-deploy fixes (v0.12.7a) 2026-02-18 11:03:56 +01:00
admin 6c1762141a v0.12.7: mandatory HDD backup, pre-dump, restore for all apps
Fix 1: HDD data backup is now mandatory for all deployed apps.
resolveAppBackupPaths() iterates ListDeployedStacks() directly — no
longer reads GetAppBackupMap() or checks the Enabled flag. DiscoverAppData()
drops backupPrefs parameter; BackupEnabled is set from HasHDDData.
Five dead settings methods removed: IsAppBackupEnabled, SetAppBackup,
GetAppBackupMap, SetAppBackupBulk, GetAppBackupPrefs.

Fix 2: Cross-drive backup now triggers a fresh DB dump (DumpStackDB)
before running. DBDumper interface added to crossdrive.go; Manager
implements it; SetDBDumper wired in main.go. Non-fatal — proceeds with
user data backup even if DB dump fails.

Fix 3: Restore dropdown shows ALL deployed apps (not just HDD+enabled).
restore.go rewritten: always restores config+DB, adds user data if hasHDD.
UI shows restore type banner (full / config+DB / config only) with
color-coded styling. Snapshot API clarified for non-HDD apps.

Fix 4: "Docker kötetek" → "Konfiguráció" — named volumes are not in
the restic backup paths; compose files + app.yaml are what's backed up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 10:38:51 +01:00
admin 4145e7b500 v0.12.6: fix rsync destination nesting and exclude app-internal DB dumps
Two rsync bugs found during Immich cross-drive backup testing:

Fix 3: Simplified destination path structure in runRsyncBackup.
Old SplitN logic kept "storage/immich" as a subpath, creating redundant
nesting: backups/rsync/immich/storage/immich/<data>. New logic: single
mount → rsync directly into the stack folder; multiple mounts → use
each mount's leaf dir name as subfolder (disambiguated by _N suffix).

Fix 4: Exclude app-internal DB dump files from rsync. Apps like Immich
store periodic pg_dumps in <data>/backups/*.sql.gz. The controller
already handles DB backups via pg_dump — copying them again wastes space.
Added --exclude backups/*.sql.gz/sql/dump to the rsync command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 08:55:32 +01:00
admin c789a00b2d v0.12.5: drive-type-aware cross-drive backup validation
Fix cross-drive backup failing for system-drive destinations. The
/mnt/hdd_placeholder folder is on the internal SSD, so IsMountPoint()
returned false and the old code hard-blocked it.

- ValidateDestination() (crossdrive.go): onSystemDrive flag replaces
  boolean-only check; system drives require ≥10 GB free and <90% usage
  to protect OS stability; external drives just need ≥100 MB free.
- CheckBackupDestination() (mounts_linux.go): Tier 4 now branches on
  h.SystemDrive; system drive blocks at <10 GB free or ≥90% usage
  (matches runner enforcement); external drive unchanged (warn 90%,
  block 95%). Removes the && h.Severity == "ok" guard that was
  preventing system-drive critical messages from overriding warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 08:14:08 +01:00
admin da576deac5 TASK.md — Cross-Drive Backup Validation Fix (v0.12.5) 2026-02-18 08:10:37 +01:00
admin d160c6c06d v0.12.4 — 15 bug fixes (CRITICAL/HIGH/MEDIUM)
CRITICAL:
- C1: SetAppBackupBulk data loss + nil map panic (settings.go)
- C2: UpdateStackConfig nil Env map panic (deploy.go)
- C3: ValidateDump missing scanner.Err() check (dbdump.go)

HIGH:
- H1: nextDailyRun DST bug — use time.Date(day+1) not Add(24h)
- H2: Cache Europe/Budapest timezone with sync.Once in scheduler
- H3: settings.save() leaks .tmp file on WriteFile failure
- H4: SetNotificationPrefs nil pointer panic
- H5: appDirSize + getDirSizeBytes ignore Sscanf return value
- H6: getDirSizeBytes has no timeout — add 30s context
- H7: dbdump.go tmpFile not using defer Close
- H8: UpdateCrossDriveStatus misleading comment

MEDIUM:
- M1: Replace custom containsBytes with strings.Contains
- M2: scheduler.Every() validates interval > 0
- M3: executeJob panic recovery now sets LastRun
- M4: logPostStartStatus copies env slice before goroutine
- M5: Cache timezone in web package via getTimezone() sync.Once

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 07:50:02 +01:00
admin 93d9b474f1 v0.12.3 — Security & correctness bug fixes (33 bugs)
CRITICAL: 10 data race and security fixes — backup.go mutex coverage
(C1-C4), IsSystemDisk 12-bit major/minor (C5), /dev/ path validation
(C6), extractName traversal (C7), TargetPath/DestinationPath against
registered paths (C8-C9), ParseComposeHDDMounts Clean-before-prefix (C10).

HIGH: 17 logic/resource fixes — ValidateDump bufio.Scanner (H1), single
appDirSize() with 30s timeout (H2/H3), snapshot ID regex (H4), cross-drive
restic prune (H5), temp file order (H6), dirSizeBytes errors (H7), atomic
fstab (H8), IsDeviceMounted suffix check (H9), eMMC partition mapping (H10),
bytesCopied mutex (H11), separator-aware migrate prefix (H13), DeleteStack
error on compose-down (H14), docker 60s timeout (H16), NotificationPrefs
deep-copy (H17), wipefs warning (H18), fstab rollback on mount fail (H19).

MEDIUM: 7 code quality fixes — formatBytes dedup (M1), .tmp filter order
(M2), sizeBytes string type (M3), elapsed in message (M6), LoadLocation
fallback (M7), pathCovers separator (M10), cancelEditLabel textContent (M11).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 21:10:55 +01:00
admin 20b3a22c88 Bug hunt and TASK.md for Sonnet 2026-02-17 20:46:50 +01:00