The main sweep — ~260 call sites across ~100 files move from string
concatenation on a single root to appDir/storageAppDirs/storageAppConfigs.
On a single-root install the resolved paths are identical, so this is a
no-op until a location is registered.
Enumerators were the interesting half. `for d in "$containers_dir"/*/`
appears in the menus, the registry/artifact scanners and the DNS setup —
and a shell glob cannot list a rootless 751 tree at all, which is the
same bug config_find_file.sh already documents in a comment. Routing them
through storageAppDirs (which enumerates as the owning user) fixes that
alongside the multi-root work.
Three places needed judgement rather than substitution:
db_app_scan.sh deletes database rows and port allocations for apps whose
folder is missing, and reaps "empty" app dirs. With a storage location
unmounted, every app on it looks exactly like that. Each of those
branches now gates on appStorageAvailable first — an app on an unplugged
drive is skipped with a notice, never deleted.
instance_create.sh rewrites cloned hooks so an instance touches its own
directory instead of the base app's. Its sed matched ${containers_dir}<type>,
which this sweep just replaced with $(appDir <type>) — so it would have
silently stopped redirecting, and an instance would have written to the
original's files (the adguard auth adapter case its own comment warns
about). Now matches both appDir forms, verified against bare, quoted,
unrelated-app, legacy and prose cases.
peer_shell/peer_pull streamed and extracted relative to the primary root.
Both now use the app's own root, and peer_shell keeps a single-root
fallback since it runs as a restricted SSH shell with no LibrePortal env.
Also fixes a pre-existing bug found on the way: webui_app_config.sh
tested "$containers_dir/frontend/data/last_update", one level short of the
real tree under the libreportal app dir, so the WebUI refresh trigger
after a config update has never once fired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
147 lines
8.5 KiB
Bash
Executable File
147 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
dockerConfigSetupFileWithData()
|
|
{
|
|
local app_name="$1"
|
|
|
|
if [[ $compose_setup == "default" ]]; then
|
|
local file_name="docker-compose.yml";
|
|
elif [[ $compose_setup == "app" ]]; then
|
|
local file_name="docker-compose.$app_name.yml";
|
|
fi
|
|
|
|
local file_path="$(appDir "$app_name")"
|
|
local full_file_path="$file_path/$file_name"
|
|
|
|
if command -v sqlite3 &> /dev/null && [[ -f "$docker_dir/$db_file" ]]; then
|
|
###############################################
|
|
# General Updates
|
|
###############################################
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "TIMEZONE_TAG" "$CFG_TIMEZONE"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "CATEGORY_TAG" "$app_category"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "TITLE_TAG" "$app_title"
|
|
# Root-path tags. The libreportal compose bind-mounts the host system's
|
|
# configs root and the live-app-data root into the WebUI container, and
|
|
# those paths get baked here so the deployed compose carries absolutes
|
|
# (`/libreportal-system/...`, `/libreportal-containers/...`) — anything
|
|
# left as `*_DATA` would make dockerComposeUp refuse to start.
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "CONFIGS_DIR_TAG" "${configs_dir%/}"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "CONTAINERS_DIR_TAG" "${containers_dir%/}"
|
|
# Generic CFG_<APP>_<KEY> -> #LIBREPORTAL|<APP>_<KEY>_TAG| fill. Covers
|
|
# every app-specific config value (secrets included) with no
|
|
# hand-maintained list — the app-specific block further down is only
|
|
# for the few tags that need computed (non-CFG) values.
|
|
tagsProcessorAppConfigValues "$full_file_path" "$app_name"
|
|
tagsProcessorDockerInstallation "$full_file_path" "$CFG_DOCKER_INSTALL_TYPE" "$CFG_DOCKER_INSTALL_USER"
|
|
tagsProcessorSocketConfiguration "$full_file_path" "$CFG_DOCKER_INSTALL_TYPE" "$CFG_DOCKER_INSTALL_USER" "$docker_rooted_socket"
|
|
# Run the container as the same identity that owns its bind-mounted
|
|
# files (the WebUI writes .auth.json into ./frontend). That identity
|
|
# is install-type dependent and already resolved into
|
|
# $docker_install_user by check_install_type.sh: rooted -> the host
|
|
# $sudo_user_name, rootless -> $CFG_DOCKER_INSTALL_USER. Hardcoding a
|
|
# UID (was 1001) breaks wherever that user's UID differs — the
|
|
# container dies with EACCES on first write and never binds its port.
|
|
# No-op for compose files without a USER_TAG.
|
|
if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rootless" ]]; then
|
|
# Rootless: the daemon runs as the install user, so container UID 0
|
|
# maps to it on the host — it owns the bind-mounts and the rootless
|
|
# socket. Using the host UID instead lands on an unmapped sub-UID
|
|
# (EACCES on writes; and group_add then calls setgroups() with a
|
|
# sub-GID outside the userns → EINVAL, container won't start).
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "USER_TAG" "0:0"
|
|
else
|
|
local container_user="${docker_install_user:-$sudo_user_name}"
|
|
local install_uid install_gid
|
|
install_uid=$(id -u "$container_user" 2>/dev/null)
|
|
install_gid=$(id -g "$container_user" 2>/dev/null)
|
|
if [[ -n "$install_uid" && -n "$install_gid" ]]; then
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "USER_TAG" "${install_uid}:${install_gid}"
|
|
fi
|
|
fi
|
|
tagsProcessorPasswordAndKeyGeneration "$full_file_path"
|
|
tagsProcessorRandomUserGeneration "$full_file_path"
|
|
tagsProcessorHealthcheck "$full_file_path" "$healthcheck"
|
|
|
|
###############################################
|
|
# Public/Traefik Updates
|
|
###############################################
|
|
# Legacy whole-app middleware (single MIDDLEWARE_TAG). Kept until
|
|
# all apps have been converted to the per-port routers below.
|
|
traefikSetupLabelsMiddlewares "$app_name"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MIDDLEWARE_TAG" "$traefik_middlewares"
|
|
|
|
# Per-port middleware (MIDDLEWARE_TAG_1, _2, ...). New apps with
|
|
# one router per Traefik-managed port use this — each port can
|
|
# independently enable basic auth via its login_required column,
|
|
# and Authelia takes precedence when installed.
|
|
tagsProcessorPortMiddlewares "$full_file_path" "$app_name"
|
|
|
|
# Per-port subdomains (DOMAINSUBNAME_TAG_1, _2, ...): one host per
|
|
# Traefik-managed port, so a container can serve unlimited hosts.
|
|
tagsProcessorPortSubdomains "$full_file_path" "$app_name"
|
|
|
|
# Strip (comment out) router blocks for ports that aren't Traefik-managed,
|
|
# so an unfilled DOMAINSUBNAME_DATA_<n> placeholder can never ship.
|
|
tagsProcessorPortRouterBlocks "$full_file_path" "$app_name"
|
|
|
|
tagsProcessorTraefikControl "$full_file_path" "$public"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "DOMAINSUBNAME_TAG" "$host_setup"
|
|
|
|
###############################################
|
|
# Network Updates (IP + Port Systems)
|
|
###############################################
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "DOCKER_NETWORK_TAG" "$CFG_NETWORK_NAME"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "PUBLIC_IP_TAG" "$public_ip_v4"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "NETWORK_SUBNET_TAG" "$CFG_NETWORK_SUBNET"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "NETWORK_MTU_TAG" "$(networkEffectiveMtu)"
|
|
ipUpdateComposeTags "$app_name" "$full_file_path"
|
|
portUpdateComposeTags "$app_name" "$full_file_path"
|
|
tagsProcessorTrustedDomains "$full_file_path"
|
|
tagsProcessorAppUrl "$full_file_path" "$app_name" "$public" "$host_setup" "$public_ip_v4" "$local_ip_v4"
|
|
|
|
###############################################
|
|
# Mail Server Settings
|
|
###############################################
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_ENABLED_TAG" "$CFG_MAIL_ENABLED"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_HOST_TAG" "$CFG_MAIL_HOST"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_PORT_TAG" "$CFG_MAIL_PORT"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_SECURE_TAG" "$CFG_MAIL_SECURE"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_USERNAME_TAG" "$CFG_MAIL_USERNAME"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_PASSWORD_TAG" "$CFG_MAIL_PASSWORD"
|
|
tagsManagerUpdateUniversalTag "$full_file_path" "MAIL_FROM_TAG" "$CFG_MAIL_FROM"
|
|
|
|
###############################################
|
|
# App Specific (per-app hook)
|
|
###############################################
|
|
# An app needing computed (non-CFG) compose tags ships
|
|
# containers/<app>/scripts/<app>_compose_tags.sh defining
|
|
# appSetupComposeTags_<app> (live-sourced by the container scan; called
|
|
# with the compose path, reads host_setup/public_ip_v4/CFG_* from scope).
|
|
# Plain CFG_<APP>_<KEY> values are already filled by
|
|
# tagsProcessorAppConfigValues above, so most apps need no hook.
|
|
local _tags_hook="appSetupComposeTags_${app_name}"
|
|
if declare -F "$_tags_hook" >/dev/null 2>&1; then
|
|
"$_tags_hook" "$full_file_path"
|
|
fi
|
|
|
|
###############################################
|
|
# Network mode (route through a gateway provider, e.g. gluetun)
|
|
###############################################
|
|
# An app may route through a gateway named in CFG_<APP>_NETWORK. The
|
|
# provider owns the wiring via hooks in containers/<provider>/scripts/:
|
|
# appNetworkApplyMode_<provider> "$file" — switch this app's compose
|
|
# appNetworkRegisterPorts_<provider> — refresh the provider's ports
|
|
# so no provider name is hardcoded here.
|
|
local network_var="CFG_${app_name^^}_NETWORK"
|
|
local network_mode="${!network_var:-default}"
|
|
if [[ "$network_mode" != "default" && "$network_mode" != "$app_name" ]]; then
|
|
declare -F "appNetworkApplyMode_${network_mode}" >/dev/null 2>&1 \
|
|
&& "appNetworkApplyMode_${network_mode}" "$full_file_path"
|
|
declare -F "appNetworkRegisterPorts_${network_mode}" >/dev/null 2>&1 \
|
|
&& "appNetworkRegisterPorts_${network_mode}"
|
|
fi
|
|
|
|
else
|
|
isNotice "Database not available, unable to setup app."
|
|
fi
|
|
} |