LibrePortal/containers/traefik/scripts/traefik_install_hooks.sh
librelad 4d7027258d feat(app): Wave B + C — collapse 28 per-app installers onto generic driver
Finishes the installApp refactor started in d941f59 (Wave A). Every app
whose <app>.sh was either pure boilerplate (Wave B) or boilerplate +
small custom logic (Wave C) now routes through the generic driver in
scripts/app/install/app_install.sh; bespoke logic moved to declarative
hooks in containers/<app>/scripts/<app>_install_hooks.sh.

Net: ~4,000 lines of duplicated 10-step sequence gone. From 31 per-app
.sh files (pre-Wave-A) down to 2 intentional keepers.

DELETED outright (pure boilerplate — driver replaces them identically):
  jellyfin, mastodon, focalboard, ipinfo, speedtest, dashy, invidious,
  nextcloud, ollama, vaultwarden, pihole

DELETED + hook-extracted (small bespoke step preserved in a hook):
  bookstack, moneyapp, owncloud, trilium, searxng, gitea, headscale,
  unbound, prometheus, grafana, gluetun, wireguard, jitsimeet, authelia,
  traefik, adguard, onlyoffice

KEPT (intentional special cases):
  crowdsec      — host-app pattern (no docker compose, runs as apt+
                   systemd via installCrowdsecHost; uninstall/stop/
                   restart hooks already live in this file and are
                   invoked by dockerUninstall/Stop/RestartApp directly).
  libreportal   — WebUI bootstrap. Pre-compose image build + post-install
                   webuiLibrePortalUpdate + bootstrap-time suppression of
                   menuShowFinalMessages don't fit the generic flow.

Driver change — scripts/app/install/app_install.sh:
  Moved monitoringToggleAppConfig "$app_name" "docker-compose.yml" from
  the post-start integrations block into the install body at post-compose
  (right after dockerComposeSetupFile, before docker-compose up). The
  toggle edits the compose file on disk — running it after start meant
  the container had already been brought up with the unmodified compose,
  so the metrics endpoint wouldn't reflect CFG_<APP>_MONITORING until
  the next restart. Matches the original ordering in every per-app .sh
  that used to call it inline.

Hook surface (declare-f-gated, silent no-op when absent):
  <slug>_install_pre              before any install work
  <slug>_install_post_setup       after dockerConfigSetupToContainer
  <slug>_install_post_compose     after dockerComposeSetupFile (+ the
                                  shared monitoring toggle on the compose)
  <slug>_install_post_start       after dockerComposeUpdateAndStartApp
  <slug>_install_message_data     echoes extra argv for menuShowFinalMessages
  <slug>_install_post             very last thing, after the final message
  + the existing _uninstall_pre/_post, _stop_post, _restart_post

Notable extractions:
  bookstack  — _install_post_start: probe :PORT_1/login until 200/302,
               then `bookstack:create-admin` inside the container with
               CFG_BOOKSTACK_ADMIN_{EMAIL,PASSWORD}; falls back to the
               seeded admin@admin.com on timeout.
  adguard    — _install_post_start drives the wizard's HTTP API
               (POST /control/install/configure) so the admin doesn't
               click through five pages, then pins the admin bind back
               to 0.0.0.0:3000 (matches the compose mapping) and health
               checks. _install_message_data echoes user/password to
               menuShowFinalMessages.
  authelia   — _install_pre requirements; _install_post_compose copies
               configuration.yml + users_database.yml, substitutes
               theme/domain/host, generates JWT/session/storage secrets,
               toggles monitoring on configuration.yml; _install_post_start
               argon2-hashes the admin password via the container, writes
               users_database.yml, restarts; _install_post echoes creds.
  traefik    — _install_pre prompts for the LE email if CFG_TRAEFIK_EMAIL
               is unset; _install_post_compose copies static + dynamic
               configs, wires CFG_TRAEFIK_DASHBOARD_ACCESS (local-only /
               domain-only / public), toggles monitoring on traefik.yml,
               then traefikUpdateWhitelist + traefikSetupLoginCredentials.
  wireguard  — _install_pre host-conflict guard (/etc/wireguard/params);
               _install_post_compose persists CFG_WIREGUARD_SUBNET,
               resolves WG_HOST (domain+traefik → host_setup, else IP),
               runs runAppCfg wireguard-ip-forward; _install_post_start
               restarts after wg-easy installs its iptables rules.
  jitsimeet  — _install_post_setup downloads the tagged release zip from
               GitHub; _install_post_compose mass-edits the .env and runs
               gen-passwords.sh; _install_post_start rewrites nginx
               default site to usedport1/2 + restart.
  prometheus — _install_post_compose seeds prometheus.yml under
               $containers_dir/prometheus/prometheus/; _install_post_start
               sets 0777 on storage dirs so the container TSDB can write
               regardless of host UID mapping.
  grafana    — _install_pre requirements; _install_post_start 0777 on
               grafana_storage.
  gluetun    — _install_post_start refreshes the provider snapshot,
               reattaches every routed app (the netns container ID is
               stale after gluetun gets recreated), then prompts to
               onboard any existing apps.
  + the smaller bookstack-shape extractions for owncloud (version scrape),
    trilium / searxng (wait-for-first-boot-config), gitea (Prometheus
    bearer token sync), headscale / unbound (config copy), moneyapp
    (Auth.js AUTH_URL), onlyoffice (compose-resolved user/pass into the
    final message).

