Normal storage add/remove no longer nukes the FileBrowser database volume.
A .fb-reset flag file is written during restore and consumed on next startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FileBrowser Quantum caches user source preferences in its SQLite
database. After a restore, the config.yaml gets correct sources but
the database still references the old "srv" source from docker-setup.sh
initial install. Now SyncFileBrowserMounts() detects when sources
changed and runs docker compose down -v to reset the database before
recreating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After restore, the setup server (port 8081) exits and the main
controller restarts on port 8080. waitForRestart() now polls port
8080 using no-cors mode and redirects there when it responds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The exec > >(tee ...) process substitution has a race condition where
the main shell exits before tee finishes printing. The print_summary
output was written to the log file but never displayed on terminal.
Added sleep 0.5 to let tee flush.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restore flow now calls MountDrivesFromLayout() after writing config,
which mounts drives by UUID and adds fstab entries. Previously drives
from the infra backup were never mounted, causing "Adattároló nem
elérhető" warnings.
Post-restore redirect now polls until the controller responds instead
of using a fixed 5-second timeout that was too short for container
restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hub: GFS retention (7d/4w/3m, ~14 versions) in new infra_backup_versions
table. Recovery endpoint supports ?version=ID. New /versions API endpoint.
Dashboard shows backup history.
Controller: local drive backups rotated into history/ (last 5 versions).
Setup wizard shows version picker for Hub restores when multiple versions
exist. Scan results enriched with app names, disk count, history badge.
Local restore supports historical versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename fails with EBUSY on Docker bind-mounted files (e.g. controller.yaml).
Fall back to os.WriteFile when os.Rename fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both autoProcessHubRestore and processHubRestore rendered the progress
page (setup_restore_exec) without starting the executeHubRestore()
goroutine, causing the template to poll forever showing "Indítás...".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docker-setup.sh --hub-customer now generates a minimal controller.yaml
(no customer.id) instead of installing full hub config, triggering the
setup wizard on first run. Hub credentials are passed via env vars
(FELHOM_SETUP_CUSTOMER_ID, FELHOM_SETUP_PASSWORD) so the wizard
auto-fills and auto-processes Hub API calls.
Welcome page shows three options in hub mode: restore from Hub (primary),
restore from local drives, or fresh install. On error, falls back to
manual form with error displayed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace traffic light colors (green/yellow/red) with brand palette:
- Primary actions: blue gradient
- Secondary actions: ghost/outline
- Destructive actions: ghost with red hover (modals keep filled red)
- Running cards: blue glow instead of green border
- Bottom-aligned buttons via flexbox column layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
StorageUrl was missing trailing slash — NC's OO connector does string
replacement of server URL (ending with /) with StorageUrl, so without
trailing slash "apps/" merges into hostname producing "nextcloudapps".
Also add "nextcloud" to NC trusted_domains so OO Document Server's
internal callbacks (Host: nextcloud) are not rejected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers architecture, state management, full lifecycle (6 steps),
both handlers with detailed occ commands and config patching,
ReapplyConfigForTarget, force-recreate rationale, Traefik middleware
for OO HTTPS proxy, UI on deploy page, wiring in main.go, and
corrected API JSON field name (enabled not enable).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SyncFileBrowserMounts: use --force-recreate so FB always picks up
config.yaml changes (bind mount not detected by docker compose up)
- OnlyOffice compose template: add Traefik middleware to forward
X-Forwarded-Proto=https (fixes mixed content errors in browser)
- Nextcloud handler: add StorageUrl=http://nextcloud for internal
file download callbacks from OO Document Server
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SyncFileBrowserMounts regenerates config.yaml from scratch, overwriting
any integration config. The old approach used an async OnStackStart hook
after container restart, which failed due to timing issues (stack state
not yet refreshed).
New approach: ReapplyConfigForTarget() writes integration config
synchronously after config generation but before container restart,
with a no-op RestartStack since the caller handles restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Protected stacks like FileBrowser have no app.yaml so Deployed=false,
which caused the URL link condition to fail. Now also shows the URL
when the stack is protected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User feedback: these settings belong on the Beállítások (settings) page,
not the app description/details page. Moves both sections from app_info.html
to deploy.html and rewires data in deployHandler instead of appDetailHandler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Containers with Docker healthchecks show 'starting' state for
~30s after compose up. The container is connectable, just hasn't
passed its healthcheck yet. Accept both running and starting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The goroutine fires immediately but needs the stack manager's
state to reflect 'running' before checking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generic integration system for connecting deployed apps via toggle UI.
First handlers: OnlyOffice→FileBrowser (config.yaml patch) and
OnlyOffice→Nextcloud (occ CLI). Lifecycle hooks auto-suspend on
stop and re-apply on start.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Clear HealthProbe on StartStack/RestartStack so stale unhealthy state
isn't re-applied by RefreshStatus
- Use 10s probe interval for unhealthy/new stacks (nil HealthProbe probes
immediately on next tick), switch to normal 5m interval once healthy
- Scheduler frequency 1m → 10s to support fast probing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug fixes:
- Add applyEnvOverrides to LoadFromBytes (M05)
- Set state=failed on compose-up failure in selfupdate (M16)
- Clamp usableMB to min 0 in memory check (M22)
- Remove "manual" schedule from triggerAllCrossBackups (M23)
- Add mmcblk device handling for partition paths (M21)
- Fix stripPartition for mmcblk devices (L25)
- Fix TruncateStr for UTF-8 and negative maxLen (L05/L06)
- Fix AllDone to return false for empty restore plans (L14)
- Fix PushOnce to return actual errors (L39)
- Restore pending events on save failure in DrainPendingEvents (M03)
- Add duplicate check in AddStoragePath (M04)
- Call CleanupTempMounts after drive scan (H13)
- Log SetStep save errors (M25)
Hardening:
- Guard scheduler Start() against double-start (M14)
- Acquire mutex in scheduler Stop() before reading cancel (L24)
- Cap log lines parameter to 10000 (L31)
- Require POST for logout (L32)
- Use sync.Once for Server.Close() (L49)
- Panic on crypto/rand.Read failure in setup CSRF (L40)
- Validate Bearer token against Hub API key in CSRF (H16 fix)
- Replace custom hasPrefix with strings.HasPrefix (L13)
- Replace simpleHash with crc32.ChecksumIEEE (L48)
Cleanup:
- Remove dead imageName function (L02)
- Remove dead detectHostIPViaRoute function (L03)
- Rename shadowed copy variable to cp (L07)
- Copy DefaultEnabledEvents in GetNotificationPrefs early return (L09)
- Update BUGHUNT.md with comprehensive audit results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Concurrency fixes:
- Deep-copy stacks in GetStack/GetStacks to prevent shared state mutation (C04)
- Add per-state mutex to watchdog pathProbeState (C05)
- Guard MetricsCollector.Start() with sync.Once against double-start (C06)
- Hold diskJobMu across entire raw mount operation (C07)
- Add mutex to SetEncryptionKey (C08), MigrateEncryption write lock (H03)
- Use sync.Once for sync.Stop() channel close (H08)
- Set syncing=true before releasing lock in TriggerSync (H09)
- Deep-copy lastDBDump/lastBackup in GetFullStatus (H11)
- Add WaitGroup for stderr goroutine in MigrateDrive (H19)
- Add mutex to SetBackupRunningCheck (M18)
Security fixes:
- Validate Bearer token against Hub API key in CSRF middleware (H16)
- Validate backup paths start with expected prefix in RemoveStack (M12)
- Guard uuid[:8] slice with length check (H20)
- Parse fstab fields exactly for mount target matching (H21)
Bug fixes:
- Use decrypted env vars for compose deploy (C01)
- Log decrypt failures in DecryptMap instead of swallowing (C02)
- Move Deployed=false inside lock in runComposeDeploy (C03)
- Fix activeDrives() to skip disconnected drives (H02)
- Fix Snapshot() stderr extraction from exec.ExitError (H01)
- Check unlockCmd.Run() error in restic (H01)
- Buffer template rendering via bytes.Buffer (H07)
- Thread context.Context through cloudflare client (H10)
- Fix leaf-name collision detection in cross-drive backup (H15)
- Add nil check for crossDriveRunner (H17)
- Use strings.TrimSpace instead of slice on command output (H18)
- Make SaveAppConfig atomic with write-to-tmp+rename (H04)
- Pass encKey on deploy failure SaveAppConfig (H05)
- Fix IPv6 address format in TCP health probe
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GeoRestrictionReport to report types and builder, so Hub can
display geo-blocking status on customer detail pages
- Update all 5 BuildReport() call sites with new geoRestriction param
- Add /api/geo/ to selfUpdateAuthMiddleware (Hub Bearer token auth)
- Replace embedded logo SVG with updated logo.svg (white text variant)
- Add FelhomFaviconSVG constant + /static/favicon.svg route
- Update layout.html and catchall.html favicon links
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cloudflare Free plan doesn't support custom response body in block
rules. Use plain block action which returns CF's default 403 page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add country-based access control managed through the Settings page.
Global allow-list with per-app overrides, searchable country selector,
automatic sync to Cloudflare WAF on settings change / deploy / remove,
plus periodic 6-hour verification.
New package: internal/cloudflare/ (client, zone, waf, countries, geosync)
New API: /api/geo/* (6 endpoints) + /api/stacks/{name}/geo/override
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add network-level health probing from the controller to deployed apps.
The controller probes containers over the shared Docker network and
overrides stack state to "unhealthy" if the service isn't responding.
Three probe types: http (any response = alive), api (validates status
code and body content), tcp (port reachability). Configured per-app
via healthcheck: section in .felhom.yml. Runs every minute, per-app
interval defaults to 5 minutes.
This replaces Docker-level healthchecks for distroless images (e.g.
Vikunja) that lack shell utilities, and complements existing Docker
healthchecks for other apps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logo handler now checks Hub-synced assets first, falling back to
embedded SVG. Added SVG favicon to layout and catchall templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Catalog sync could fail permanently if the container was killed mid-fetch,
leaving behind .git/shallow.lock (or index.lock, HEAD.lock). Now cleaned
up automatically before each git fetch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Passwords and secrets from deploy fields (type: password/secret) are now
encrypted at rest in app.yaml using a per-node 32-byte key. Values stored
as ENC:base64(nonce+ciphertext), decrypted transparently for docker-compose
and web UI. Key included in infra backup bundle for disaster recovery.
Existing plaintext values migrated automatically on startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix password fields showing empty after deployment: now reads value
from DeployedFieldValues (app.yaml env) instead of only .Default
- Post-deploy card: passwords are masked with reveal + copy buttons
instead of showing plaintext
- Settings page: deployed password fields show "initial password" hint
explaining the value won't update if changed in the app
- Hide Generate button on settings page for already-deployed apps
- Added EMAIL to username-detection heuristic for credential display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Password deploy fields now use type=password (masked by default)
- Added eye toggle button to reveal/hide password and confirm fields
- Added confirmation field below each password input
- Generate button fills both password and confirmation fields
- Form validation checks password confirmation matches before deploy
- Confirmation field only shown for new deployments (not already deployed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of a generic "default creds" message, the post-deploy card now
reads actual username/password values from the deploy form fields and
displays them in a table. Filters out internal DB passwords and secret
keys, showing only user-facing credentials (admin user, admin password).
Falls back to metadata defaultCreds for apps without typed deploy fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Naplóviewer was showing relative times like '3586mp' (seconds ago)
which were also negative due to timezone mismatch — parseLine used
time.Parse (UTC) but log.LstdFlags outputs local time. Now:
- parseLine uses time.ParseInLocation with time.Local
- fmtTime JS shows absolute HH:MM:SS (or MM-DD HH:MM:SS for old entries)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>