diff --git a/containers/adguard/adguard.config b/containers/adguard/adguard.config index 313a265..09ec974 100755 --- a/containers/adguard/adguard.config +++ b/containers/adguard/adguard.config @@ -14,6 +14,7 @@ # CFG_ADGUARD_APP_NAME=adguard CFG_ADGUARD_BACKUP=true +CFG_ADGUARD_BACKUP_STRATEGY=auto CFG_ADGUARD_COMPOSE_FILE=default CFG_ADGUARD_HEALTHCHECK=true CFG_ADGUARD_AUTHELIA=false diff --git a/containers/crowdsec/crowdsec.config b/containers/crowdsec/crowdsec.config index 7146041..d9ef1d1 100644 --- a/containers/crowdsec/crowdsec.config +++ b/containers/crowdsec/crowdsec.config @@ -19,6 +19,7 @@ CFG_CROWDSEC_HOST_SERVICE=crowdsec CFG_CROWDSEC_HOST_SERVICES=crowdsec.service,crowdsec-firewall-bouncer.service CFG_CROWDSEC_HOST_LOG_FILES="crowdsec.service|/var/log/crowdsec.log,crowdsec-firewall-bouncer.service|/var/log/crowdsec-firewall-bouncer.log" CFG_CROWDSEC_BACKUP=true +CFG_CROWDSEC_BACKUP_STRATEGY=auto CFG_CROWDSEC_MONITORING=false CFG_CROWDSEC_PROMETHEUS_LISTEN=0.0.0.0:6060 # diff --git a/containers/dashy/dashy.config b/containers/dashy/dashy.config index 178cb01..f040346 100755 --- a/containers/dashy/dashy.config +++ b/containers/dashy/dashy.config @@ -11,6 +11,7 @@ # CFG_DASHY_APP_NAME=dashy CFG_DASHY_BACKUP=true +CFG_DASHY_BACKUP_STRATEGY=auto CFG_DASHY_COMPOSE_FILE=default CFG_DASHY_HEALTHCHECK=true CFG_DASHY_AUTHELIA=false diff --git a/containers/focalboard/focalboard.config b/containers/focalboard/focalboard.config index e9a97a2..1cd00f1 100755 --- a/containers/focalboard/focalboard.config +++ b/containers/focalboard/focalboard.config @@ -11,6 +11,7 @@ # CFG_FOCALBOARD_APP_NAME=focalboard CFG_FOCALBOARD_BACKUP=true +CFG_FOCALBOARD_BACKUP_STRATEGY=auto CFG_FOCALBOARD_COMPOSE_FILE=default CFG_FOCALBOARD_HEALTHCHECK=true CFG_FOCALBOARD_AUTHELIA=false diff --git a/containers/gitea/gitea.config b/containers/gitea/gitea.config index fde8914..2b8c08d 100755 --- a/containers/gitea/gitea.config +++ b/containers/gitea/gitea.config @@ -13,6 +13,7 @@ # CFG_GITEA_APP_NAME=gitea CFG_GITEA_BACKUP=true +CFG_GITEA_BACKUP_STRATEGY=auto CFG_GITEA_COMPOSE_FILE=default CFG_GITEA_HEALTHCHECK=true CFG_GITEA_AUTHELIA=false diff --git a/containers/gluetun/gluetun.config b/containers/gluetun/gluetun.config index 5bb2e38..6631c84 100644 --- a/containers/gluetun/gluetun.config +++ b/containers/gluetun/gluetun.config @@ -12,6 +12,7 @@ # CFG_GLUETUN_APP_NAME=gluetun CFG_GLUETUN_BACKUP=true +CFG_GLUETUN_BACKUP_STRATEGY=auto CFG_GLUETUN_COMPOSE_FILE=default CFG_GLUETUN_HEALTHCHECK=true CFG_GLUETUN_AUTHELIA=false diff --git a/containers/grafana/grafana.config b/containers/grafana/grafana.config index 4f99564..849c016 100755 --- a/containers/grafana/grafana.config +++ b/containers/grafana/grafana.config @@ -14,6 +14,7 @@ CFG_GRAFANA_APP_NAME=grafana CFG_GRAFANA_REQUIRES="prometheus" CFG_GRAFANA_BACKUP=true +CFG_GRAFANA_BACKUP_STRATEGY=auto CFG_GRAFANA_COMPOSE_FILE=default CFG_GRAFANA_HEALTHCHECK=true CFG_GRAFANA_AUTHELIA=false diff --git a/containers/ipinfo/ipinfo.config b/containers/ipinfo/ipinfo.config index 67b2df3..388ce20 100755 --- a/containers/ipinfo/ipinfo.config +++ b/containers/ipinfo/ipinfo.config @@ -11,6 +11,7 @@ # CFG_IPINFO_APP_NAME=ipinfo CFG_IPINFO_BACKUP=false +CFG_IPINFO_BACKUP_STRATEGY=auto CFG_IPINFO_COMPOSE_FILE=default CFG_IPINFO_HEALTHCHECK=true CFG_IPINFO_AUTHELIA=false diff --git a/containers/jellyfin/jellyfin.config b/containers/jellyfin/jellyfin.config index daaaa95..df15d75 100755 --- a/containers/jellyfin/jellyfin.config +++ b/containers/jellyfin/jellyfin.config @@ -11,6 +11,7 @@ # CFG_JELLYFIN_APP_NAME=jellyfin CFG_JELLYFIN_BACKUP=true +CFG_JELLYFIN_BACKUP_STRATEGY=auto CFG_JELLYFIN_COMPOSE_FILE=default CFG_JELLYFIN_HEALTHCHECK=true CFG_JELLYFIN_AUTHELIA=false diff --git a/containers/jitsimeet/jitsimeet.config b/containers/jitsimeet/jitsimeet.config index b137ff9..512ca92 100755 --- a/containers/jitsimeet/jitsimeet.config +++ b/containers/jitsimeet/jitsimeet.config @@ -11,6 +11,7 @@ # CFG_JITSIMEET_APP_NAME=jitsimeet CFG_JITSIMEET_BACKUP=true +CFG_JITSIMEET_BACKUP_STRATEGY=auto CFG_JITSIMEET_COMPOSE_FILE=default CFG_JITSIMEET_HEALTHCHECK=true CFG_JITSIMEET_AUTHELIA=false diff --git a/containers/libreportal/frontend/js/components/app/apps-manager.js b/containers/libreportal/frontend/js/components/app/apps-manager.js index 183b812..bc46e6b 100755 --- a/containers/libreportal/frontend/js/components/app/apps-manager.js +++ b/containers/libreportal/frontend/js/components/app/apps-manager.js @@ -1295,6 +1295,12 @@ class AppsManager { if (!selectOptions && typeof ConfigOptions !== 'undefined' && ConfigOptions.isDropdownKey?.(cfgKey)) { selectOptions = ConfigOptions.getSelectOptions(cfgKey); } + // Backup strategy: "live" is only valid for apps we can snapshot + // consistently. Hide it elsewhere so we never offer a choice that + // would just fall back to stopping the app. + if (selectOptions && cfgKey.endsWith('_BACKUP_STRATEGY') && !this.isCurrentAppLiveCapable()) { + selectOptions = selectOptions.filter(o => String(o.value) !== 'live'); + } // Fall back to default if stored value isn't in the option list. let effectiveValue = value; if (selectOptions && selectOptions.length > 0) { @@ -2696,6 +2702,21 @@ class AppsManager { } return {}; } + + // True when the current app can be backed up live (set by the apps.json + // generator from its compose backup labels). Drives whether the per-app + // "live" backup-strategy option is offered. + isCurrentAppLiveCapable() { + if (window.apps && this.currentApp) { + const target = String(this.currentApp).toLowerCase(); + const app = window.apps.find(a => { + const slug = (a.command || '').split(' ').pop(); + return slug.toLowerCase() === target; + }); + return app?.backup_live_capable === true; + } + return false; + } /** * Collect configuration from form and format as pipe-separated string */ diff --git a/containers/libreportal/libreportal.config b/containers/libreportal/libreportal.config index 9c2d7f2..1072113 100755 --- a/containers/libreportal/libreportal.config +++ b/containers/libreportal/libreportal.config @@ -13,6 +13,7 @@ # CFG_LIBREPORTAL_APP_NAME=libreportal CFG_LIBREPORTAL_BACKUP=true +CFG_LIBREPORTAL_BACKUP_STRATEGY=auto CFG_LIBREPORTAL_COMPOSE_FILE=default CFG_LIBREPORTAL_HEALTHCHECK=true CFG_LIBREPORTAL_AUTHELIA=false diff --git a/containers/moneyapp/moneyapp.config b/containers/moneyapp/moneyapp.config index 78eb510..36b209c 100644 --- a/containers/moneyapp/moneyapp.config +++ b/containers/moneyapp/moneyapp.config @@ -13,6 +13,7 @@ CFG_MONEYAPP_APP_NAME=moneyapp CFG_MONEYAPP_REQUIRES="" CFG_MONEYAPP_BACKUP=true +CFG_MONEYAPP_BACKUP_STRATEGY=auto CFG_MONEYAPP_COMPOSE_FILE=default CFG_MONEYAPP_HEALTHCHECK=true CFG_MONEYAPP_AUTHELIA=false diff --git a/containers/ollama/ollama.config b/containers/ollama/ollama.config index b2f9e4e..340dbfb 100755 --- a/containers/ollama/ollama.config +++ b/containers/ollama/ollama.config @@ -12,6 +12,7 @@ # CFG_OLLAMA_APP_NAME=ollama CFG_OLLAMA_BACKUP=true +CFG_OLLAMA_BACKUP_STRATEGY=auto CFG_OLLAMA_COMPOSE_FILE=default CFG_OLLAMA_HEALTHCHECK=true CFG_OLLAMA_AUTHELIA=false diff --git a/containers/onlyoffice/onlyoffice.config b/containers/onlyoffice/onlyoffice.config index 4a16c0e..70eb362 100755 --- a/containers/onlyoffice/onlyoffice.config +++ b/containers/onlyoffice/onlyoffice.config @@ -11,6 +11,7 @@ # CFG_ONLYOFFICE_APP_NAME=onlyoffice CFG_ONLYOFFICE_BACKUP=true +CFG_ONLYOFFICE_BACKUP_STRATEGY=auto CFG_ONLYOFFICE_COMPOSE_FILE=default CFG_ONLYOFFICE_HEALTHCHECK=true CFG_ONLYOFFICE_AUTHELIA=false diff --git a/containers/pihole/pihole.config b/containers/pihole/pihole.config index fb9bffd..bc2a697 100755 --- a/containers/pihole/pihole.config +++ b/containers/pihole/pihole.config @@ -12,6 +12,7 @@ # CFG_PIHOLE_APP_NAME=pihole CFG_PIHOLE_BACKUP=true +CFG_PIHOLE_BACKUP_STRATEGY=auto CFG_PIHOLE_COMPOSE_FILE=default CFG_PIHOLE_HEALTHCHECK=true CFG_PIHOLE_AUTHELIA=false diff --git a/containers/prometheus/prometheus.config b/containers/prometheus/prometheus.config index e8a6e9b..75d648e 100755 --- a/containers/prometheus/prometheus.config +++ b/containers/prometheus/prometheus.config @@ -11,6 +11,7 @@ # CFG_PROMETHEUS_APP_NAME=prometheus CFG_PROMETHEUS_BACKUP=true +CFG_PROMETHEUS_BACKUP_STRATEGY=auto CFG_PROMETHEUS_COMPOSE_FILE=default CFG_PROMETHEUS_HEALTHCHECK=true CFG_PROMETHEUS_AUTHELIA=false diff --git a/containers/searxng/searxng.config b/containers/searxng/searxng.config index c8f3177..c7d1496 100755 --- a/containers/searxng/searxng.config +++ b/containers/searxng/searxng.config @@ -11,6 +11,7 @@ # CFG_SEARXNG_APP_NAME=searxng CFG_SEARXNG_BACKUP=false +CFG_SEARXNG_BACKUP_STRATEGY=auto CFG_SEARXNG_COMPOSE_FILE=default CFG_SEARXNG_HEALTHCHECK=true CFG_SEARXNG_AUTHELIA=false diff --git a/containers/speedtest/speedtest.config b/containers/speedtest/speedtest.config index 619b82b..8439980 100755 --- a/containers/speedtest/speedtest.config +++ b/containers/speedtest/speedtest.config @@ -11,6 +11,7 @@ # CFG_SPEEDTEST_APP_NAME=speedtest CFG_SPEEDTEST_BACKUP=false +CFG_SPEEDTEST_BACKUP_STRATEGY=auto CFG_SPEEDTEST_COMPOSE_FILE=default CFG_SPEEDTEST_HEALTHCHECK=false CFG_SPEEDTEST_AUTHELIA=false diff --git a/containers/traefik/traefik.config b/containers/traefik/traefik.config index c7f0aa2..0403583 100755 --- a/containers/traefik/traefik.config +++ b/containers/traefik/traefik.config @@ -12,6 +12,7 @@ # CFG_TRAEFIK_APP_NAME=traefik CFG_TRAEFIK_BACKUP=true +CFG_TRAEFIK_BACKUP_STRATEGY=auto CFG_TRAEFIK_COMPOSE_FILE=default CFG_TRAEFIK_HEALTHCHECK=true CFG_TRAEFIK_AUTHELIA=false diff --git a/containers/unbound/unbound.config b/containers/unbound/unbound.config index ffa7366..e86527a 100755 --- a/containers/unbound/unbound.config +++ b/containers/unbound/unbound.config @@ -12,6 +12,7 @@ # CFG_UNBOUND_APP_NAME=unbound CFG_UNBOUND_BACKUP=true +CFG_UNBOUND_BACKUP_STRATEGY=auto CFG_UNBOUND_COMPOSE_FILE=default CFG_UNBOUND_HEALTHCHECK=true CFG_UNBOUND_AUTHELIA=false diff --git a/containers/wireguard/wireguard.config b/containers/wireguard/wireguard.config index fe31a2b..3252d4d 100755 --- a/containers/wireguard/wireguard.config +++ b/containers/wireguard/wireguard.config @@ -13,6 +13,7 @@ CFG_WIREGUARD_APP_NAME=wireguard CFG_WIREGUARD_SUBNET= CFG_WIREGUARD_BACKUP=true +CFG_WIREGUARD_BACKUP_STRATEGY=auto CFG_WIREGUARD_COMPOSE_FILE=default CFG_WIREGUARD_HEALTHCHECK=true CFG_WIREGUARD_AUTHELIA=false diff --git a/scripts/webui/data/generators/apps/webui_config.sh b/scripts/webui/data/generators/apps/webui_config.sh index 0cb4926..bbf7d40 100644 --- a/scripts/webui/data/generators/apps/webui_config.sh +++ b/scripts/webui/data/generators/apps/webui_config.sh @@ -181,6 +181,18 @@ EOF services_json="${services_json%,}" fi + # Live-backup capability: the app can be snapshotted live without + # downtime only if it declares a dumpable database, or is explicitly + # marked live-safe. The app config UI uses this to hide the "live" + # strategy option where it would be unsafe. + local backup_live_capable="false" + if [[ -f "$compose_file" ]]; then + if grep -qE '^[[:space:]]*libreportal\.backup\.db[[:space:]]*:' "$compose_file" 2>/dev/null \ + || grep -qE '^[[:space:]]*libreportal\.backup\.live[[:space:]]*:[[:space:]]*["'\'']?true' "$compose_file" 2>/dev/null; then + backup_live_capable="true" + fi + fi + if [[ "$first_app" == false ]]; then echo " }," >> "$temp_file" fi @@ -197,6 +209,7 @@ EOF fields+=(" \"category\": \"$mapped_category\"") [[ -n "$mapped_categories_json" ]] && fields+=(" \"categories\": $mapped_categories_json") fields+=(" \"installed\": $is_installed") + fields+=(" \"backup_live_capable\": $backup_live_capable") [[ -n "$url" ]] && fields+=(" \"url\": \"$url\"") [[ -n "$description" ]] && fields+=(" \"description\": \"$description\"") [[ -n "$icon_file" ]] && fields+=(" \"icon\": \"icons/apps/$icon_file\"") diff --git a/scripts/webui/data/generators/categories/webui_create_app_field_mappings.sh b/scripts/webui/data/generators/categories/webui_create_app_field_mappings.sh index 4025aa6..568ff08 100755 --- a/scripts/webui/data/generators/categories/webui_create_app_field_mappings.sh +++ b/scripts/webui/data/generators/categories/webui_create_app_field_mappings.sh @@ -163,7 +163,7 @@ PORTEOF "category": "advanced", "label": "Backup Strategy", "type": "select", - "tooltip": "How this app is quiesced before its backup snapshot. Automatic picks the safest zero-downtime method available (a live, consistent database dump for this app), and falls back to stopping the app if a live dump ever fails. Only shown for apps that can be backed up live.", + "tooltip": "How this app is quiesced before its backup snapshot. Automatic picks the safest zero-downtime method available and falls back to stopping the app if a live dump ever fails. The Live option appears only for apps that can be backed up consistently without stopping (a database we can dump, or a static app marked live-safe).", "advanced": true, "options": [ {"value": "auto", "label": "Automatic (recommended)"},