Manifest + arrays regenerated. Verified end-to-end:
  - bash -n on every hook file + the driver: clean
  - Each hook file sources cleanly in a subshell, exposes only the
    intended functions, flagged lazy-loadable (not eager)
  - Smoke-stubbed install run for jellyfin (pure), nextcloud (pure),
    bookstack (hooked), crowdsec (kept): correct dispatch in all cases —
    deleted apps route to installApp, kept apps still hit their real
    function

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-27 13:26:49 +01:00

100 lines
4.5 KiB
Bash

#!/bin/bash
# Traefik install hooks — interactive email prompt (LE notices), static +
# dynamic config copy, dashboard-access mode wiring (local-only / domain-
# only / public), monitoring toggle on traefik.yml, whitelist + login.
traefik_install_pre()
{
local app_name="$1"
if [[ -z "$CFG_TRAEFIK_EMAIL" || "$CFG_TRAEFIK_EMAIL" == "changeme" || "$CFG_TRAEFIK_EMAIL" == "Change-Me" ]]; then
if [[ "$LIBREPORTAL_NONINTERACTIVE" == "1" ]]; then
isError "CFG_TRAEFIK_EMAIL not set. Run 'libreportal config update CFG_TRAEFIK_EMAIL=you@example.com' first."
return 1
fi
local traefik_email=""
while true; do
isQuestion "Administrator email for LetsEncrypt (cert-expiry notices) : "
read -p "" traefik_email
emailValidation "$traefik_email"
[[ $? -eq 0 ]] && break
isNotice "Please provide a valid email address."
done
updateConfigOption "CFG_TRAEFIK_EMAIL" "$traefik_email"
sourceScanFiles "libreportal_configs"
fi
}
traefik_install_post_compose()
{
local app_name="$1"
local result
result=$(createFolders "loud" $docker_install_user "$containers_dir$app_name/etc" "$containers_dir$app_name/etc/certs" "$containers_dir$app_name/etc/dynamic" "$containers_dir$app_name/etc/dynamic/middlewears")
checkSuccess "Created etc and certs & dynamic Directories"
result=$(copyResource "$app_name" "traefik.yml" "etc")
checkSuccess "Copy Traefik configuration file for $app_name"
result=$(runFileOp sed -i "s|DEBUGLEVEL|$CFG_TRAEFIK_LOGGING|g" "$containers_dir$app_name/etc/traefik.yml")
checkSuccess "Configured Traefik debug level with: $CFG_TRAEFIK_LOGGING for $app_name"
configSetupFileWithData $app_name "traefik.yml" "etc"
# Apply CFG_TRAEFIK_DASHBOARD_ACCESS: rewrites api.insecure + the
# `traefik:` entrypoint in traefik.yml, and (for local-only) prefixes
# the compose port mapping with 127.0.0.1: so :8080 binds to loopback.
local traefik_yml="$containers_dir$app_name/etc/traefik.yml"
local compose_yml="$containers_dir$app_name/docker-compose.yml"
local access="${CFG_TRAEFIK_DASHBOARD_ACCESS:-local-only}"
case "$access" in
local-only)
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1true|' "$traefik_yml"
runFileOp sed -i '/#LIBREPORTAL|PORTS_TAG_1|/ {
/127\.0\.0\.1:/! s|"\([0-9]\+:[0-9]\+\)"|"127.0.0.1:\1"|
}' "$compose_yml"
checkSuccess "Dashboard access: local-only (loopback :8080 + auth-protected domain)"
;;
domain-only)
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1false|' "$traefik_yml"
runFileOp sed -i '/^\s*traefik:\s*$/,/^\s*address:\s*:8080\s*$/d' "$traefik_yml"
checkSuccess "Dashboard access: domain-only (auth-protected via Host route only)"
;;
public)
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1true|' "$traefik_yml"
runFileOp sed -i '/#LIBREPORTAL|PORTS_TAG_1|/ s|"127\.0\.0\.1:\([0-9]\+:[0-9]\+\)"|"\1"|' "$compose_yml"
checkSuccess "Dashboard access: public (unauthenticated :8080 on all interfaces — legacy)"
;;
*)
isNotice "Unknown CFG_TRAEFIK_DASHBOARD_ACCESS='$access'; leaving traefik.yml at defaults."
;;
esac
# Traefik's metrics block lives in traefik.yml; toggle that here. The
# driver already toggled docker-compose.yml.
monitoringToggleAppConfig "$app_name" "etc/traefik.yml"
result=$(copyResource "$app_name" "config.yml" "etc/dynamic")
checkSuccess "Copy Traefik Dynamic config.yml configuration file for $app_name"
result=$(runFileOp sed -i "s|ERRORWEBSITE|$CFG_TRAEFIK_404_SITE|g" "$containers_dir$app_name/etc/dynamic/config.yml")
checkSuccess "Configured Traefik error website with URL: $CFG_TRAEFIK_404_SITE for $app_name"
configSetupFileWithData $app_name "config.yml" "etc/dynamic"
result=$(copyResource "$app_name" "whitelist.yml" "etc/dynamic")
checkSuccess "Copy Traefik Dynamic whitelist.yml configuration file for $app_name"
result=$(copyResource "$app_name" "protectionauth.yml" "etc/dynamic/middlewears")
checkSuccess "Copy Traefik Dynamic protectionauth.yml configuration file for $app_name"
traefikUpdateWhitelist
result=$(copyResource "$app_name" "tls.yml" "etc/dynamic")
checkSuccess "Copy Traefik Dynamic tls.yml configuration file for $app_name"
traefikSetupLoginCredentials
}