Detect and offer to format empty (no filesystem) partitions on the system
disk. Adds IsSystemPartition() for granular per-partition safety checks
instead of blocking the entire system disk. Init wizard shows formatable
partitions with appropriate warnings. Add felhotest demo node to docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deactivated drives (Schedulable=false) now treated like disconnected for
Tier2 backups. New IsStoragePathSchedulable() checks active+connected+not
decommissioned. UI shows yellow "Cél meghajtó inaktív" badge, scheduler
skips silently with WARN log.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, removing a storage drive from the controller only marked it as
disconnected if the StoragePath entry still existed with Disconnected:true.
Drives removed entirely from storage_paths were invisible to the check,
causing Tier2 backup UI to show green "Sikeres" and scheduler to attempt
backups to a no-longer-managed destination.
New IsStoragePathKnown() method covers both cases. UI shows yellow
"Cél meghajtó leválasztva" and scheduler skips silently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add structured operational logging at INFO, WARN, and ERROR levels to
every controller module. Standardize custom prefixes ([GEO], [SCHED],
[SYNC]) to use [INFO/WARN/ERROR] [module] format. Fix misleveled logs
(WARN->ERROR for data loss scenarios, WARN->INFO for routine operations).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add detailed [DEBUG] logging to every controller module when
logging.level is set to "debug". Each module with stateful debug
uses SetDebug(bool) wired from main.go. Covers stacks, backup,
cloudflare, integrations, system, monitor, settings, scheduler,
web handlers, storage, metrics, API, selfupdate, and assets.
Also includes the app export/import (.fab bundles) feature from
v0.32.0 and its debug page integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
- 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>
After successful deploy, shows a rich info card instead of auto-redirecting
to the apps list. Includes direct app link, first steps from catalog metadata,
default credentials info, and link to settings page for password reveal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deployed-but-stopped apps were included in telemetry reports with all-zero
memory/CPU values, dragging down hub-side averages. Now isStackRunning()
filters to only running/starting/unhealthy/restarting states.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stopped/undeployed app subdomains now show a branded page instead of
Traefik 404. Deploy settings page gains start/stop/restart controls.
Dashboard shows "Megnyitás" button for running apps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously used bare "docker compose restart" which doesn't inject
env vars or pick up template changes. Now matches StartStack behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deploy API now returns immediately after validation + config save.
docker compose up -d runs in a background goroutine so the UI shows
progress during image pulls instead of blocking for 30-60s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/debug/telemetry endpoint runs full telemetry pipeline on-demand
- GetTelemetryPreview callback added to DebugCallbacks, wired in main.go
- BuildAppTelemetryForDebug() exported wrapper in report/telemetry.go
- Debug page: new collapsible section with per-app table (memory, CPU, log errors/warnings, issues) and raw JSON viewer
- Available regardless of hub configuration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New internal/metrics/telemetry.go: MetricsStore.GetContainerTelemetry()
aggregates container memory/CPU from SQLite over the last 15 min
- New internal/metrics/logscanner.go: ScanContainerLogs() scans docker logs
for errors/warnings, deduplicates via fingerprinting (strips timestamps,
replaces 6+ digit numbers, hex strings, UUIDs)
- New internal/report/telemetry.go: buildAppTelemetrySection() assembles
per-stack AppTelemetry by aggregating container metrics and log summaries
- internal/report/types.go: added AppTelemetry field to Report struct plus
AppTelemetry type with memory/CPU/log fields and LogIssue references
- internal/report/builder.go: calls buildAppTelemetrySection() in BuildReport()
- Backward-compatible: old Hub versions silently ignore app_telemetry field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deploy page, pre-start check, and deploy validation now use actual
/proc/meminfo usage instead of declared mem_request sums. New
GetMemoryMB() helper for lightweight real-time memory reads. Monitoring
page gains a stacked memory distribution bar showing per-container
usage, OS overhead, and free memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The filebrowser stack has no .env file — domain is baked into compose labels
by docker-setup.sh. The sync always bailed with a WARN and storage paths
were never applied to FileBrowser's config.yaml or docker-compose.yml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now customize the subdomain for each app during deployment
instead of using a fixed value. The deploy page shows an editable text
input with the default pre-filled and the base domain as a suffix.
New "subdomain" deploy field type with DNS-safe format validation,
reserved name blocklist, and uniqueness check across deployed stacks.
Locked after deploy — changing requires Remove + Redeploy.
Backward compatible: InjectMissingFields() auto-fills SUBDOMAIN from
.felhom.yml defaults for existing deployed apps on next sync/restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Domain field now displays subdomain.base_domain (e.g. wiki.demo-felhom.eu)
instead of just the base domain, matching the app card display.
Applies to both pre-deploy and post-deploy settings pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pre-generate domain + secret field values when deploy page loads,
so user sees actual domain and masked passwords (with reveal button)
before deploying. Same values submitted as hidden inputs → saved to app.yaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>