LibrePortal/scripts/docker/app/functions/function_install_app.sh
librelad b7a0743d8b feat(network): add ipInSubnet + IP-only network reset scope
Foundations for network-drift healing:

- ipInSubnet(ip, cidr): prefix-aware CIDR membership (pure bash), so
  stored IPs can be checked against docker's real subnet. Honours the
  actual prefix, so a healthy /16-subnet + /24-ip-range install is not
  mistaken for drift.

- dockerInstallApp now accepts reset_network="ip": re-roll the static IP
  from the current subnet but PRESERVE published host ports (clears only
  IP rows; LIBREPORTAL_RESET_IP_ONLY keeps port_allocate reusing existing
  ports). This is the heal path — a subnet move strands the IP, not the
  port, so we don't churn bookmarks/forwards/proxy upstreams. reset="true"
  still re-rolls both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 15:54:55 +01:00

85 lines
3.8 KiB
Bash
Executable File

#!/bin/bash
dockerInstallApp()
{
local app_name="$1"
local config_variables="$2"
local reset_network="$3"
local app_name_ucfirst="$(tr '[:lower:]' '[:upper:]' <<< ${app_name:0:1})${app_name:1}"
local installFuncName="install${app_name_ucfirst}"
# App slugs are lowercase (e.g. "libreportal"), but installer functions keep
# their own internal capitalisation (e.g. installLibrePortal) — capitalising
# only the first letter can't reproduce that camelCase. If the naive name
# isn't a defined function, resolve it case-insensitively against the real
# function table so any compound brand/app name (LibrePortal, …) just works.
if ! declare -F "$installFuncName" >/dev/null 2>&1; then
local _resolved
_resolved="$(compgen -A function | grep -ixF "install${app_name}" | head -n1)"
[[ -n "$_resolved" ]] && installFuncName="$_resolved"
fi
if ! declare -F "$installFuncName" >/dev/null 2>&1; then
# No per-app install function (no containers/<app>/<app>.sh defining
# one). Fall through to the generic installApp driver — it does the
# standard 10-step sequence and picks up any <slug>_install_* hooks
# the app declares in tools/<slug>_tools.sh. This is the path taken
# by every app that doesn't need bespoke install code: their
# <app>.sh has been deleted and the dispatcher routes them here.
if declare -F installApp >/dev/null 2>&1; then
installApp "$app_name" "$config_variables"
return $?
fi
isError "No installer function found for app '${app_name}' (looked for ${installFuncName}; installApp fallback unavailable)."
return 1
fi
# reset_network: "true" = re-roll IPs AND host ports; "ip" = re-roll the
# static IP only and PRESERVE published ports (the network-heal path — a
# subnet move strands the IP, not the port, so don't churn bookmarks /
# forwards / proxy upstreams). Anything else = preserve both (normal reuse).
if [[ "$reset_network" == "true" || "$reset_network" == "ip" ]]; then
export LIBREPORTAL_RESET_NETWORK=1
[[ "$reset_network" == "ip" ]] && export LIBREPORTAL_RESET_IP_ONLY=1
if declare -f ipRemoveFromDatabase >/dev/null 2>&1; then
ipRemoveFromDatabase "$app_name"
fi
if [[ "$reset_network" != "ip" ]] && declare -f portsRemoveFromDatabase >/dev/null 2>&1; then
portsRemoveFromDatabase "$app_name"
fi
if [[ "$reset_network" == "ip" ]]; then
isNotice "Network reset (IP-only): cleared previous IP allocation for $app_name; ports preserved."
else
isNotice "Network reset: cleared previous IP/port allocations for $app_name."
fi
fi
# Silently update the template config so apps.json regen reflects the saved
# values. The visible "Updated CFG_X" lines come from the deployed-config
# pass inside dockerConfigSetupToContainer.
if [[ -n "$config_variables" ]]; then
local template_config="$install_containers_dir$app_name/$app_name.config"
if [[ -f "$template_config" ]]; then
IFS='|' read -ra config_pairs <<< "$config_variables"
for pair in "${config_pairs[@]}"; do
if [[ "$pair" =~ ^(CFG_[A-Z0-9_]+)=(.*)$ ]]; then
local _value="${BASH_REMATCH[2]//%7C/|}"
updateConfigOption "${BASH_REMATCH[1]}" "$_value" "$template_config" >/dev/null
fi
done
fi
fi
# Create a variable with the name of $app_name and set its value to "i"
declare "${app_name}=i"
"${installFuncName}" "$config_variables"
if declare -f webuiGenerateLibrePortalConfig >/dev/null 2>&1; then
webuiGenerateLibrePortalConfig >/dev/null 2>&1 || true
fi
unset LIBREPORTAL_RESET_NETWORK LIBREPORTAL_RESET_IP_ONLY
}