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>
This commit is contained in:
librelad 2026-08-24 04:04:19 +01:00
parent db04f8f000
commit 2d24a764a8
65 changed files with 114 additions and 83 deletions

View File

@ -5,7 +5,7 @@
# mount set takes effect. # mount set takes effect.
crowdsecToggleLibrePortalLogMounts() { crowdsecToggleLibrePortalLogMounts() {
local mode="$1" local mode="$1"
local compose="${containers_dir}libreportal/docker-compose.yml" local compose="$(webuiDir)/docker-compose.yml"
[[ -f "$compose" ]] || return 0 [[ -f "$compose" ]] || return 0
case "$mode" in case "$mode" in
@ -26,7 +26,7 @@ crowdsecToggleLibrePortalLogMounts() {
if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^libreportal-service$'; then if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^libreportal-service$'; then
isNotice "Recreating libreportal so log mount toggle takes effect..." isNotice "Recreating libreportal so log mount toggle takes effect..."
( cd "${containers_dir}libreportal" && runAsManager docker compose up -d >/dev/null 2>&1 ) || true ( cd "$(webuiDir)" && runAsManager docker compose up -d >/dev/null 2>&1 ) || true
fi fi
} }

View File

@ -9,8 +9,8 @@
appDashyUpdateConf() appDashyUpdateConf()
{ {
local conf_file="${containers_dir}dashy/etc/conf.yml" local conf_file="${containers_dir}dashy/etc/conf.yml"
local services_json="${containers_dir}libreportal/frontend/data/apps/generated/apps-services.json" local services_json="$(webuiDir)/frontend/data/apps/generated/apps-services.json"
local icons_src_dir="${containers_dir}libreportal/frontend/icons/apps" local icons_src_dir="$(webuiDir)/frontend/icons/apps"
# Don't check the apps DB here — appUpdateSpecifics calls us at # Don't check the apps DB here — appUpdateSpecifics calls us at
# install step 6, BEFORE databaseInstallApp (step 8) writes the # install step 6, BEFORE databaseInstallApp (step 8) writes the

View File

@ -10,7 +10,7 @@
# gluetun adds/removes providers and protocols. Falls back silently to the # gluetun adds/removes providers and protocols. Falls back silently to the
# previous snapshot (or the bundled default) on network failure. # previous snapshot (or the bundled default) on network failure.
appWebuiRefresh_gluetun() { appWebuiRefresh_gluetun() {
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/gluetun-providers.json" local output_file="$(webuiDir)/frontend/data/apps/generated/gluetun-providers.json"
local upstream="https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json" local upstream="https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json"
local tmp="$(mktemp)" local tmp="$(mktemp)"
local raw="${output_file}.raw.$$" local raw="${output_file}.raw.$$"

View File

@ -5,7 +5,7 @@
# override flag. Skips itself, Traefik, LibrePortal, fail2ban. # override flag. Skips itself, Traefik, LibrePortal, fail2ban.
gluetunRouteExistingAppsPrompt() gluetunRouteExistingAppsPrompt()
{ {
local categories_file="${containers_dir}libreportal/frontend/data/apps/gluetun-eligible-categories.json" local categories_file="$(webuiDir)/frontend/data/apps/gluetun-eligible-categories.json"
local override="${CFG_REQUIREMENT_GLUETUN_FOR_ALL:-false}" local override="${CFG_REQUIREMENT_GLUETUN_FOR_ALL:-false}"
if ! command -v sqlite3 >/dev/null 2>&1 || [[ ! -f "$docker_dir/$db_file" ]]; then if ! command -v sqlite3 >/dev/null 2>&1 || [[ ! -f "$docker_dir/$db_file" ]]; then

View File

@ -25,7 +25,7 @@ libreportal_catalog_install_post_setup()
# Bundle the App Center's category icons so the site's sidebar matches the # Bundle the App Center's category icons so the site's sidebar matches the
# WebUI (same per-category glyphs). Copied from the live frontend so they # WebUI (same per-category glyphs). Copied from the live frontend so they
# stay in step; best-effort (the site falls back to misc.svg if absent). # stay in step; best-effort (the site falls back to misc.svg if absent).
local cat_src="$containers_dir/libreportal/frontend/core/icons/categories" local cat_src="$(webuiDir)/frontend/core/icons/categories"
if [[ -d "$cat_src" ]]; then if [[ -d "$cat_src" ]]; then
runFileOp mkdir -p "$dest/categories" || true runFileOp mkdir -p "$dest/categories" || true
runFileOp cp -f "$cat_src"/*.svg "$dest/categories/" 2>/dev/null || true runFileOp cp -f "$cat_src"/*.svg "$dest/categories/" 2>/dev/null || true

View File

@ -12,7 +12,7 @@
cliAppToolList() cliAppToolList()
{ {
local filter_app="$1" local filter_app="$1"
local manifest="$containers_dir/libreportal/frontend/data/apps/generated/apps-tools.json" local manifest="$(webuiDir)/frontend/data/apps/generated/apps-tools.json"
if [[ ! -f "$manifest" ]]; then if [[ ! -f "$manifest" ]]; then
isError "apps-tools.json not found at $manifest. Run the WebUI updater to generate it." isError "apps-tools.json not found at $manifest. Run the WebUI updater to generate it."

View File

@ -194,7 +194,7 @@ updaterSetAnchorRef()
# — i.e. the roll-back target. Read from history.json's most recent update/ok. # — i.e. the roll-back target. Read from history.json's most recent update/ok.
updaterLastUpdateFrom() updaterLastUpdateFrom()
{ {
local app="$1" hist="$containers_dir/libreportal/frontend/data/updater/generated/history.json" local app="$1" hist="$(webuiDir)/frontend/data/updater/generated/history.json"
[ -f "$hist" ] && command -v jq >/dev/null 2>&1 || return 0 [ -f "$hist" ] && command -v jq >/dev/null 2>&1 || return 0
jq -r --arg a "$app" 'first(.entries[]? | select(.app==$a and .action=="update" and .result=="ok") | .from) // ""' "$hist" 2>/dev/null jq -r --arg a "$app" 'first(.entries[]? | select(.app==$a and .action=="update" and .result=="ok") | .from) // ""' "$hist" 2>/dev/null
} }
@ -336,7 +336,7 @@ updaterRecordHistory()
{ {
local app="$1" action="$2" from="$3" to="$4" result="$5" local app="$1" action="$2" from="$3" to="$4" result="$5"
local artifact_id="${6:-}" serial="${7:-}" undo_id="${8:-}" trigger="${9:-manual}" local artifact_id="${6:-}" serial="${7:-}" undo_id="${8:-}" trigger="${9:-manual}"
local f="$containers_dir/libreportal/frontend/data/updater/generated/history.json" local f="$(webuiDir)/frontend/data/updater/generated/history.json"
local ts; ts="$(date -Iseconds 2>/dev/null || date)" local ts; ts="$(date -Iseconds 2>/dev/null || date)"
[ -f "$f" ] || printf '{ "entries": [] }\n' | runFileWrite "$f" [ -f "$f" ] || printf '{ "entries": [] }\n' | runFileWrite "$f"

View File

@ -71,7 +71,7 @@ cliHandleWebuiCommands()
cliWebuiLoginReset() cliWebuiLoginReset()
{ {
local auth_file="$containers_dir/libreportal/frontend/.auth.json" local auth_file="$(webuiDir)/frontend/.auth.json"
local webui_logins_file="$configs_dir/webui/webui_logins" local webui_logins_file="$configs_dir/webui/webui_logins"
isNotice "Resetting WebUI credentials..." isNotice "Resetting WebUI credentials..."

View File

@ -21,7 +21,7 @@
# crontab_task_processor.sh and webui_task_create.sh use; if those move, both # crontab_task_processor.sh and webui_task_create.sh use; if those move, both
# this file and they have to update together. # this file and they have to update together.
_taskDir() { _taskDir() {
echo "${containers_dir}libreportal/frontend/data/tasks" echo "$(webuiDir)/frontend/data/tasks"
} }
_genTaskId() { _genTaskId() {

View File

@ -142,7 +142,7 @@ reconcileConfigFile()
if [[ -s "$tmp" ]] && grep -q '^CFG_' "$tmp" && ! runInstallOp cmp -s "$tmp" "$live"; then if [[ -s "$tmp" ]] && grep -q '^CFG_' "$tmp" && ! runInstallOp cmp -s "$tmp" "$live"; then
local bak_file="${live%/*}/.${live##*/}.bak" local bak_file="${live%/*}/.${live##*/}.bak"
runCfgOp cp -a "$live" "$bak_file" runCfgOp cp -a "$live" "$bak_file"
{ [[ -n "$containers_dir" && "$live" == "$containers_dir"* ]] && runFileWrite "$live" < "$tmp" || runInstallOp cp "$tmp" "$live"; } { pathIsContainerData "$live" && runFileWrite "$live" < "$tmp" || runInstallOp cp "$tmp" "$live"; }
# A hidden .<file>.bak sibling is still written above for safety; the # A hidden .<file>.bak sibling is still written above for safety; the
# per-file result is intentionally silent — the intro notice and the # per-file result is intentionally silent — the intro notice and the
# per-section "...completed." lines are the only reconcile output. # per-section "...completed." lines are the only reconcile output.

View File

@ -51,6 +51,6 @@ tagsManagerUpdateUniversalTag()
# manager-owned configs/ + install templates use runInstallOp. The read # manager-owned configs/ + install templates use runInstallOp. The read
# (awk above) needs no escalation — config/compose files are world-readable. # (awk above) needs no escalation — config/compose files are world-readable.
local op="runInstallOp" local op="runInstallOp"
[[ "$file_path" == "$containers_dir"* || "$file_path" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$file_path" && op="runFileOp"
$op sed -i "/#LIBREPORTAL|${tag_name}|/s|${esc_placeholder}|${esc_new}|g" "$file_path" $op sed -i "/#LIBREPORTAL|${tag_name}|/s|${esc_placeholder}|${esc_new}|g" "$file_path"
} }

View File

@ -89,7 +89,7 @@ dockerUninstallApp()
# scan happened to run. Surgical delete rather than a rescan: a full # scan happened to run. Surgical delete rather than a rescan: a full
# updater scan re-runs CVE checks against every image and has no place # updater scan re-runs CVE checks against every image and has no place
# inside an uninstall. # inside an uninstall.
local _upd_gen="${containers_dir}libreportal/frontend/data/updater/generated" local _upd_gen="$(webuiDir)/frontend/data/updater/generated"
local _upd_f local _upd_f
for _upd_f in updates.json cves.json; do for _upd_f in updates.json cves.json; do
if [[ -f "$_upd_gen/$_upd_f" ]] && command -v jq >/dev/null 2>&1; then if [[ -f "$_upd_gen/$_upd_f" ]] && command -v jq >/dev/null 2>&1; then
@ -114,7 +114,7 @@ dockerUninstallApp()
echo "" echo ""
echo "---- $menu_number. Removing related task history" echo "---- $menu_number. Removing related task history"
echo "" echo ""
local _tasks_dir="${containers_dir}libreportal/frontend/data/tasks" local _tasks_dir="$(webuiDir)/frontend/data/tasks"
local _removed=0 local _removed=0
if [[ -d "$_tasks_dir" ]]; then if [[ -d "$_tasks_dir" ]]; then
for _tf in "$_tasks_dir"/task_*.json; do for _tf in "$_tasks_dir"/task_*.json; do

View File

@ -91,13 +91,33 @@ runInstallWrite() {
# file" bug — the substitution silently failed, leaving the placeholder). # file" bug — the substitution silently failed, leaving the placeholder).
runCfgOp() { runCfgOp() {
local _file="${!#}" local _file="${!#}"
if [[ -n "$containers_dir" && "$_file" == "$containers_dir"* ]]; then if _runCfgIsContainerPath "$_file"; then
runFileOp "$@" runFileOp "$@"
else else
runInstallOp "$@" runInstallOp "$@"
fi fi
} }
# paths.sh owns pathIsContainerData (it knows every registered storage root).
# init.sh sources THIS file directly at install time WITHOUT paths.sh, so fall
# back to the primary root alone — at install no extra locations exist yet, so
# the two answers agree.
#
# Deliberately not named pathIsContainerData: a same-named top-level definition
# here gets indexed by generate_function_manifest.sh, and the resulting autoload
# stub would shadow the real multi-root implementation with this primary-only
# one — silently classifying every file on a second disk as manager-owned.
_runCfgIsContainerPath() {
local p="$1"
if declare -F pathIsContainerData >/dev/null 2>&1; then
pathIsContainerData "$p"
return $?
fi
local root="${LP_CONTAINERS_DIR:-/libreportal-containers}"
root="${root%/}"
[[ -n "$p" && ( "$p" == "$root" || "$p" == "$root/"* ) ]]
}
# Backup-engine command (borg/restic/kopia) run AS the dedicated backup user # Backup-engine command (borg/restic/kopia) run AS the dedicated backup user
# ($docker_install_user), with the repo password and BORG_/RESTIC_/KOPIA_ env # ($docker_install_user), with the repo password and BORG_/RESTIC_/KOPIA_ env
# vars carried across the privilege drop. Never root — the scoped sudoers lets # vars carried across the privilege drop. Never root — the scoped sudoers lets

View File

@ -43,7 +43,7 @@ dockerRemoveStrandedContainers()
[[ -z "$name" || -z "$work_dir" ]] && continue [[ -z "$name" || -z "$work_dir" ]] && continue
# Only LibrePortal's own container tree, and only when the project # Only LibrePortal's own container tree, and only when the project
# directory is provably gone. # directory is provably gone.
[[ "$work_dir" == "$containers_dir"* ]] || continue pathIsContainerData "$work_dir" || continue
[[ -d "$work_dir" ]] && continue [[ -d "$work_dir" ]] && continue
if runFileOp docker rm -f "$name" >/dev/null 2>&1; then if runFileOp docker rm -f "$name" >/dev/null 2>&1; then

View File

@ -67,7 +67,7 @@ dockerHealthScan() {
# $state_file, written ONLY by the throttled caller (webuiSystemHealthCheck) # $state_file, written ONLY by the throttled caller (webuiSystemHealthCheck)
# via HEALTH_RESTART_SNAPSHOT, so the heal's rapid re-scans don't disturb the # via HEALTH_RESTART_SNAPSHOT, so the heal's rapid re-scans don't disturb the
# baseline. One inspect over all containers (name|count|status|restarting). # baseline. One inspect over all containers (name|count|status|restarting).
local state_file="${containers_dir}/libreportal/frontend/data/system/.health_restart_counts" local state_file="$(webuiDir)/frontend/data/system/.health_restart_counts"
HEALTH_RESTART_SNAPSHOT="" HEALTH_RESTART_SNAPSHOT=""
declare -A _prev_rc=() declare -A _prev_rc=()

View File

@ -17,7 +17,7 @@ copyFile()
# the manager-owned control plane (configs/logs/etc.) is runInstallOp. # the manager-owned control plane (configs/logs/etc.) is runInstallOp.
# Mirrors createTouch's path-based ownership; $user_name is now advisory. # Mirrors createTouch's path-based ownership; $user_name is now advisory.
local op="runInstallOp" local op="runInstallOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$save_dir" && op="runFileOp"
if [ "$silent_flag" == "loud" ]; then if [ "$silent_flag" == "loud" ]; then
local result; result=$($op cp $flags_full "$file" "$save_dir") local result; result=$($op cp $flags_full "$file" "$save_dir")
@ -43,7 +43,7 @@ repairStubDirForFile()
[[ -d "$target" ]] || return 0 [[ -d "$target" ]] || return 0
local op="runInstallOp" local op="runInstallOp"
[[ "$target" == "$containers_dir"* || "$target" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$target" && op="runFileOp"
local name; name=$(basename "$target") local name; name=$(basename "$target")
local staged="$target.stub-repair.$$" local staged="$target.stub-repair.$$"

View File

@ -9,7 +9,7 @@ copyFiles()
# Write as the destination's owner (see copyFile). # Write as the destination's owner (see copyFile).
local op="runInstallOp" local op="runInstallOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$save_dir" && op="runFileOp"
local files=($($op find "$source" -type f)) local files=($($op find "$source" -type f))
if [ ${#files[@]} -eq 0 ]; then if [ ${#files[@]} -eq 0 ]; then

View File

@ -21,7 +21,7 @@ createTouch()
local file_dir=$(dirname "$clean_file") local file_dir=$(dirname "$clean_file")
local op="runInstallOp" local op="runInstallOp"
if [[ "$clean_file" == "$containers_dir"* || "$clean_file" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then if pathIsContainerData "$clean_file"; then
op="runFileOp" op="runFileOp"
fi fi

View File

@ -10,7 +10,7 @@ moveFile()
if [ -e "$file" ]; then if [ -e "$file" ]; then
# Move as the destination's owner — no root, no chown (see copyFile). # Move as the destination's owner — no root, no chown (see copyFile).
local op="runInstallOp" local op="runInstallOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$save_dir" && op="runFileOp"
local result; result=$($op mv "$file" "$save_dir") local result; result=$($op mv "$file" "$save_dir")
checkSuccess "Moving $file_name to $save_dir" checkSuccess "Moving $file_name to $save_dir"
else else

View File

@ -8,8 +8,8 @@ copyFolder()
local user_name="$3" # advisory — the destination path determines the owner local user_name="$3" # advisory — the destination path determines the owner
# Write as the destination's owner — no root, no chown (see copyFile). # Write as the destination's owner — no root, no chown (see copyFile).
if [[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then if pathIsContainerData "$save_dir"; then
if [[ "$folder" == "$containers_dir"* || "$folder" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then if pathIsContainerData "$folder"; then
# container -> container: same owner (dockerinstall), a plain cp works. # container -> container: same owner (dockerinstall), a plain cp works.
local result; result=$(runFileOp cp -rf "$folder" "$save_dir") local result; result=$(runFileOp cp -rf "$folder" "$save_dir")
else else

View File

@ -8,7 +8,7 @@ copyFolders()
# Write as the destination's owner — no root, no chown (see copyFile). # Write as the destination's owner — no root, no chown (see copyFile).
local op="runInstallOp" local op="runInstallOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp" pathIsContainerData "$save_dir" && op="runFileOp"
local subdirs=($(find "$source" -mindepth 1 -maxdepth 1 -type d)) local subdirs=($(find "$source" -mindepth 1 -maxdepth 1 -type d))
if [ ${#subdirs[@]} -eq 0 ]; then if [ ${#subdirs[@]} -eq 0 ]; then

View File

@ -14,7 +14,7 @@ createFolders()
# AS that user via runFileOp — creating it as the right owner avoids a # AS that user via runFileOp — creating it as the right owner avoids a
# chown-to-another-user the unprivileged runtime can't do. Mirrors # chown-to-another-user the unprivileged runtime can't do. Mirrors
# createTouch; the $user_name hint is advisory for these paths. # createTouch; the $user_name hint is advisory for these paths.
if [[ "$clean_dir" == "$containers_dir"* || "$clean_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then if pathIsContainerData "$clean_dir"; then
if [ ! -d "$dir_path" ]; then if [ ! -d "$dir_path" ]; then
local result; result=$(runFileOp mkdir -p "$dir_path") local result; result=$(runFileOp mkdir -p "$dir_path")
[ "$silent_flag" == "loud" ] && checkSuccess "Creating $folder_name directory" [ "$silent_flag" == "loud" ] && checkSuccess "Creating $folder_name directory"

View File

@ -125,7 +125,7 @@ setupApplyFinalize()
# failed app's own task row is already red; the WebUI watcher is what gates # failed app's own task row is already red; the WebUI watcher is what gates
# the "your install is ready" hand-off on this same group-level result. # the "your install is ready" hand-off on this same group-level result.
if [[ -n "$setup_group" ]]; then if [[ -n "$setup_group" ]]; then
local tasks_dir="${containers_dir}libreportal/frontend/data/tasks" local tasks_dir="$(webuiDir)/frontend/data/tasks"
local total=0 failed=0 failed_names="" f local total=0 failed=0 failed_names="" f
if [[ -d "$tasks_dir" ]]; then if [[ -d "$tasks_dir" ]]; then
for f in "$tasks_dir"/task_*.json; do for f in "$tasks_dir"/task_*.json; do

View File

@ -1,21 +1,27 @@
#!/bin/bash #!/bin/bash
SETUP_LOCK_FILE="${containers_dir}libreportal/frontend/data/.setup_complete" # Resolved on call, not at source time: this file can be sourced by the lazy
# loader before paths.sh has run, and a source-time expansion would bake an
# empty root into an absolute-looking path.
setupLockFile()
{
printf '%s' "$(webuiDir)/frontend/data/.setup_complete"
}
isSetupWizardComplete() isSetupWizardComplete()
{ {
[[ -f "$SETUP_LOCK_FILE" ]] [[ -f "$(setupLockFile)" ]]
} }
setupWizardMarkComplete() setupWizardMarkComplete()
{ {
# .setup_complete lives in the docker-install-owned frontend/data -> runFileOp # .setup_complete lives in the docker-install-owned frontend/data -> runFileOp
# (created as that user; no root, no chown). # (created as that user; no root, no chown).
runFileOp touch "$SETUP_LOCK_FILE" 2>/dev/null runFileOp touch "$(setupLockFile)" 2>/dev/null
runFileOp chmod 644 "$SETUP_LOCK_FILE" 2>/dev/null runFileOp chmod 644 "$(setupLockFile)" 2>/dev/null
} }
setupWizardReset() setupWizardReset()
{ {
runFileOp rm -f "$SETUP_LOCK_FILE" 2>/dev/null runFileOp rm -f "$(setupLockFile)" 2>/dev/null
} }

View File

@ -915,6 +915,7 @@ declare -gA LP_FN_MAP=(
[runAsManager]="docker/command/run_privileged.sh" [runAsManager]="docker/command/run_privileged.sh"
[runBackupOp]="docker/command/run_privileged.sh" [runBackupOp]="docker/command/run_privileged.sh"
[runBinInstall]="docker/command/run_privileged.sh" [runBinInstall]="docker/command/run_privileged.sh"
[_runCfgIsContainerPath]="docker/command/run_privileged.sh"
[runCfgOp]="docker/command/run_privileged.sh" [runCfgOp]="docker/command/run_privileged.sh"
[runCrowdsec]="docker/command/run_privileged.sh" [runCrowdsec]="docker/command/run_privileged.sh"
[runFileOp]="docker/command/run_privileged.sh" [runFileOp]="docker/command/run_privileged.sh"
@ -951,6 +952,7 @@ declare -gA LP_FN_MAP=(
[setupHeadscaleUser]="headscale/scripts/headscale_user.sh" [setupHeadscaleUser]="headscale/scripts/headscale_user.sh"
[setupHeadscaleVariables]="network/variables/headscale_variables.sh" [setupHeadscaleVariables]="network/variables/headscale_variables.sh"
[setupLocalDnsRewrites]="network/dns/setup_local_dns.sh" [setupLocalDnsRewrites]="network/dns/setup_local_dns.sh"
[setupLockFile]="setup/setup_lock.sh"
[setupTaskDir]="task/crontab_task_processor.sh" [setupTaskDir]="task/crontab_task_processor.sh"
[setupWizardMarkComplete]="setup/setup_lock.sh" [setupWizardMarkComplete]="setup/setup_lock.sh"
[setupWizardReset]="setup/setup_lock.sh" [setupWizardReset]="setup/setup_lock.sh"
@ -2097,6 +2099,7 @@ declare -gA LP_FN_ROOT=(
[runAsManager]="scripts" [runAsManager]="scripts"
[runBackupOp]="scripts" [runBackupOp]="scripts"
[runBinInstall]="scripts" [runBinInstall]="scripts"
[_runCfgIsContainerPath]="scripts"
[runCfgOp]="scripts" [runCfgOp]="scripts"
[runCrowdsec]="scripts" [runCrowdsec]="scripts"
[runFileOp]="scripts" [runFileOp]="scripts"
@ -2133,6 +2136,7 @@ declare -gA LP_FN_ROOT=(
[setupHeadscaleUser]="containers" [setupHeadscaleUser]="containers"
[setupHeadscaleVariables]="scripts" [setupHeadscaleVariables]="scripts"
[setupLocalDnsRewrites]="scripts" [setupLocalDnsRewrites]="scripts"
[setupLockFile]="scripts"
[setupTaskDir]="scripts" [setupTaskDir]="scripts"
[setupWizardMarkComplete]="scripts" [setupWizardMarkComplete]="scripts"
[setupWizardReset]="scripts" [setupWizardReset]="scripts"
@ -2387,7 +2391,6 @@ LP_EAGER_FILES=(
"containers:matrix/scripts/matrix_auth.sh" "containers:matrix/scripts/matrix_auth.sh"
"scripts:migrate/migrate_url_rewrite.sh" "scripts:migrate/migrate_url_rewrite.sh"
"containers:nextcloud/scripts/nextcloud_upgrade_hooks.sh" "containers:nextcloud/scripts/nextcloud_upgrade_hooks.sh"
"scripts:setup/setup_lock.sh"
"scripts:source/artifacts.sh" "scripts:source/artifacts.sh"
"scripts:task/crontab_check_processor.sh" "scripts:task/crontab_check_processor.sh"
"scripts:task/crontab_task_processor.sh" "scripts:task/crontab_task_processor.sh"
@ -3316,6 +3319,7 @@ runAppCfg() { unset -f runAppCfg; __lpAutoload "${install_scripts_dir}docker/com
runAsManager() { unset -f runAsManager; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runAsManager "$@"; } runAsManager() { unset -f runAsManager; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runAsManager "$@"; }
runBackupOp() { unset -f runBackupOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runBackupOp "$@"; } runBackupOp() { unset -f runBackupOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runBackupOp "$@"; }
runBinInstall() { unset -f runBinInstall; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runBinInstall "$@"; } runBinInstall() { unset -f runBinInstall; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runBinInstall "$@"; }
_runCfgIsContainerPath() { unset -f _runCfgIsContainerPath; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; _runCfgIsContainerPath "$@"; }
runCfgOp() { unset -f runCfgOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runCfgOp "$@"; } runCfgOp() { unset -f runCfgOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runCfgOp "$@"; }
runCrowdsec() { unset -f runCrowdsec; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runCrowdsec "$@"; } runCrowdsec() { unset -f runCrowdsec; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runCrowdsec "$@"; }
runFileOp() { unset -f runFileOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runFileOp "$@"; } runFileOp() { unset -f runFileOp; __lpAutoload "${install_scripts_dir}docker/command/run_privileged.sh"; runFileOp "$@"; }
@ -3352,6 +3356,7 @@ setupHeadscaleRemote() { unset -f setupHeadscaleRemote; __lpAutoload "${install_
setupHeadscaleUser() { unset -f setupHeadscaleUser; __lpAutoload "${install_containers_dir}headscale/scripts/headscale_user.sh"; setupHeadscaleUser "$@"; } setupHeadscaleUser() { unset -f setupHeadscaleUser; __lpAutoload "${install_containers_dir}headscale/scripts/headscale_user.sh"; setupHeadscaleUser "$@"; }
setupHeadscaleVariables() { unset -f setupHeadscaleVariables; __lpAutoload "${install_scripts_dir}network/variables/headscale_variables.sh"; setupHeadscaleVariables "$@"; } setupHeadscaleVariables() { unset -f setupHeadscaleVariables; __lpAutoload "${install_scripts_dir}network/variables/headscale_variables.sh"; setupHeadscaleVariables "$@"; }
setupLocalDnsRewrites() { unset -f setupLocalDnsRewrites; __lpAutoload "${install_scripts_dir}network/dns/setup_local_dns.sh"; setupLocalDnsRewrites "$@"; } setupLocalDnsRewrites() { unset -f setupLocalDnsRewrites; __lpAutoload "${install_scripts_dir}network/dns/setup_local_dns.sh"; setupLocalDnsRewrites "$@"; }
setupLockFile() { unset -f setupLockFile; __lpAutoload "${install_scripts_dir}setup/setup_lock.sh"; setupLockFile "$@"; }
setupTaskDir() { unset -f setupTaskDir; __lpAutoload "${install_scripts_dir}task/crontab_task_processor.sh"; setupTaskDir "$@"; } setupTaskDir() { unset -f setupTaskDir; __lpAutoload "${install_scripts_dir}task/crontab_task_processor.sh"; setupTaskDir "$@"; }
setupWizardMarkComplete() { unset -f setupWizardMarkComplete; __lpAutoload "${install_scripts_dir}setup/setup_lock.sh"; setupWizardMarkComplete "$@"; } setupWizardMarkComplete() { unset -f setupWizardMarkComplete; __lpAutoload "${install_scripts_dir}setup/setup_lock.sh"; setupWizardMarkComplete "$@"; }
setupWizardReset() { unset -f setupWizardReset; __lpAutoload "${install_scripts_dir}setup/setup_lock.sh"; setupWizardReset "$@"; } setupWizardReset() { unset -f setupWizardReset; __lpAutoload "${install_scripts_dir}setup/setup_lock.sh"; setupWizardReset "$@"; }

View File

@ -3,7 +3,7 @@
crontabSetupCheckProcessor() crontabSetupCheckProcessor()
{ {
local task_check_script="$install_scripts_dir/task/crontab_check_processor.sh" local task_check_script="$install_scripts_dir/task/crontab_check_processor.sh"
local task_dir="$containers_dir/libreportal/frontend/data/tasks" local task_dir="$(webuiDir)/frontend/data/tasks"
# Update TASK_DIR in the task processor script # Update TASK_DIR in the task processor script
if [ -f "$task_check_script" ]; then if [ -f "$task_check_script" ]; then

View File

@ -3,7 +3,7 @@
crontabSetupTaskProcessor() crontabSetupTaskProcessor()
{ {
local task_processor_script="$install_scripts_dir/task/crontab_task_processor.sh" local task_processor_script="$install_scripts_dir/task/crontab_task_processor.sh"
local task_dir="$containers_dir/libreportal/frontend/data/tasks" local task_dir="$(webuiDir)/frontend/data/tasks"
# Update TASK_DIR in the task processor script # Update TASK_DIR in the task processor script
if [ -f "$task_processor_script" ]; then if [ -f "$task_processor_script" ]; then

View File

@ -3,7 +3,7 @@
# Update a specific app's status in apps.json (used for install/uninstall operations) # Update a specific app's status in apps.json (used for install/uninstall operations)
webuiUpdateAppStatus() { webuiUpdateAppStatus() {
local app_name="$1" local app_name="$1"
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps.json" local output_file="$(webuiDir)/frontend/data/apps/generated/apps.json"
if [[ -z "$app_name" ]]; then if [[ -z "$app_name" ]]; then
isError "app_name parameter required for webuiUpdateAppStatus" isError "app_name parameter required for webuiUpdateAppStatus"

View File

@ -33,7 +33,7 @@ _webuiReadServiceTags() {
webuiGenerateLibrePortalConfig() { webuiGenerateLibrePortalConfig() {
local testing_mode="$1" local testing_mode="$1"
local specific_app="$2" local specific_app="$2"
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps.json" local output_file="$(webuiDir)/frontend/data/apps/generated/apps.json"
local first_app=true local first_app=true
local app_count=0 local app_count=0
@ -162,7 +162,7 @@ EOF
icon_file="$app_name.png" icon_file="$app_name.png"
fi fi
if [[ -n "$icon_file" ]]; then if [[ -n "$icon_file" ]]; then
local icons_apps_dir="${containers_dir}libreportal/frontend/core/icons/apps" local icons_apps_dir="$(webuiDir)/frontend/core/icons/apps"
runFileOp mkdir -p "$icons_apps_dir" runFileOp mkdir -p "$icons_apps_dir"
runFileWrite "$icons_apps_dir/$icon_file" < "$dir/$icon_file" 2>/dev/null runFileWrite "$icons_apps_dir/$icon_file" < "$dir/$icon_file" 2>/dev/null
fi fi

View File

@ -7,7 +7,7 @@ webuiPatchAppConfigJson() {
local app_name="$1" local app_name="$1"
[[ -z "$app_name" ]] && return 1 [[ -z "$app_name" ]] && return 1
local apps_json="${containers_dir}libreportal/frontend/data/apps/generated/apps.json" local apps_json="$(webuiDir)/frontend/data/apps/generated/apps.json"
[[ ! -f "$apps_json" ]] && return 1 [[ ! -f "$apps_json" ]] && return 1
local deployed_cfg="${containers_dir}${app_name}/${app_name}.config" local deployed_cfg="${containers_dir}${app_name}/${app_name}.config"

View File

@ -4,7 +4,7 @@
# Generates apps-services.json from database # Generates apps-services.json from database
webuiGenerateAppsServicesConfig() { webuiGenerateAppsServicesConfig() {
local testing_mode="$1" local testing_mode="$1"
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps-services.json" local output_file="$(webuiDir)/frontend/data/apps/generated/apps-services.json"
local first_service=true local first_service=true
local service_count=0 local service_count=0
@ -81,8 +81,8 @@ EOF
# source IP of the default route) is what LAN/VPN clients dial, # source IP of the default route) is what LAN/VPN clients dial,
# and matches the host APP_URL is stamped with. # and matches the host APP_URL is stamped with.
local server_ip="" local server_ip=""
if [[ -f "${containers_dir}libreportal/config/generated/configs.json" ]]; then if [[ -f "$(webuiDir)/config/generated/configs.json" ]]; then
server_ip=$(grep -o '"CFG_SERVER_IP":[[:space:]]*"[^"]*"' "${containers_dir}libreportal/config/generated/configs.json" | cut -d'"' -f4) server_ip=$(grep -o '"CFG_SERVER_IP":[[:space:]]*"[^"]*"' "$(webuiDir)/config/generated/configs.json" | cut -d'"' -f4)
fi fi
[[ -z "$server_ip" ]] && server_ip="${local_ip_v4:-localhost}" [[ -z "$server_ip" ]] && server_ip="${local_ip_v4:-localhost}"
[[ -z "$server_ip" ]] && server_ip="localhost" [[ -z "$server_ip" ]] && server_ip="localhost"

View File

@ -47,7 +47,7 @@ webuiGenerateAppsToolsConfig() {
return $? return $?
fi fi
local output_file="${containers_dir}libreportal/frontend/data/apps/generated/apps-tools.json" local output_file="$(webuiDir)/frontend/data/apps/generated/apps-tools.json"
local tmp="$(mktemp)" local tmp="$(mktemp)"
runFileOp mkdir -p "$(dirname "$output_file")" runFileOp mkdir -p "$(dirname "$output_file")"

View File

@ -3,7 +3,7 @@
webuiGenerateBackupAppStatus() webuiGenerateBackupAppStatus()
{ {
local app_name="${1:-}" local app_name="${1:-}"
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated/apps" local output_dir="$(webuiDir)/frontend/data/backup/generated/apps"
runFileOp mkdir -p "$output_dir" runFileOp mkdir -p "$output_dir"
# Resolve the app list: a single app (direct call — e.g. right after that # Resolve the app list: a single app (direct call — e.g. right after that

View File

@ -2,7 +2,7 @@
webuiGenerateBackupDashboard() webuiGenerateBackupDashboard()
{ {
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated" local output_dir="$(webuiDir)/frontend/data/backup/generated"
local output_file="$output_dir/dashboard.json" local output_file="$output_dir/dashboard.json"
local temp_file="${output_file}.tmp.$$" local temp_file="${output_file}.tmp.$$"

View File

@ -7,7 +7,7 @@
webuiGenerateBackupEngines() webuiGenerateBackupEngines()
{ {
local src_dir="$install_scripts_dir/backup/engines" local src_dir="$install_scripts_dir/backup/engines"
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated/engines" local out_dir="$(webuiDir)/frontend/data/backup/generated/engines"
runFileOp mkdir -p "$out_dir" runFileOp mkdir -p "$out_dir"
if [[ ! -d "$src_dir" ]]; then if [[ ! -d "$src_dir" ]]; then

View File

@ -2,7 +2,7 @@
webuiGenerateBackupLocations() webuiGenerateBackupLocations()
{ {
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated" local output_dir="$(webuiDir)/frontend/data/backup/generated"
local output_file="$output_dir/locations.json" local output_file="$output_dir/locations.json"
local temp_file="${output_file}.tmp.$$" local temp_file="${output_file}.tmp.$$"
runFileOp mkdir -p "$output_dir" runFileOp mkdir -p "$output_dir"

View File

@ -11,7 +11,7 @@
webuiGenerateBackupMigrate() webuiGenerateBackupMigrate()
{ {
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated" local output_dir="$(webuiDir)/frontend/data/backup/generated"
local output_file="$output_dir/migrate.json" local output_file="$output_dir/migrate.json"
runFileOp mkdir -p "$output_dir" runFileOp mkdir -p "$output_dir"

View File

@ -2,7 +2,7 @@
webuiGenerateBackupPasswords() webuiGenerateBackupPasswords()
{ {
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated" local output_dir="$(webuiDir)/frontend/data/backup/generated"
local output_file="$output_dir/passwords.txt" local output_file="$output_dir/passwords.txt"
local temp_file="${output_file}.tmp.$$" local temp_file="${output_file}.tmp.$$"

View File

@ -10,7 +10,7 @@
webuiGenerateBackupSchema() webuiGenerateBackupSchema()
{ {
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated" local out_dir="$(webuiDir)/frontend/data/backup/generated"
local out_file="$out_dir/schema.json" local out_file="$out_dir/schema.json"
runFileOp mkdir -p "$out_dir" runFileOp mkdir -p "$out_dir"

View File

@ -3,7 +3,7 @@
webuiGenerateBackupSnapshots() webuiGenerateBackupSnapshots()
{ {
local scope="${1:-all}" local scope="${1:-all}"
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated" local output_dir="$(webuiDir)/frontend/data/backup/generated"
runFileOp mkdir -p "$output_dir" runFileOp mkdir -p "$output_dir"
local indices=() local indices=()

View File

@ -14,7 +14,7 @@ createTaskFile() {
fi fi
# Set task directory # Set task directory
local task_dir="${containers_dir}libreportal/frontend/data/tasks" local task_dir="$(webuiDir)/frontend/data/tasks"
# Ensure task directory exists (container-owned WebUI data tree -> runFileOp) # Ensure task directory exists (container-owned WebUI data tree -> runFileOp)
if [ ! -d "$task_dir" ]; then if [ ! -d "$task_dir" ]; then

View File

@ -78,7 +78,7 @@ webuiGenerateSystemConfigs() {
fi fi
} }
local output_file="${containers_dir}libreportal/frontend/data/config/generated/configs.json" local output_file="$(webuiDir)/frontend/data/config/generated/configs.json"
local temp_file="$(mktemp)" local temp_file="$(mktemp)"
runFileOp mkdir -p "$(dirname "$output_file")" runFileOp mkdir -p "$(dirname "$output_file")"

View File

@ -8,7 +8,7 @@
webuiGeneratePeers() webuiGeneratePeers()
{ {
local output_dir="$containers_dir/libreportal/frontend/data/peers/generated" local output_dir="$(webuiDir)/frontend/data/peers/generated"
local output_file="$output_dir/peers.json" local output_file="$output_dir/peers.json"
runFileOp mkdir -p "$output_dir" runFileOp mkdir -p "$output_dir"

View File

@ -6,7 +6,7 @@
webuiGenerateSshAccess() webuiGenerateSshAccess()
{ {
local out_dir="$containers_dir/libreportal/frontend/data/ssh" local out_dir="$(webuiDir)/frontend/data/ssh"
local out_file="$out_dir/access.json" local out_file="$out_dir/access.json"
runFileOp mkdir -p "$out_dir" runFileOp mkdir -p "$out_dir"

View File

@ -27,7 +27,7 @@ webuiSystemDisk() {
fi fi
# Create system info directory if it doesn't exist # Create system info directory if it doesn't exist
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
createFolders "quiet" $sudo_user_name "$system_dir" createFolders "quiet" $sudo_user_name "$system_dir"
# Create temp file first, then atomic move # Create temp file first, then atomic move

View File

@ -20,7 +20,7 @@
webuiSystemHealthCheck() { webuiSystemHealthCheck() {
local force_flag="$1" local force_flag="$1"
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
local final_file="${system_dir}/health_status.json" local final_file="${system_dir}/health_status.json"
local stamp_file="${system_dir}/.health_check_stamp" local stamp_file="${system_dir}/.health_check_stamp"
local heal_stamp="${system_dir}/.health_heal_stamp" local heal_stamp="${system_dir}/.health_heal_stamp"

View File

@ -31,7 +31,7 @@ webuiSystemInfo() {
local kernel_version=$(uname -r) local kernel_version=$(uname -r)
# Create system info directory if it doesn't exist # Create system info directory if it doesn't exist
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
createFolders "quiet" $sudo_user_name "$system_dir" createFolders "quiet" $sudo_user_name "$system_dir"
# Create temp file first, then atomic move # Create temp file first, then atomic move

View File

@ -23,7 +23,7 @@ webuiSystemMemory() {
local mem_text="$mem_used_gb GB / $mem_total_gb GB - $mem_percent%" local mem_text="$mem_used_gb GB / $mem_total_gb GB - $mem_percent%"
# Create system info directory if it doesn't exist # Create system info directory if it doesn't exist
local system_dir="${containers_dir}libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
createFolders "quiet" $sudo_user_name "$system_dir" createFolders "quiet" $sudo_user_name "$system_dir"
# Create temp file first, then atomic move # Create temp file first, then atomic move

View File

@ -27,7 +27,7 @@ _metricsReadCpu() {
} }
webuiSystemMetrics() { webuiSystemMetrics() {
local system_dir="${containers_dir}libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
createFolders "quiet" "$sudo_user_name" "$system_dir" createFolders "quiet" "$sudo_user_name" "$system_dir"
local now_epoch; now_epoch=$(date +%s) local now_epoch; now_epoch=$(date +%s)
@ -209,7 +209,7 @@ EOF
# run per CFG_APP_STORAGE_INTERVAL (default 10 min) via the .stamp idiom used by # run per CFG_APP_STORAGE_INTERVAL (default 10 min) via the .stamp idiom used by
# the update/verify checks above. Pass "force" to bypass it. # the update/verify checks above. Pass "force" to bypass it.
webuiSystemAppStorage() { webuiSystemAppStorage() {
local system_dir="${containers_dir}libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
createFolders "quiet" "$sudo_user_name" "$system_dir" createFolders "quiet" "$sudo_user_name" "$system_dir"
local final_file="$system_dir/app_storage.json" local final_file="$system_dir/app_storage.json"
@ -304,7 +304,7 @@ webuiSystemAppStorage() {
# compose project label fall back to their container name so nothing is lost. # compose project label fall back to their container name so nothing is lost.
webuiSystemApps() { webuiSystemApps() {
local now_epoch="$1" now_iso="$2" system_dir="$3" local now_epoch="$1" now_iso="$2" system_dir="$3"
[[ -z "$system_dir" ]] && system_dir="${containers_dir}libreportal/frontend/data/system" [[ -z "$system_dir" ]] && system_dir="$(webuiDir)/frontend/data/system"
command -v docker &>/dev/null || return 0 command -v docker &>/dev/null || return 0
# name|project|state|status for every container (running or not) # name|project|state|status for every container (running or not)

View File

@ -16,7 +16,7 @@
webuiSystemNetworkCheck() { webuiSystemNetworkCheck() {
local force_flag="$1" local force_flag="$1"
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
local final_file="${system_dir}/network_status.json" local final_file="${system_dir}/network_status.json"
local stamp_file="${system_dir}/.network_check_stamp" local stamp_file="${system_dir}/.network_check_stamp"
local interval="${CFG_NETWORK_CHECK_INTERVAL:-300}" local interval="${CFG_NETWORK_CHECK_INTERVAL:-300}"

View File

@ -52,7 +52,7 @@ webuiSystemUpdateCheck() {
local force_flag="$1" local force_flag="$1"
local repo_dir="${script_dir}" local repo_dir="${script_dir}"
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
local final_file="${system_dir}/update_status.json" local final_file="${system_dir}/update_status.json"
local stamp_file="${system_dir}/.update_check_stamp" local stamp_file="${system_dir}/.update_check_stamp"
# How long (seconds) a fetch result stays "fresh" before we hit the network # How long (seconds) a fetch result stays "fresh" before we hit the network
@ -267,7 +267,7 @@ EOF
webuiSystemVerify() { webuiSystemVerify() {
local force_flag="$1" local force_flag="$1"
local system_dir="$containers_dir/libreportal/frontend/data/system" local system_dir="$(webuiDir)/frontend/data/system"
local final_file="${system_dir}/verify_status.json" local final_file="${system_dir}/verify_status.json"
local stamp_file="${system_dir}/.verify_check_stamp" local stamp_file="${system_dir}/.verify_check_stamp"
local interval="${CFG_VERIFY_CHECK_INTERVAL:-86400}" local interval="${CFG_VERIFY_CHECK_INTERVAL:-86400}"

View File

@ -355,7 +355,7 @@ updaterDisplayVersion() {
} }
webuiUpdaterScan() { webuiUpdaterScan() {
local out_dir="$containers_dir/libreportal/frontend/data/updater/generated" local out_dir="$(webuiDir)/frontend/data/updater/generated"
runFileOp mkdir -p "$out_dir" 2>/dev/null || mkdir -p "$out_dir" 2>/dev/null runFileOp mkdir -p "$out_dir" 2>/dev/null || mkdir -p "$out_dir" 2>/dev/null
local now; now="$(date -Iseconds 2>/dev/null || date)" local now; now="$(date -Iseconds 2>/dev/null || date)"

View File

@ -15,7 +15,7 @@
# the leftover lock would otherwise wedge EVERY future refresh. No single refresh # the leftover lock would otherwise wedge EVERY future refresh. No single refresh
# runs anywhere near this long, so a lock older than the threshold is a leftover. # runs anywhere near this long, so a lock older than the threshold is a leftover.
webuiCheckUpdateLock() { webuiCheckUpdateLock() {
local lock_file="$containers_dir/libreportal/frontend/data/updater.lock" local lock_file="$(webuiDir)/frontend/data/updater.lock"
local stale_after=900 # seconds (15 min); far longer than any real refresh local stale_after=900 # seconds (15 min); far longer than any real refresh
if [ ! -f "$lock_file" ]; then if [ ! -f "$lock_file" ]; then

View File

@ -4,7 +4,7 @@
# Creates update lock file to prevent concurrent updates # Creates update lock file to prevent concurrent updates
webuiCreateUpdateLock() { webuiCreateUpdateLock() {
local lock_file="$containers_dir/libreportal/frontend/data/updater.lock" local lock_file="$(webuiDir)/frontend/data/updater.lock"
# Create lock file with timestamp and process ID. The lock lives in the # Create lock file with timestamp and process ID. The lock lives in the
# dockerinstall-owned WebUI data dir, so write it as the path owner. # dockerinstall-owned WebUI data dir, so write it as the path owner.

View File

@ -4,7 +4,7 @@
# Removes setup lock file after update completion # Removes setup lock file after update completion
webuiRemoveSetupLock() { webuiRemoveSetupLock() {
local lock_file="$containers_dir/libreportal/frontend/data/setup.lock" local lock_file="$(webuiDir)/frontend/data/setup.lock"
if [ -f "$lock_file" ]; then if [ -f "$lock_file" ]; then
runFileOp rm -f "$lock_file" runFileOp rm -f "$lock_file"

View File

@ -4,7 +4,7 @@
# Removes update lock file after update completion # Removes update lock file after update completion
webuiRemoveUpdateLock() { webuiRemoveUpdateLock() {
local lock_file="$containers_dir/libreportal/frontend/data/updater.lock" local lock_file="$(webuiDir)/frontend/data/updater.lock"
if [ -f "$lock_file" ]; then if [ -f "$lock_file" ]; then
runFileOp rm -f "$lock_file" runFileOp rm -f "$lock_file"

View File

@ -4,7 +4,7 @@
webuiCreateLogsFolders() webuiCreateLogsFolders()
{ {
local log_dir="${containers_dir}libreportal/frontend/logs" local log_dir="$(webuiDir)/frontend/logs"
local apps_dir="${log_dir}/apps" local apps_dir="${log_dir}/apps"
if [ ! -d "$apps_dir" ]; then if [ ! -d "$apps_dir" ]; then
local result; result=$(createFolders "loud" $sudo_user_name $log_dir $apps_dir) local result; result=$(createFolders "loud" $sudo_user_name $log_dir $apps_dir)

View File

@ -4,7 +4,7 @@
# Ensures essential task system files exist for LibrePortal task management # Ensures essential task system files exist for LibrePortal task management
webuiEnsureTaskFiles() { webuiEnsureTaskFiles() {
local task_dir="${containers_dir}libreportal/frontend/data/tasks" local task_dir="$(webuiDir)/frontend/data/tasks"
# Create tasks directory if it doesn't exist # Create tasks directory if it doesn't exist
if [ ! -d "$task_dir" ]; then if [ ! -d "$task_dir" ]; then

View File

@ -13,7 +13,7 @@ webuiSyncAppIcon() {
local app_name="$1" local app_name="$1"
[[ -z "$app_name" ]] && return 1 [[ -z "$app_name" ]] && return 1
local icons_dir="${containers_dir}libreportal/frontend/core/icons/apps" local icons_dir="$(webuiDir)/frontend/core/icons/apps"
runFileOp mkdir -p "$icons_dir" runFileOp mkdir -p "$icons_dir"
# Icons live in the install template dir — copy_build_context.sh # Icons live in the install template dir — copy_build_context.sh
@ -65,7 +65,7 @@ webuiSyncAppIcons() {
# Only ever removes icons it can match to a missing template, never anything # Only ever removes icons it can match to a missing template, never anything
# else in the directory. # else in the directory.
webuiPruneAppIcons() { webuiPruneAppIcons() {
local icons_dir="${containers_dir}libreportal/frontend/core/icons/apps" local icons_dir="$(webuiDir)/frontend/core/icons/apps"
[[ -d "$icons_dir" ]] || return 0 [[ -d "$icons_dir" ]] || return 0
[[ -d "$install_containers_dir" ]] || return 0 [[ -d "$install_containers_dir" ]] || return 0

View File

@ -6,7 +6,7 @@ webuiUpdateAppLog()
local type=$2 # install or uninstall local type=$2 # install or uninstall
# Create logs directory if it doesn't exist # Create logs directory if it doesn't exist
local log_dir="${containers_dir}libreportal/frontend/logs" local log_dir="$(webuiDir)/frontend/logs"
local apps_dir="${log_dir}/apps" local apps_dir="${log_dir}/apps"
if [ ! -d "$apps_dir" ]; then if [ ! -d "$apps_dir" ]; then
local result; result=$(createFolders "loud" $sudo_user_name $log_dir $apps_dir) local result; result=$(createFolders "loud" $sudo_user_name $log_dir $apps_dir)

View File

@ -13,7 +13,7 @@ atomicWriteWebUI() {
# can write the dockerinstall-owned WebUI/app files. Temp + rename share the # can write the dockerinstall-owned WebUI/app files. Temp + rename share the
# target's directory, so the mv stays atomic (same filesystem, same owner). # target's directory, so the mv stays atomic (same filesystem, same owner).
local op="runInstallOp" wop="runInstallWrite" local op="runInstallOp" wop="runInstallWrite"
if [[ "$target_file" == "$containers_dir"* || "$target_file" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then if pathIsContainerData "$target_file"; then
op="runFileOp"; wop="runFileWrite" op="runFileOp"; wop="runFileWrite"
fi fi

View File

@ -17,7 +17,7 @@ installLibrePortalImageWebUI()
# libreportal.config as a Docker-created directory. The tar copy below can't # libreportal.config as a Docker-created directory. The tar copy below can't
# extract a file over a directory ("Cannot open: File exists") and aborts the # extract a file over a directory ("Cannot open: File exists") and aborts the
# whole source copy, so clear the stub first. # whole source copy, so clear the stub first.
repairStubDirForFile "$containers_dir/libreportal/libreportal.config" "loud" repairStubDirForFile "$(webuiDir)/libreportal.config" "loud"
local result; result=$(copyFolder "$install_containers_dir/libreportal" "$containers_dir" "$sudo_user_name") local result; result=$(copyFolder "$install_containers_dir/libreportal" "$containers_dir" "$sudo_user_name")
checkSuccess "Copy the LibrePortal to the containers folder" checkSuccess "Copy the LibrePortal to the containers folder"
@ -40,6 +40,6 @@ installLibrePortalImageWebUI()
fi fi
isNotice "Building libreportal-service image, this may take a while..." isNotice "Building libreportal-service image, this may take a while..."
local result; result=$(runFileOp docker build -t libreportal-service -f $containers_dir/libreportal/Dockerfile $containers_dir/libreportal >/dev/null 2>&1) local result; result=$(runFileOp docker build -t libreportal-service -f $(webuiDir)/Dockerfile $(webuiDir) >/dev/null 2>&1)
checkSuccess "Built LibrePortal WebUI Docker image" checkSuccess "Built LibrePortal WebUI Docker image"
} }

View File

@ -14,7 +14,7 @@ installLibrePortalWebUITaskService()
[[ "$CFG_REQUIREMENT_WEBUI_SERVICE" == "true" ]] || return 0 [[ "$CFG_REQUIREMENT_WEBUI_SERVICE" == "true" ]] || return 0
local task_processor_script="$install_scripts_dir/task/crontab_task_processor.sh" local task_processor_script="$install_scripts_dir/task/crontab_task_processor.sh"
local task_dir="$containers_dir/libreportal/frontend/data/tasks" local task_dir="$(webuiDir)/frontend/data/tasks"
# Point the processor at the task dir (idempotent). This edits the # Point the processor at the task dir (idempotent). This edits the
# manager-owned install tree, so no privilege is needed. # manager-owned install tree, so no privilege is needed.

View File

@ -49,7 +49,7 @@ _lpRegenOrphanedApp() {
lpRegenWebui() { lpRegenWebui() {
local force="$1" local force="$1"
local gen="${containers_dir}libreportal/frontend/data/apps/generated" local gen="$(webuiDir)/frontend/data/apps/generated"
local apps_json="$gen/apps.json" local apps_json="$gen/apps.json"
local tools_json="$gen/apps-tools.json" local tools_json="$gen/apps-tools.json"

View File

@ -59,7 +59,7 @@ webuiLibrePortalUpdate() {
checkSuccess "Generated system configurations..." checkSuccess "Generated system configurations..."
# Generate categories # Generate categories
local result; result=$(webuiCreateCategories $containers_dir/libreportal/frontend/data) local result; result=$(webuiCreateCategories $(webuiDir)/frontend/data)
checkSuccess "Generated app and config categories..." checkSuccess "Generated app and config categories..."
# Generate LibrePortal app configuration # Generate LibrePortal app configuration