Merge claude/1

This commit is contained in:
librelad 2026-05-25 15:09:39 +01:00
commit fc2c6a6197
27 changed files with 170 additions and 58 deletions

View File

@ -2,7 +2,7 @@
# Backup Engine - **ADVANCED** Engine-level knobs most users won't need to touch
# ================================================================================
CFG_BACKUP_ENGINE=restic # Default Backup Engine - Fallback engine for new locations (each location can override) [restic:Restic|borg:BorgBackup|kopia:Kopia]
CFG_BACKUP_DEFAULT_PATH=/docker/backups # Default Backup Location - Base directory for locations set to Automatic path mode; each location lives in its own numbered subfolder (<path>/<id>)
CFG_BACKUP_DEFAULT_PATH= # Default Backup Location - Base directory for locations set to Automatic path mode; each location lives in its own numbered subfolder (<path>/<id>). Empty = the LibrePortal backups root (own mount-able).
CFG_BACKUP_STRATEGY=auto # Backup Strategy - How containers are quiesced before snapshotting [auto:Automatic — live where safe, stop otherwise (recommended)|stop-snapshot-start:Stop → snapshot → start (always safe)|pause-snapshot-unpause:Pause → snapshot → unpause (less downtime)|live:Live — snapshot while running (force)]
CFG_BACKUP_VERIFY_AFTER=true # Verify After Backup - Run integrity check after each backup
CFG_BACKUP_VERIFY_DATA_PERCENT=5 # Verify Data Sample % - Percentage of repo data to checksum-verify weekly

View File

@ -32,7 +32,11 @@ const router = express.Router();
const TASKS_DIR = path.join(__dirname, '..', '..', 'frontend', 'data', 'tasks');
const FIFO_PATH = path.join(TASKS_DIR, '.queue.fifo');
const CONTAINERS_DIR = '/docker/containers';
// Host live-app-data root. Provided by the compose env (LP_CONTAINERS_DIR, filled
// from the host's containers root at generation — see scripts/source/paths.sh).
// Falls back to the legacy /docker path so a container that hasn't been recreated
// since the split-layout change keeps working until it is.
const CONTAINERS_DIR = process.env.LP_CONTAINERS_DIR || '/docker/containers';
const APPS_SERVICES_JSON = path.join(__dirname, '..', '..', 'frontend', 'data', 'apps', 'generated', 'apps-services.json');
// =====================================================================

View File

@ -31,6 +31,7 @@ services:
environment:
FRONTEND_PATH: /data/frontend
LIBREPORTAL_CONFIG_PATH: /app/libreportal.config
LP_CONTAINERS_DIR: CONTAINERS_DIR_DATA #LIBREPORTAL|CONTAINERS_DIR_TAG|CONTAINERS_DIR_DATA
TZ: TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
labels:
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA

57
init.sh
View File

