LibrePortal/containers/gluetun/scripts/gluetun_route_apps.sh
librelad 2d24a764a8 refactor(storage): route elevation tests and the WebUI tree through paths.sh
Two mechanical sweeps, no behaviour change on a single-root install.

The 14 `[[ "$p" == "$containers_dir"* ]]` prefix tests that decide
manager-vs-container-user elevation become pathIsContainerData, so a file
on a second storage root is no longer misclassified as manager-owned —
which would have written it with the wrong owner and failed later, far
from the cause. The 65 references to the WebUI's own tree become
webuiDir(), which is pinned to the primary root by design.

Two traps found while doing it:

run_privileged.sh is sourced directly by init.sh without paths.sh, so it
needs a fallback. Defining one named pathIsContainerData was wrong:
generate_function_manifest.sh indexes top-level definitions, and the
resulting autoload stub would have shadowed the real multi-root
implementation with the primary-only fallback — silently classifying
every file on a second disk as manager-owned, which is exactly the bug
this sweep exists to prevent. Renamed to _runCfgIsContainerPath, which
delegates when the real one is loaded.

setup_lock.sh built its path in a top-level assignment, so it was
evaluated at source time and needed the file flagged eager. Made it a
function instead: the path resolves on call, and the file drops off
LP_EAGER_FILES entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 04:04:19 +01:00

100 lines
4.0 KiB
Bash

#!/bin/bash
# Walks installed apps and offers to flip CFG_<APP>_NETWORK to gluetun for each
# eligible one. Honors the curated category list + CFG_REQUIREMENT_GLUETUN_FOR_ALL
# override flag. Skips itself, Traefik, LibrePortal, fail2ban.
gluetunRouteExistingAppsPrompt()
{
local categories_file="$(webuiDir)/frontend/data/apps/gluetun-eligible-categories.json"
local override="${CFG_REQUIREMENT_GLUETUN_FOR_ALL:-false}"
if ! command -v sqlite3 >/dev/null 2>&1 || [[ ! -f "$docker_dir/$db_file" ]]; then
isNotice "Database not available; skipping per-app routing prompt."
return 0
fi
local allow_csv=""
if [[ -f "$categories_file" ]] && command -v jq >/dev/null 2>&1; then
allow_csv=$(jq -r '.categories | join(",")' "$categories_file" 2>/dev/null | tr '[:upper:]' '[:lower:]')
fi
local installed
installed=$(runInstallOp sqlite3 "$docker_dir/$db_file" \
"SELECT name FROM apps WHERE status = 1 AND name NOT IN ('gluetun','libreportal','traefik','fail2ban') ORDER BY name;" 2>/dev/null)
if [[ -z "$installed" ]]; then
isNotice "No other apps installed yet. Skipping."
return 0
fi
local eligible=()
local app # local: bash is dynamically scoped, and a while-read loop
# leaves an undeclared name EMPTY in the CALLER's scope at EOF.
while IFS= read -r app; do
[[ -z "$app" ]] && continue
local cfg_file="${containers_dir}${app}/${app}.config"
[[ -f "$cfg_file" ]] || continue
local category
category=$(grep -E "^CFG_${app^^}_CATEGORY=" "$cfg_file" 2>/dev/null \
| head -1 | cut -d'=' -f2 | tr -d '"' | tr '[:upper:]' '[:lower:]')
local is_eligible="false"
if [[ "$override" == "true" ]]; then
is_eligible="true"
elif [[ -n "$allow_csv" ]]; then
IFS=',' read -ra cats <<< "$allow_csv"
for c in "${cats[@]}"; do
if [[ "$c" == "$category" ]]; then is_eligible="true"; break; fi
done
fi
[[ "$is_eligible" == "true" ]] && eligible+=("$app")
done <<< "$installed"
if [[ ${#eligible[@]} -eq 0 ]]; then
isNotice "No eligible installed apps. (Enable CFG_REQUIREMENT_GLUETUN_FOR_ALL to expose every app.)"
return 0
fi
echo " The following installed apps can be routed through Gluetun:"
for app in "${eligible[@]}"; do
local cfg_file="${containers_dir}${app}/${app}.config"
local current
current=$(grep -E "^CFG_${app^^}_NETWORK=" "$cfg_file" 2>/dev/null | head -1 | cut -d'=' -f2)
[[ -z "$current" ]] && current="default"
echo " - ${app} (current: ${current})"
done
echo ""
isQuestion "Apply gluetun routing now? Tick each app to flip. (y/N): "
local resp
read -rp "" resp
echo ""
case "$resp" in
[yY]*) ;;
*) isNotice "Skipped. You can apply later via the WebUI gluetun page or per-app config."; return 0 ;;
esac
for app in "${eligible[@]}"; do
local cfg_file="${containers_dir}${app}/${app}.config"
local current
current=$(grep -E "^CFG_${app^^}_NETWORK=" "$cfg_file" 2>/dev/null | head -1 | cut -d'=' -f2)
[[ -z "$current" ]] && current="default"
isQuestion " Route '$app' through gluetun? (current: $current) [y/n/skip]: "
local r
read -rp "" r
case "$r" in
[yY]*)
updateConfigOption "CFG_${app^^}_NETWORK" "gluetun" "$cfg_file"
isSuccessful " Set CFG_${app^^}_NETWORK=gluetun. Re-running install for $app..."
dockerInstallApp "$app"
;;
[nN]*)
if [[ "$current" == "gluetun" ]]; then
updateConfigOption "CFG_${app^^}_NETWORK" "default" "$cfg_file"
isSuccessful " Reverted $app to default network. Re-running install..."
dockerInstallApp "$app"
fi
;;
*) ;;
esac
done
}