From 7c2800777907e5d9f068950c3843d16f0b0ec7d8 Mon Sep 17 00:00:00 2001 From: librelad Date: Fri, 12 Jun 2026 22:33:23 +0100 Subject: [PATCH] refactor(config): updater knobs -> configs/webui/webui_updater; fix config heal/reconcile gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the WebUI-updater settings out of general_terminal into their own advanced webui-category file (webui_logs precedent): new configs/webui/webui_updater holds CFG_UPDATER_SCAN_INTERVAL and the migrated CFG_HOTFIX_AUTO, listed in webui/.category. The move only reaches existing installs if the config convergence machinery works, and three pieces of it silently didn't: - checkConfigFilesMissingFiles walked a stale hardcoded category list ('general features network' — features doesn't exist; webui/backup/ security never healed). Derive the categories from the template tree instead, and heal .category metadata too: copy it when absent and merge missing SUBCATEGORY_ORDER entries when present, so healed files actually appear in the WebUI Config editor. core_categories removed. - Option reconciliation never touched ANY nested config file: configs_dir carries a trailing slash, so rel stripping missed ('configs//'), the template lookup failed, and reconcileConfigFile early-returned for every file. Strip the slash before matching. - reconcileConfigFile's AUTO_DELETE=false branch read a never-populated live_line array, losing the dropped keys it promised to keep. Populate it alongside live_value. Also exclude *.bak from config sourcing (reconciliation writes .bak next to live configs — now that it runs, sourcing backups would resurrect deleted keys), and add 'libreportal config check' as a non-interactive front door to the converge pass (was only reachable via install flows and the interactive menu). Co-Authored-By: Claude Fable 5 Signed-off-by: librelad --- configs/general/general_terminal | 2 - configs/webui/.category | 2 +- configs/webui/webui_updater | 5 ++ .../commands/config/cli_config_commands.sh | 10 +++ .../cli/commands/config/cli_config_header.sh | 3 + scripts/config/core/config_check_missing.sh | 83 +++++++++++++------ .../variables/config_missing_variables.sh | 6 +- .../core/variables/config_scan_variables.sh | 3 +- scripts/source/loading/scan_files.sh | 2 +- variables.sh | 1 - 10 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 configs/webui/webui_updater diff --git a/configs/general/general_terminal b/configs/general/general_terminal index 4520c05..e28bb85 100755 --- a/configs/general/general_terminal +++ b/configs/general/general_terminal @@ -2,8 +2,6 @@ # Terminal - System utilities and advanced settings **ADVANCED** # ================================================================================ CFG_UPDATER_CHECK=60 # Update Check Interval - Hours between system update checks -CFG_HOTFIX_AUTO=security-breakage # Hotfix Auto-Apply - Which signed hotfix severities apply automatically on the update check [security-breakage|all|off] -CFG_UPDATER_SCAN_INTERVAL=30 # App Scan Interval - Minutes between automatic app update/CVE/improvement scans (0 disables) CFG_SWAPFILE_SIZE=2G # Swap File Size - Size of swap file for memory management CFG_GENERATED_PASS_LENGTH=14 # Password Length - Length for auto generated passwords CFG_GENERATED_USER_LENGTH=8 # Username Length - Length for auto generated usernames diff --git a/configs/webui/.category b/configs/webui/.category index 30bf00c..3c3fb27 100755 --- a/configs/webui/.category +++ b/configs/webui/.category @@ -2,4 +2,4 @@ TITLE=WebUI DESCRIPTION=Web interface settings and preferences ICON=webui ORDER=2 -SUBCATEGORY_ORDER=webui_logins,webui_logs +SUBCATEGORY_ORDER=webui_logins,webui_logs,webui_updater diff --git a/configs/webui/webui_updater b/configs/webui/webui_updater new file mode 100644 index 0000000..77029cb --- /dev/null +++ b/configs/webui/webui_updater @@ -0,0 +1,5 @@ +# ================================================================================ +# WebUI Updater - Automatic app update, CVE & improvement scanning **ADVANCED** +# ================================================================================ +CFG_UPDATER_SCAN_INTERVAL=30 # App Scan Interval - Minutes between automatic app update/CVE/improvement scans. 0 disables. +CFG_HOTFIX_AUTO=security-breakage # Hotfix Auto-Apply - Which signed hotfix severities apply automatically on the update check [security-breakage|all|off] diff --git a/scripts/cli/commands/config/cli_config_commands.sh b/scripts/cli/commands/config/cli_config_commands.sh index 18f8709..0af0bd9 100755 --- a/scripts/cli/commands/config/cli_config_commands.sh +++ b/scripts/cli/commands/config/cli_config_commands.sh @@ -14,6 +14,16 @@ cliHandleConfigCommands() configUpdateBatch "$encoded_pairs" ;; + "check") + # Converge live configs on the install templates: copy any missing + # files / categories (and surface them to the WebUI Config editor + # via .category), then reconcile the options inside each file — + # add new keys, drop removed ones, refresh comments, keep the + # user's values. Read-and-converge; safe to run any time. + checkConfigFilesMissingFiles + checkConfigFilesMissingVariables true + ;; + "") cliShowConfigHelp ;; diff --git a/scripts/cli/commands/config/cli_config_header.sh b/scripts/cli/commands/config/cli_config_header.sh index 91f3bc4..6ca9bba 100755 --- a/scripts/cli/commands/config/cli_config_header.sh +++ b/scripts/cli/commands/config/cli_config_header.sh @@ -5,6 +5,9 @@ cliShowConfigHelp() echo "" echo "Available Config Commands:" echo "" + echo " libreportal config check - Converge live configs on the install templates:" + echo " copy missing files, then reconcile options in each" + echo " (add new keys, drop removed ones, keep your values)" echo " libreportal config update '' - Apply a batch of CFG_KEY=VALUE pairs" echo " is pipe-separated; literal '|' in" echo " values must be URL-encoded as %7C." diff --git a/scripts/config/core/config_check_missing.sh b/scripts/config/core/config_check_missing.sh index 5dcda3f..65b0031 100755 --- a/scripts/config/core/config_check_missing.sh +++ b/scripts/config/core/config_check_missing.sh @@ -11,31 +11,66 @@ checkConfigFilesMissingFiles() local missing_files_count=0 local found_files_count=0 - # Check for core config categories in the new structure - for category in "${core_categories[@]}"; do - local install_category_dir="$install_configs_dir/$category" - local target_category_dir="$configs_dir/$category" - - if [ -d "$install_category_dir" ]; then - # Create target category directory if it doesn't exist - if [ ! -d "$target_category_dir" ]; then - runInstallOp mkdir -p "$target_category_dir" - fi - - # Check each file in the install category directory - for install_file in "$install_category_dir"/*; do - if [ -f "$install_file" ] && [[ ! "$install_file" =~ \.category$ ]]; then - local filename=$(basename "$install_file") - local target_file="$target_category_dir/$filename" - - if [ ! -f "$target_file" ]; then - # Copy missing file from install directory - runInstallOp cp "$install_file" "$target_file" - ((missing_files_count++)) - fi - ((found_files_count++)) + # Walk every category the install templates define (configs//), + # derived from the template tree itself — so a new category, or one a + # hardcoded list would miss (webui/backup/security were), heals onto + # existing installs automatically. + local install_category_dir category target_category_dir + for install_category_dir in "$install_configs_dir"*/; do + [ -d "$install_category_dir" ] || continue + category="$(basename "$install_category_dir")" + target_category_dir="${configs_dir%/}/$category" + + # Create target category directory if it doesn't exist + if [ ! -d "$target_category_dir" ]; then + runInstallOp mkdir -p "$target_category_dir" + fi + + # Check each file in the install category directory + local install_file + for install_file in "$install_category_dir"*; do + if [ -f "$install_file" ] && [[ ! "$install_file" =~ \.category$ ]]; then + local filename=$(basename "$install_file") + local target_file="$target_category_dir/$filename" + + if [ ! -f "$target_file" ]; then + # Copy missing file from install directory + runInstallOp cp "$install_file" "$target_file" + ((missing_files_count++)) fi - done + ((found_files_count++)) + fi + done + + # Heal the category's .category metadata too: copy it when absent (a + # brand-new category is invisible to the WebUI Config editor without + # it), and when present merge in any template SUBCATEGORY_ORDER entries + # it lacks — the editor only renders files listed there, so a healed + # config file would otherwise ship hidden. User-kept order and any + # extra local entries are preserved; new entries append. + local tmpl_cat="${install_category_dir}.category" + local live_cat="$target_category_dir/.category" + if [ -f "$tmpl_cat" ]; then + if [ ! -f "$live_cat" ]; then + runInstallOp cp "$tmpl_cat" "$live_cat" + else + local tmpl_order live_order merged entry _entries + tmpl_order="$(grep -m1 '^SUBCATEGORY_ORDER=' "$tmpl_cat" 2>/dev/null | cut -d= -f2-)" + live_order="$(runInstallOp grep -m1 '^SUBCATEGORY_ORDER=' "$live_cat" 2>/dev/null | cut -d= -f2-)" + merged="$live_order" + IFS=',' read -ra _entries <<< "$tmpl_order" + for entry in "${_entries[@]}"; do + [ -n "$entry" ] || continue + [[ ",$merged," == *",$entry,"* ]] || merged="${merged:+$merged,}$entry" + done + if [ -n "$merged" ] && [ "$merged" != "$live_order" ]; then + if runInstallOp grep -q '^SUBCATEGORY_ORDER=' "$live_cat" 2>/dev/null; then + runInstallOp sed -i "s/^SUBCATEGORY_ORDER=.*/SUBCATEGORY_ORDER=$merged/" "$live_cat" + else + printf 'SUBCATEGORY_ORDER=%s\n' "$merged" | runInstallOp tee -a "$live_cat" >/dev/null + fi + fi + fi fi done diff --git a/scripts/config/core/variables/config_missing_variables.sh b/scripts/config/core/variables/config_missing_variables.sh index 30a106d..c23bfcc 100755 --- a/scripts/config/core/variables/config_missing_variables.sh +++ b/scripts/config/core/variables/config_missing_variables.sh @@ -8,7 +8,11 @@ checkLibrePortalConfigFilesMissingVariables() while IFS= read -r live; do [[ -f "$live" ]] || continue fn=$(basename "$live") - rel="${live#"$configs_dir"/}" + # configs_dir carries a trailing slash (paths.sh) — strip it before + # appending one, or the prefix never matches ("configs//"), rel stays + # the absolute path, the template lookup misses, and every nested + # config file silently skips reconciliation. + rel="${live#"${configs_dir%/}"/}" remote="$install_configs_dir$fn" [[ -f "$remote" ]] || remote="$install_configs_dir$rel" reconcileConfigFile "$live" "$remote" diff --git a/scripts/config/core/variables/config_scan_variables.sh b/scripts/config/core/variables/config_scan_variables.sh index b38dfb9..f10a3d2 100755 --- a/scripts/config/core/variables/config_scan_variables.sh +++ b/scripts/config/core/variables/config_scan_variables.sh @@ -41,13 +41,14 @@ reconcileConfigFile() [[ -f "$live" ]] || return 0 runInstallOp test -s "$template" 2>/dev/null || return 0 - declare -A live_value emitted + declare -A live_value live_line emitted local line key while IFS= read -r line; do if [[ "$line" =~ ^(CFG_[A-Za-z0-9_]+)= ]]; then key="${BASH_REMATCH[1]}" _reconcileSplitValueComment "$line" live_value["$key"]="$_reconcile_value" + live_line["$key"]="$line" fi done < <(runInstallOp cat "$live") diff --git a/scripts/source/loading/scan_files.sh b/scripts/source/loading/scan_files.sh index 9643c24..c1145c4 100755 --- a/scripts/source/loading/scan_files.sh +++ b/scripts/source/loading/scan_files.sh @@ -26,7 +26,7 @@ sourceScanFiles() # echo "$load_type NEW FILE $file" fi fi - done < <(find "$folder_dir" -maxdepth 2 -type f ! -name "*.category" ! -name "config_*" ! -name ".*" -print0) + done < <(find "$folder_dir" -maxdepth 2 -type f ! -name "*.category" ! -name "config_*" ! -name ".*" ! -name "*.bak" -print0) # Per-location backup configs live nested at depth 3 # (configs/backup/locations//location.config) — source them via diff --git a/variables.sh b/variables.sh index c056384..9b2f080 100755 --- a/variables.sh +++ b/variables.sh @@ -54,7 +54,6 @@ run_file=run.txt # Configs update_done=false config_file_wireguard=config_wireguard -core_categories=("general" "features" "network") # Menu menu_number=0 \ No newline at end of file