@ -110,22 +110,47 @@ lp_lib_dir="/usr/local/lib/libreportal"
command_script="$lp_lib_dir/libreportal"
command_symlink="/usr/local/bin/libreportal"
# Directories
docker_dir="/docker"
containers_dir="$docker_dir/containers/"
ssl_dir="$docker_dir/ssl/"
ssh_dir="$docker_dir/ssh/"
wireguard_dir="$docker_dir/wireguard/"
logs_dir="$docker_dir/logs/"
configs_dir="$docker_dir/configs/"
backup_dir="$docker_dir/backups"
restore_dir="$docker_dir/restore"
migrate_dir="$docker_dir/migrate"
# Install Scripts
script_dir="$docker_dir/install"
install_configs_dir="$script_dir/configs/"
install_containers_dir="$script_dir/containers/"
install_scripts_dir="$script_dir/scripts/"
# Directories — three independently-relocatable roots (see scripts/source/paths.sh
# for the canonical description). Defaults below; overridden by --system-dir /
# --containers-dir / --backups-dir (parsed in the flag loop) via the LP_*_DIR
# vars. init.sh derives inline (kept in sync with paths.sh) because the bare
# /root/init.sh reinstall copy has no scripts/ alongside to source.
# LP_SYSTEM_DIR — manager-owned control plane (configs/logs/install/db/…)
# LP_CONTAINERS_DIR — container-user-owned live app data
# LP_BACKUPS_DIR — container-user-owned backup repos (own mount-able)
libreportalDerivePaths() {
# Transitional compat: an existing install (legacy single /docker tree,
# identified by its config marker) keeps using /docker until a deliberate
# reinstall — so deploying new code never strands a running box.
if [[ -z "${LP_SYSTEM_DIR:-}" ]]; then
if [[ ! -e /libreportal-system && -f /docker/configs/general/general_docker_install ]]; then
LP_SYSTEM_DIR=/docker
: "${LP_CONTAINERS_DIR:=/docker/containers}"
: "${LP_BACKUPS_DIR:=/docker/backups}"
else
LP_SYSTEM_DIR=/libreportal-system
fi
fi
: "${LP_CONTAINERS_DIR:=/libreportal-containers}"
: "${LP_BACKUPS_DIR:=/libreportal-backups}"
docker_dir="$LP_SYSTEM_DIR"
system_dir="$LP_SYSTEM_DIR"
configs_dir="$LP_SYSTEM_DIR/configs/"
logs_dir="$LP_SYSTEM_DIR/logs/"
ssl_dir="$LP_SYSTEM_DIR/ssl/"
ssh_dir="$LP_SYSTEM_DIR/ssh/"
wireguard_dir="$LP_SYSTEM_DIR/wireguard/"
migrate_dir="$LP_SYSTEM_DIR/migrate"
restore_dir="$LP_SYSTEM_DIR/restore"
script_dir="$LP_SYSTEM_DIR/install"
install_configs_dir="$script_dir/configs/"
install_containers_dir="$script_dir/containers/"
install_scripts_dir="$script_dir/scripts/"
containers_dir="$LP_CONTAINERS_DIR/"
backup_dir="$LP_BACKUPS_DIR"
}
libreportalDerivePaths
# Parse flags
init_shift_count=0

View File

@ -52,7 +52,7 @@ backupLocationsMigrate()
runFileOp chown "$owner":"$owner" "$loc_dir"
runFileOp chmod 0700 "$loc_dir"
local old_pass="/docker/configs/security/restic/loc_${idx}.pass"
local old_pass="${configs_dir}security/restic/loc_${idx}.pass"
local pass_value=""
if [[ -f "$old_pass" ]]; then
pass_value=$(runFileOp cat "$old_pass" | tr -d '\n\r')
@ -75,7 +75,7 @@ backupLocationsMigrate()
replacePlainPasswords "$cfg_file"
fi
local old_kopia="/docker/configs/security/restic/kopia/loc_${idx}.config"
local old_kopia="${configs_dir}security/restic/kopia/loc_${idx}.config"
local new_kopia
new_kopia=$(backupLocationKopiaConfig "$idx")
if [[ -f "$old_kopia" ]]; then
@ -85,7 +85,7 @@ backupLocationsMigrate()
isNotice "Moved kopia state for location $idx$new_kopia"
fi
local old_ssh="/docker/configs/security/restic/ssh/loc_${idx}.key"
local old_ssh="${configs_dir}security/restic/ssh/loc_${idx}.key"
local new_ssh
new_ssh=$(backupLocationSshKey "$idx")
if [[ -f "$old_ssh" ]]; then

View File

@ -69,7 +69,7 @@ backupLocationResolvedPath()
if [[ "$mode" == "auto" ]]; then
# Base dir is the configurable Default Backup Location (Backup Engine
# config); each location gets its own numbered subfolder.
local base="${CFG_BACKUP_DEFAULT_PATH:-$docker_dir/backups}"
local base="${CFG_BACKUP_DEFAULT_PATH:-${backup_dir:-$docker_dir/backups}}"
echo "${base%/}/${idx}"
else
resticLocationField "$idx" PATH

