#!/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//.sh defining # one). Fall through to the generic installApp driver — it does the # standard 10-step sequence and picks up any _install_* hooks # the app declares in tools/_tools.sh. This is the path taken # by every app that doesn't need bespoke install code: their # .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 }