View File

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

View File

@ -21,6 +21,9 @@ tagsProcessorStandardReplacements()
tagsManagerUpdateUniversalTag "$full_file_path" "DOMAINSUBNAME_TAG" "$host_setup"
tagsManagerUpdateUniversalTag "$full_file_path" "TIMEZONE_TAG" "$CFG_TIMEZONE"
tagsManagerUpdateUniversalTag "$full_file_path" "DOCKER_NETWORK_TAG" "$CFG_NETWORK_NAME"
# Host live-app-data root, passed into the WebUI container (only the
# libreportal compose carries this tag; "only update tags that exist").
tagsManagerUpdateUniversalTag "$full_file_path" "CONTAINERS_DIR_TAG" "${containers_dir%/}"
isSuccessful "Standard LibrePortal tag replacements applied using universal tag manager"
}

View File

@ -4,7 +4,7 @@
crontabClean()
{
# Remove old backup log entries
data_to_remove=" >> /docker/logs/backup.log 2>&1"
data_to_remove=" >> ${logs_dir}${backup_log_file} 2>&1"
if crontab -l 2>/dev/null | grep -q "$data_to_remove"; then
crontab -l 2>/dev/null | sed "s|$data_to_remove||g" | crontab -

View File

@ -16,13 +16,18 @@ if [[ "$script_check_processor_flag" == "start_script" ]]; then
# them every privileged op is "command not found". Same bootstrap as
# crontab_task_processor.sh. These files are pure function/var defs, safe to
# source.
LP_SCRIPTS="${install_scripts_dir:-/docker/install/scripts/}"
LP_DOCKER_CFG="/docker/configs/general/general_docker_install"
# Self-locate the scripts dir, then load the relocatable path roots (sets
# docker_dir/containers_dir/configs_dir/…). Same bootstrap as the task processor.
LP_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
LP_SCRIPTS="${install_scripts_dir:-$(cd "$LP_SELF_DIR/../.." 2>/dev/null && pwd)/}"
[[ -f "${LP_SCRIPTS}source/paths.sh" ]] && source "${LP_SCRIPTS}source/paths.sh"
LP_SCRIPTS="${install_scripts_dir:-$LP_SCRIPTS}"
LP_DOCKER_CFG="${configs_dir:-/libreportal-system/configs/}general/general_docker_install"
[[ -f "$LP_DOCKER_CFG" ]] && \
eval "$(grep -E '^CFG_DOCKER_INSTALL_(TYPE|USER)=' "$LP_DOCKER_CFG" | sed 's/[[:space:]]*#.*//')"
: "${sudo_user_name:=libreportal}"
: "${containers_dir:=/docker/containers/}"
: "${docker_dir:=/docker}"
: "${containers_dir:=/libreportal-containers/}"
: "${docker_dir:=/libreportal-system}"
for _lp_f in docker/command/run_privileged.sh \
docker/command/docker_run_install.sh \
checks/requirements/check_install_type.sh; do
@ -37,7 +42,7 @@ command -v resolveDockerInstallUser >/dev/null 2>&1 && resolveDockerInstallUser
# Essential configuration (keeping your original structure)
TASK_DIR=""
if [[ "$TASK_DIR" == "" ]]; then
TASK_DIR="/docker/containers/libreportal/frontend/data/tasks"
TASK_DIR="${containers_dir:-/libreportal-containers/}libreportal/frontend/data/tasks"
fi
LOCK_FILE="$TASK_DIR/task_processor.lock"

View File

@ -32,13 +32,19 @@ script_task_processor_flag="$1"
# docker-install-owned task dir fails ("command not found") and tasks loop
# forever. Load them here — these files are pure function/var defs, safe to
# source, no side effects.
LP_SCRIPTS="${install_scripts_dir:-/docker/install/scripts/}"
LP_DOCKER_CFG="/docker/configs/general/general_docker_install"
# Self-locate the scripts dir (where systemd launched us) so we can load the
# relocatable path roots — paths.sh sets docker_dir/containers_dir/configs_dir/…
# (the data + backup roots can't be derived from our own location).
LP_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
LP_SCRIPTS="${install_scripts_dir:-$(cd "$LP_SELF_DIR/../.." 2>/dev/null && pwd)/}"
[[ -f "${LP_SCRIPTS}source/paths.sh" ]] && source "${LP_SCRIPTS}source/paths.sh"
LP_SCRIPTS="${install_scripts_dir:-$LP_SCRIPTS}"
LP_DOCKER_CFG="${configs_dir:-/libreportal-system/configs/}general/general_docker_install"
[[ -f "$LP_DOCKER_CFG" ]] && \
eval "$(grep -E '^CFG_DOCKER_INSTALL_(TYPE|USER)=' "$LP_DOCKER_CFG" | sed 's/[[:space:]]*#.*//')"
: "${sudo_user_name:=libreportal}"
: "${containers_dir:=/docker/containers/}"
: "${docker_dir:=/docker}"
: "${containers_dir:=/libreportal-containers/}"
: "${docker_dir:=/libreportal-system}"
for _lp_f in docker/command/run_privileged.sh \
docker/command/docker_run_install.sh \
checks/requirements/check_install_type.sh; do
@ -50,7 +56,7 @@ command -v resolveDockerInstallUser >/dev/null 2>&1 && resolveDockerInstallUser
# PATHS & CONSTANTS
# ============================================================================
TASK_DIR="${TASK_DIR:-/docker/containers/libreportal/frontend/data/tasks}"
TASK_DIR="${TASK_DIR:-${containers_dir:-/libreportal-containers/}libreportal/frontend/data/tasks}"
LOCK_FILE="$TASK_DIR/.processor.lock"
FIFO="$TASK_DIR/.queue.fifo"
LOG_FILE="$TASK_DIR/task_processor.log"

View File

@ -101,7 +101,7 @@ _runRootHelper() {
if [[ -x "$helper" ]]; then
sudo "$helper" "$@"
elif [[ $EUID -eq 0 ]]; then
bash "${script_dir:-/docker/install}/scripts/system/$name" "$@"
bash "${script_dir:-/libreportal-system/install}/scripts/system/$name" "$@"
else
sudo "$helper" "$@"
fi

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ copyFolders()
# Write as the destination's owner — no root, no chown (see copyFile).
local op="runInstallOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == /docker/containers/* ]] && op="runFileOp"
[[ "$save_dir" == "$containers_dir"* || "$save_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]] && op="runFileOp"
local subdirs=($(find "$source" -mindepth 1 -maxdepth 1 -type d))
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
# chown-to-another-user the unprivileged runtime can't do. Mirrors
# createTouch; the $user_name hint is advisory for these paths.
if [[ "$clean_dir" == "$containers_dir"* || "$clean_dir" == /docker/containers/* ]]; then
if [[ "$clean_dir" == "$containers_dir"* || "$clean_dir" == "${LP_CONTAINERS_DIR:-/libreportal-containers}"/* ]]; then
if [ ! -d "$dir_path" ]; then
local result=$(runFileOp mkdir -p "$dir_path")
[ "$silent_flag" == "loud" ] && checkSuccess "Creating $folder_name directory"

View File

@ -63,17 +63,17 @@ runReinstall()
AUTH_HTTP_REPO_URL="http://${CFG_GIT_USER}:${CFG_GIT_KEY}@${CLEAN_GIT_URL}.git"
# Try HTTPS first
if runAsManager git clone -q "$AUTH_HTTPS_REPO_URL" "/docker/install" 2>/dev/null; then
runSystem cp -f /docker/install/init.sh /root/
echo "SUCCESS: Git repository cloned via HTTPS into /docker/install."
if runAsManager git clone -q "$AUTH_HTTPS_REPO_URL" "$script_dir" 2>/dev/null; then
runSystem cp -f "$script_dir/init.sh" /root/
echo "SUCCESS: Git repository cloned via HTTPS into $script_dir."
echo ""
echo "SUCCESS: Reinstallation complete, you can now run the "libreportal run" command."
echo ""
else
# If HTTPS fails, try HTTP
if runAsManager git clone -q "$AUTH_HTTP_REPO_URL" "/docker/install" 2>/dev/null; then
runSystem cp -f /docker/install/init.sh /root/
echo "SUCCESS: Git repository cloned via HTTP into /docker/install."
if runAsManager git clone -q "$AUTH_HTTP_REPO_URL" "$script_dir" 2>/dev/null; then
runSystem cp -f "$script_dir/init.sh" /root/
echo "SUCCESS: Git repository cloned via HTTP into $script_dir."
echo ""
echo "SUCCESS: Reinstallation complete, you can now run the "libreportal run" command."
echo ""

View File

@ -3,7 +3,7 @@
setupHeadscaleGenerateAuthKey()
{
headscale_preauthkey=""
local temp_key_file="/docker/key.txt"
local temp_key_file="${docker_dir:-/libreportal-system}/key.txt"
local CFG_INSTALL_NAME=$(echo "$CFG_INSTALL_NAME" | tr '[:upper:]' '[:lower:]')
dockerCommandRun "docker exec headscale headscale preauthkeys create -e 1h -u $CFG_INSTALL_NAME" > "$temp_key_file" 2>&1

View File

@ -5,7 +5,7 @@
# mount set takes effect.
crowdsecToggleLibrePortalLogMounts() {
local mode="$1"
local compose="/docker/containers/libreportal/docker-compose.yml"
local compose="${containers_dir}libreportal/docker-compose.yml"
[[ -f "$compose" ]] || return 0
case "$mode" in
@ -24,7 +24,7 @@ crowdsecToggleLibrePortalLogMounts() {
if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^libreportal-service$'; then
isNotice "Recreating libreportal so log mount toggle takes effect..."
( cd /docker/containers/libreportal && runAsManager docker compose up -d >/dev/null 2>&1 ) || true
( cd "${containers_dir}libreportal" && runAsManager docker compose up -d >/dev/null 2>&1 ) || true
fi
}
@ -215,7 +215,7 @@ installCrowdsecHost()
# (use the rotate Tools action for that); this is a visibility
# surface, not the auth source of truth.
local key_file="/etc/crowdsec/traefik_bouncer.key"
local cfg_file="/docker/configs/security/security_crowdsec"
local cfg_file="${configs_dir}security/security_crowdsec"
if ! runSystem cscli bouncers list -o raw 2>/dev/null | grep -q '^traefik-bouncer'; then
local bouncer_key

View File

@ -22,7 +22,7 @@ installResticHost()
installResticMigrateLegacyPasswords()
{
local pass_dir="/docker/configs/security/restic"
local pass_dir="${configs_dir}security/restic"
[[ ! -d "$pass_dir" ]] && return 0
declare -A legacy_map=(

View File

@ -95,6 +95,7 @@ EOF
"source/files/app_files.sh"|"source/files/cli_files.sh"| \
"source/loading/check_files.sh"|"source/loading/initilize_files.sh"| \
"source/loading/scan_files.sh"|"source/load_sources.sh"| \
"source/paths.sh"| \
"source/files/generate_arrays.sh")
continue
;;

62
scripts/source/paths.sh Normal file
View File

@ -0,0 +1,62 @@
#!/bin/bash
#
# LibrePortal path roots — single source of truth for the (relocatable) layout.
#
# Three independently-placeable roots, each owned by exactly one principal:
# LP_SYSTEM_DIR control plane — manager (libreportal) owned, 750
# configs/ logs/ install/ database.db ssl/ ssh/ migrate/ restore/
# LP_CONTAINERS_DIR live app data — container user (dockerinstall) owned (rootless)
# LP_BACKUPS_DIR restic/kopia repos — container user owned (separable / own mount)
#
# The roots come from the environment when set (the install bakes them into the
# task-processor systemd unit, and the CLI/app inherit them from init.sh), else
# they default to /libreportal-*. A custom location is chosen at INSTALL time and
# baked by root — never read at runtime from a manager-writable config.
#
# SECURITY: the root-owned helpers under /usr/local/lib/libreportal/ do NOT source
# this file. They get the paths baked in at install (sed placeholders), so the
# manager cannot redirect a root `chown`/`chmod` by editing config. This file is
# only for the manager-run code (app, CLI, task processor), which runs without
# extra privilege.
#
# Mirror copy: init.sh derives the same vars inline (it is self-contained for the
# bare /root/init.sh reinstall case, where scripts/ isn't alongside). Keep the two
# derivations in sync.
# --- Resolve the three roots ------------------------------------------------
# Transitional compat: an EXISTING install (the legacy single /docker tree,
# identified by its config marker) keeps using /docker until a deliberate
# reinstall to the split layout — so deploying new code never strands a running
# box. Fresh installs (no marker) get the /libreportal-* split.
if [[ -z "${LP_SYSTEM_DIR:-}" ]]; then
if [[ ! -e /libreportal-system && -f /docker/configs/general/general_docker_install ]]; then
LP_SYSTEM_DIR=/docker
: "${LP_CONTAINERS_DIR:=/docker/containers}"
: "${LP_BACKUPS_DIR:=/docker/backups}"
else
LP_SYSTEM_DIR=/libreportal-system
fi
fi
: "${LP_CONTAINERS_DIR:=/libreportal-containers}"
: "${LP_BACKUPS_DIR:=/libreportal-backups}"
# --- Derived: system tree (manager-owned). docker_dir is the legacy name. ---
docker_dir="$LP_SYSTEM_DIR"
system_dir="$LP_SYSTEM_DIR"
configs_dir="$LP_SYSTEM_DIR/configs/"
logs_dir="$LP_SYSTEM_DIR/logs/"
ssl_dir="$LP_SYSTEM_DIR/ssl/"
ssh_dir="$LP_SYSTEM_DIR/ssh/"
wireguard_dir="$LP_SYSTEM_DIR/wireguard/"
migrate_dir="$LP_SYSTEM_DIR/migrate"
restore_dir="$LP_SYSTEM_DIR/restore"
script_dir="$LP_SYSTEM_DIR/install"
install_configs_dir="$script_dir/configs/"
install_containers_dir="$script_dir/containers/"
install_scripts_dir="$script_dir/scripts/"
# --- Derived: data tree (container-user-owned) — the root IS the dir ---------
containers_dir="$LP_CONTAINERS_DIR/"
# --- Derived: backups tree (container-user-owned; own mount-able) -----------
backup_dir="$LP_BACKUPS_DIR"

View File

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

View File

@ -34,6 +34,11 @@ showRunHelp()
initLibrePortal()
{
# Load the relocatable path roots up front (sets logs_dir/docker_dir/…) — the
# install-log below needs logs_dir before load_sources runs. cwd is the install
# dir, so the relative path resolves.
[[ -f "scripts/source/paths.sh" ]] && source "scripts/source/paths.sh"
# For the full application loading
if [[ "$initial_command1" == "run" ]]; then
if [[ -z "$initial_command2" ]]; then
@ -46,8 +51,8 @@ initLibrePortal()
# Capture the install run to a log so credentials/URLs can be recovered
# after we clear the screen at the end.
if [[ "$initial_command2" == "install" ]]; then
install_log_path="/docker/logs/install-$(date +%Y%m%d-%H%M%S).log"
sudo mkdir -p /docker/logs 2>/dev/null
install_log_path="${logs_dir:-/libreportal-system/logs/}install-$(date +%Y%m%d-%H%M%S).log"
sudo mkdir -p "${logs_dir:-/libreportal-system/logs/}" 2>/dev/null
sudo touch "$install_log_path" 2>/dev/null
# Own it by whoever runs the install (the manager under Model A) so the
# tee below — which runs as that user, not root — can append. A