Back up existing files before every service overwrites them
Confirmed live: install_frigate()'s fresh-install path overwrote a working, hand-crafted docker-compose.yml (Frigate + mosquitto + frigate-notify) with zero backup, because that file's shape didn't match what frigate.sh's own "existing install" detection knew how to recognize. Every service's own detection is a judgment call about what counts as "already installed" and can miss a real setup built outside this repo's conventions. lib/common.sh gains backup_if_exists(FILE) — copies FILE to FILE.bak.<timestamp> if it exists, no-ops otherwise (including DRY_RUN). Applied before every service's own `cat > docker-compose.yml`/`cat > .env` write across all 60 services that do one (115 call sites), plus a matching standalone-mode stub added to every service's own bootstrap block, same convention already used for port_in_use/find_free_port. This doesn't replace a service's own update/fresh-reinstall detection — it's the safety net underneath it, so a wrong detection costs a .bak file to restore from instead of the original silently disappearing. Also fixes the actual gap that surfaced this: services/frigate.sh's Authelia offer only checked for Authelia installed locally on Frigate's own box, which is never true for a dedicated NVR box with no local Caddy either (the common shape — Caddy lives elsewhere, snippet-generation mode already handles that). Now offers Authelia protection unconditionally and, when Authelia isn't local, asks whether it lives on the same machine as Caddy (still "import authelia", since that's local to wherever Caddy ends up) or on a genuinely separate third machine (the explicit header-pinned forward_auth form, per CLAUDE.md's "forward_auth to a remote Authelia" note, needed because a bare authelia:9091 shortcut only works one hop).
This commit is contained in:
@@ -802,6 +802,32 @@ write_readme() {
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/README.md" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Copies FILE to FILE.bak.<timestamp> if it already exists, right before a
|
||||
# caller is about to overwrite it with a fresh `cat > FILE` heredoc. No-ops
|
||||
# in DRY_RUN and silently no-ops if FILE doesn't exist yet (first install,
|
||||
# nothing to save) — safe to call unconditionally right before every such
|
||||
# write, fresh install or not.
|
||||
#
|
||||
# Confirmed live: install_frigate()'s fresh-install path overwrote a
|
||||
# working, hand-crafted multi-container docker-compose.yml (Frigate +
|
||||
# mosquitto + frigate-notify) with zero backup, because that file's shape
|
||||
# didn't match what the service's own "existing install" detection knew
|
||||
# how to recognize. Every service's own detection logic is a judgment call
|
||||
# about what counts as "already installed" and can miss a real setup built
|
||||
# outside this repo's own conventions — this exists as the safety net
|
||||
# underneath that judgment call, not a replacement for it: call it right
|
||||
# before any `cat > FILE` that could clobber something a user already has,
|
||||
# so a wrong detection costs a `.bak` file to restore from instead of the
|
||||
# original silently disappearing.
|
||||
backup_if_exists() {
|
||||
local file="$1"
|
||||
[ "$DRY_RUN" = true ] && return 0
|
||||
[ -f "$file" ] || return 0
|
||||
local backup="${file}.bak.$(date +%Y%m%d-%H%M%S)"
|
||||
cp -p "$file" "$backup" 2>/dev/null \
|
||||
&& log_info "Backed up existing $(basename "$file") to $(basename "$backup")"
|
||||
}
|
||||
|
||||
# ── Host port collision avoidance (shared by every service that publishes a
|
||||
# fixed host port) ────────────────────────────────────────────────────────────
|
||||
# With 70+ services in this repo, several ship the same default port (e.g.
|
||||
|
||||
@@ -180,6 +180,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -388,6 +393,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << AB_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -405,6 +411,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
AB_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << AB_ENV
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -170,6 +170,11 @@ CBLOCK
|
||||
[[ "${DRY_RUN:-false}" == "true" ]] && return 0
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
local _len="${1:-32}"
|
||||
@@ -368,6 +373,7 @@ install_ai-gpu() {
|
||||
sed -i "s|America/New_York|$TZ_VAL|g" {} \;
|
||||
fi
|
||||
|
||||
backup_if_exists "$IMAGE_GEN_DIR/.env"
|
||||
cat > "$IMAGE_GEN_DIR/.env" << IMGENV
|
||||
# InvokeAI — image generation
|
||||
TZ=${TZ_VAL}
|
||||
@@ -407,6 +413,7 @@ IMGENV
|
||||
local WEBUI_SECRET
|
||||
WEBUI_SECRET="$(generate_password 32)"
|
||||
|
||||
backup_if_exists "$LLM_DIR/.env"
|
||||
cat > "$LLM_DIR/.env" << LLMENV
|
||||
# Ollama + Open WebUI + SearXNG
|
||||
TZ=${TZ_VAL}
|
||||
@@ -441,6 +448,7 @@ LLMENV
|
||||
fi
|
||||
fi
|
||||
|
||||
backup_if_exists "$PORTAL_DIR/.env"
|
||||
cat > "$PORTAL_DIR/.env" << PORTALENV
|
||||
# AI Portal — GPU stack swap controller
|
||||
TZ=${TZ_VAL}
|
||||
|
||||
@@ -174,6 +174,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -240,6 +245,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << ABCOMPOSE
|
||||
name: archivebox
|
||||
|
||||
@@ -262,6 +268,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
ABCOMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ABENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
ABENV
|
||||
|
||||
@@ -188,6 +188,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -278,6 +283,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << ARM_COMPOSE
|
||||
name: arm
|
||||
|
||||
@@ -306,6 +312,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
ARM_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ARM_ENV
|
||||
ARM_OUTPUT=$ARM_OUTPUT
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -255,6 +255,11 @@ CBLOCK
|
||||
[[ "${DRY_RUN:-false}" == "true" ]] && return 0
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
local _len="${1:-32}"
|
||||
@@ -1588,6 +1593,7 @@ _asterisk_write_compose() {
|
||||
"
|
||||
[[ "$USE_EMBEDDED_COTURN" != true ]] && _COTURN_DEPENDS="" && _COTURN_SERVICE=""
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EOF
|
||||
name: PROJECT_NAME_PLACEHOLDER
|
||||
|
||||
@@ -2727,6 +2733,7 @@ install_asterisk() {
|
||||
# WireGuard/Tailscale) on a subnet this box isn't directly attached to."
|
||||
fi
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ENV
|
||||
# ── Domain ────────────────────────────────────────────────────
|
||||
# ${_domain_comment}
|
||||
|
||||
@@ -180,6 +180,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -379,6 +384,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << ABS_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -400,6 +406,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
ABS_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ABS_ENV
|
||||
AUDIOBOOKS_PATH=$AUDIOBOOKS_PATH
|
||||
PODCASTS_PATH=./podcasts
|
||||
|
||||
@@ -186,6 +186,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -408,6 +413,7 @@ install_authelia() {
|
||||
cd "$AUTHELIA_DIR" || return 1
|
||||
|
||||
# ── .env ─────────────────────────────────────────────────────────────────
|
||||
backup_if_exists .env
|
||||
cat > .env << AUTHELIA_ENV
|
||||
MY_DOMAIN=${AUTHELIA_DOMAIN}
|
||||
SMTP_USER=${AUTHELIA_SMTP_USER}
|
||||
@@ -416,6 +422,7 @@ TZ=${AUTHELIA_TZ}
|
||||
AUTHELIA_ENV
|
||||
|
||||
# ── docker-compose.yml (quoted heredoc: ${SMTP_USER} resolved by compose/.env) ──
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << 'AUTHELIA_COMPOSE'
|
||||
name: authelia
|
||||
|
||||
|
||||
@@ -120,6 +120,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
local _companion; _companion="$(dirname "${BASH_SOURCE[0]}")/beszel.md"
|
||||
[ -f "$_companion" ] && cat "$_companion" >> "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -458,6 +463,7 @@ networks:
|
||||
# profile recognizes. security_opt: apparmor:unconfined below is
|
||||
# Beszel's own documented fix (beszel.dev/guide/systemd#apparmor-error)
|
||||
# — confirmed live, this exact error on a real box.
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << BESZEL_COMPOSE
|
||||
name: beszel
|
||||
|
||||
@@ -504,6 +510,7 @@ BESZEL_COMPOSE
|
||||
# README). Not threaded through automatically here because Caddy setup
|
||||
# (below) happens after this file is written, same ordering every
|
||||
# other service in this repo uses for its own Caddy prompt.
|
||||
backup_if_exists .env
|
||||
cat > .env << BESZEL_ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
@@ -678,6 +685,7 @@ install_beszel-agent() {
|
||||
# and there's no caddy_net to conditionally join since this box never
|
||||
# runs a web UI of its own. See that function's own comment for why the
|
||||
# systemd/dbus/sensor mounts below matter (Services/Temp columns).
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << AGENT_COMPOSE
|
||||
name: beszel-agent
|
||||
|
||||
@@ -703,6 +711,7 @@ services:
|
||||
- /sys/class/thermal:/sys/class/thermal:ro
|
||||
AGENT_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << AGENT_ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
HUB_URL=$HUB_URL
|
||||
|
||||
@@ -192,6 +192,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -279,6 +284,7 @@ install_caddy() {
|
||||
|
||||
cd "$CADDY_DIR" || return 1
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << 'CADDY_COMPOSE'
|
||||
name: caddy
|
||||
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -242,6 +247,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << CW_COMPOSE
|
||||
name: calibre-web
|
||||
|
||||
@@ -264,6 +270,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
CW_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << CW_ENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
CW_ENV
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -243,6 +248,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << CD_COMPOSE
|
||||
name: changedetection
|
||||
|
||||
@@ -272,6 +278,7 @@ ${_CADDY_NET_BLOCK} depends_on:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
CD_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << CD_ENV
|
||||
# Changedetection.io environment — edit before starting if needed
|
||||
BASE_URL=https://changes.${SITE_DOMAIN}
|
||||
|
||||
@@ -174,6 +174,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -212,6 +217,7 @@ install_ddclient() {
|
||||
|
||||
local TZ_VAL; TZ_VAL="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << 'DDCLIENT_COMPOSE'
|
||||
name: ddclient
|
||||
|
||||
@@ -229,6 +235,7 @@ services:
|
||||
- ./config:/config
|
||||
DDCLIENT_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << DDCLIENT_ENV
|
||||
TZ=$TZ_VAL
|
||||
DDCLIENT_ENV
|
||||
|
||||
@@ -178,6 +178,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -477,6 +482,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << DRUM_COMPOSE
|
||||
name: drum-rhythm-game
|
||||
|
||||
@@ -492,6 +498,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
DRUM_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << DRUM_ENV
|
||||
CADDY_NET=${SITE_CADDY_NET}
|
||||
DRUM_ENV
|
||||
|
||||
@@ -189,6 +189,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -352,6 +357,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EMBY_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -377,6 +383,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
EMBY_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << EMBY_ENV
|
||||
MEDIA_PATH=$MEDIA_PATH
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -315,6 +320,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << FB_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -334,6 +340,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
FB_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << FB_ENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
FB_ENV
|
||||
|
||||
@@ -199,6 +199,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -339,6 +344,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << FMD_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -357,6 +363,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
FMD_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << FMD_ENV
|
||||
FMD_REGISTRATIONTOKEN=$FMD_TOKEN
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -207,6 +207,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -365,6 +370,7 @@ install_frigate-audio() {
|
||||
|
||||
# ── .env ──────────────────────────────────────────────────────────────────
|
||||
log_info "Writing .env..."
|
||||
backup_if_exists "$DIR/.env"
|
||||
cat > "$DIR/.env" << ENVEOF
|
||||
# Frigate audio stack — generated by setup.sh
|
||||
# DO NOT commit this file — it contains credentials.
|
||||
@@ -432,6 +438,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists "$DIR/docker-compose.yml"
|
||||
cat > "$DIR/docker-compose.yml" << 'COMPOSEEOF'
|
||||
# Frigate NVR + Mosquitto MQTT + frigate-notify
|
||||
# Generated by ubuntu-post-install setup.sh
|
||||
|
||||
@@ -174,6 +174,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -234,6 +239,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << FN_COMPOSE
|
||||
name: frigate-notify
|
||||
|
||||
|
||||
+66
-4
@@ -202,6 +202,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -626,6 +631,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << FRIGATE_COMPOSE
|
||||
name: frigate
|
||||
|
||||
@@ -673,16 +679,71 @@ FRIGATE_COMPOSE
|
||||
# reverse_proxy block) stops that trust from being spoofed by a
|
||||
# request that reaches Frigate's published host port directly,
|
||||
# bypassing Caddy/Authelia entirely.
|
||||
# Whether to even OFFER this can't just check "$DOCKER_DIR/authelia"
|
||||
# locally — Frigate's own box very often has no local Caddy at all
|
||||
# (configure_caddy_for_service falls back to writing a snippet for
|
||||
# a remote Caddy machine to pick up, confirmed live: this is the
|
||||
# normal shape for a dedicated NVR box), in which case Authelia, if
|
||||
# it exists anywhere, lives on THAT remote Caddy machine instead —
|
||||
# a box this script has no filesystem access to inspect. Default to
|
||||
# "y" only when local Authelia is actually confirmed; otherwise still
|
||||
# offer it (default "n") and sort out local-vs-remote below once the
|
||||
# admin says yes, rather than silently refusing to ask at all.
|
||||
local FRIGATE_USE_AUTHELIA="n" FRIGATE_PROXY_SECRET="" AUTH_CONFIG_BLOCK=""
|
||||
if [ -d "$DOCKER_DIR/authelia" ]; then
|
||||
local _frigate_local_authelia="n"
|
||||
[ -d "$DOCKER_DIR/authelia" ] && _frigate_local_authelia="y"
|
||||
echo ""
|
||||
prompt_yn "Protect Frigate with Authelia SSO (disables Frigate's own login)? (y/n):" "y" FRIGATE_USE_AUTHELIA
|
||||
fi
|
||||
prompt_yn "Protect Frigate with Authelia SSO (disables Frigate's own login)? (y/n):" "$_frigate_local_authelia" FRIGATE_USE_AUTHELIA
|
||||
|
||||
if [[ "$FRIGATE_USE_AUTHELIA" =~ ^[Yy]$ ]]; then
|
||||
FRIGATE_PROXY_SECRET="${ENV_MAP[FRIGATE_PROXY_AUTH_SECRET]:-$(generate_password 32)}"
|
||||
|
||||
# Local Authelia snippet (import authelia) only actually exists
|
||||
# in the Caddyfile it's imported into if Authelia is on THAT
|
||||
# same machine. When it's on this box, that's this box's own
|
||||
# Caddy — safe to assume. When Caddy itself turns out to be
|
||||
# remote (below), "local" instead means "local to wherever
|
||||
# Caddy is", which this script can't see — so ask, rather than
|
||||
# silently emit an import that would fail Caddy's own reload
|
||||
# with "file to import not found" on that other machine.
|
||||
local _frigate_auth_block=" import authelia"
|
||||
if [ "$_frigate_local_authelia" != "y" ]; then
|
||||
echo ""
|
||||
log_info "No local Authelia on this box — Caddy for Frigate may end up on a"
|
||||
log_info "different machine (decided next)."
|
||||
local _authelia_with_caddy=""
|
||||
prompt_yn " Does Authelia run on that SAME machine as Caddy? (y/n):" "y" _authelia_with_caddy
|
||||
if [[ ! "$_authelia_with_caddy" =~ ^[Yy]$ ]]; then
|
||||
# Genuinely cross-machine: Authelia is a third box,
|
||||
# different from both this one and wherever Caddy ends
|
||||
# up. Needs the explicit header-pinned forward_auth form
|
||||
# — see CLAUDE.md's "forward_auth to a remote Authelia"
|
||||
# note for why the bare "authelia:9091" shortcut can't
|
||||
# be used here and X-Forwarded-Host must be pinned
|
||||
# explicitly (a second Caddy hop otherwise silently
|
||||
# evaluates every domain as if it were auth's own
|
||||
# portal domain — confirmed live, a real incident this
|
||||
# exact snippet shape was written to prevent).
|
||||
local _remote_authelia_domain=""
|
||||
prompt_text " Authelia's own portal domain (e.g. authelia.example.com):" "" _remote_authelia_domain
|
||||
if [ -n "$_remote_authelia_domain" ]; then
|
||||
_frigate_auth_block=" forward_auth https://${_remote_authelia_domain} {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||
header_up X-Forwarded-Method {method}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Uri {uri}
|
||||
}"
|
||||
else
|
||||
log_warning "No domain entered — falling back to 'import authelia', which will fail"
|
||||
log_warning "Caddy's reload unless Authelia is actually local to that Caddy machine."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
configure_caddy_for_service "Frigate" "frigate:5000" "frigate" \
|
||||
" import authelia" \
|
||||
"$_frigate_auth_block" \
|
||||
" header_up X-Proxy-Secret ${FRIGATE_PROXY_SECRET}"
|
||||
if [ "${CADDY_SERVICE_CONFIGURED:-false}" = true ]; then
|
||||
AUTH_CONFIG_BLOCK="auth:
|
||||
@@ -780,6 +841,7 @@ snapshots:
|
||||
FRIGATE_CONFIG
|
||||
fi
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << FRIGATE_ENV
|
||||
FRIGATE_MEDIA=$FRIGATE_MEDIA
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -92,6 +92,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -199,6 +204,7 @@ install_garage-webui() {
|
||||
ensure_docker_dir_ownership "$DIR"
|
||||
cd "$DIR" || return 1
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << COMPOSE
|
||||
name: garage-webui
|
||||
|
||||
@@ -219,6 +225,7 @@ services:
|
||||
- "${WEB_PORT}:3909"
|
||||
COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
|
||||
|
||||
@@ -108,6 +108,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -277,6 +282,7 @@ api_bind_addr = "[::]:${ADMIN_PORT}"
|
||||
admin_token = "${ADMIN_TOKEN}"
|
||||
TOML
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << COMPOSE
|
||||
name: garage
|
||||
|
||||
@@ -297,6 +303,7 @@ services:
|
||||
- "${ADMIN_PORT}:${ADMIN_PORT}"
|
||||
COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
|
||||
|
||||
@@ -187,6 +187,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -405,6 +410,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << GATUS_COMPOSE
|
||||
name: gatus
|
||||
|
||||
@@ -423,6 +429,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
GATUS_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << GATUS_ENV
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -104,6 +104,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -585,6 +590,7 @@ install_gitea() {
|
||||
[[ "$WEB_PORT" != 3001 ]] && log_info "Port 3001 was taken — Gitea's web UI will use ${WEB_PORT}."
|
||||
[[ "$SSH_PORT" != 2222 ]] && log_info "Port 2222 was taken — Gitea's SSH clone port will use ${SSH_PORT}."
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EOF
|
||||
name: gitea
|
||||
services:
|
||||
@@ -699,6 +705,7 @@ EOF
|
||||
cp -f "$SYNC_SRC" "$DIR/gitea-github-sync.sh"
|
||||
chmod +x "$DIR/gitea-github-sync.sh"
|
||||
|
||||
backup_if_exists "$DIR/.env"
|
||||
cat > "$DIR/.env" << ENV
|
||||
# Written by services/gitea.sh — re-run that (update mode) to change any of this.
|
||||
GITEA_URL='http://localhost:${WEB_PORT}'
|
||||
|
||||
@@ -274,6 +274,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << HOMEASSISTANT_COMPOSE
|
||||
name: homeassistant
|
||||
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -311,6 +316,7 @@ networks:
|
||||
local HB_PEPPER
|
||||
HB_PEPPER="$(generate_password 48)"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << HB_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -331,6 +337,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
HB_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << HB_ENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
|
||||
@@ -183,6 +183,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -606,6 +611,7 @@ networks:
|
||||
[ -n "$EXTERNAL_LIBRARY" ] && _EXTERNAL_VOLUME_LINE=" - \${EXTERNAL_LIBRARY}:/usr/src/app/external:ro
|
||||
"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << IMMICH_COMPOSE
|
||||
name: $PROJECT
|
||||
|
||||
@@ -686,6 +692,7 @@ S3_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY
|
||||
fi
|
||||
|
||||
if [ "$IMMICH_STRATEGY" = "2" ]; then
|
||||
backup_if_exists .env
|
||||
cat > .env << IMMICH_ENV
|
||||
# IMMICH CONFIGURATION — External Library Mode
|
||||
#
|
||||
@@ -713,6 +720,7 @@ TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
IMMICH_ENV
|
||||
else
|
||||
backup_if_exists .env
|
||||
cat > .env << IMMICH_ENV
|
||||
# IMMICH CONFIGURATION — Unified Library
|
||||
#
|
||||
|
||||
@@ -185,6 +185,11 @@ CBLOCK
|
||||
[[ "${DRY_RUN:-false}" == "true" ]] && return 0
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -314,6 +319,7 @@ networks:
|
||||
fi
|
||||
|
||||
if [[ "$USE_GPU" =~ ^[Yy]$ ]]; then
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << IOPAINT_GPU
|
||||
name: iopaint
|
||||
|
||||
@@ -346,6 +352,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
IOPAINT_GPU
|
||||
else
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << IOPAINT_CPU
|
||||
name: iopaint
|
||||
|
||||
@@ -373,6 +380,7 @@ IOPAINT_CPU
|
||||
fi
|
||||
|
||||
# ── .env ─────────────────────────────────────────────────────────────────
|
||||
backup_if_exists .env
|
||||
cat > .env << IOPAINT_ENV
|
||||
# IOPaint — change MODEL and restart to switch (no need to edit docker-compose.yml)
|
||||
|
||||
|
||||
@@ -182,6 +182,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -347,6 +352,7 @@ networks:
|
||||
'
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << JELLYFIN_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -368,6 +374,7 @@ $HWACCEL_BLOCK
|
||||
${_DISCOVERY_PORTS}${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
JELLYFIN_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << JELLYFIN_ENV
|
||||
MEDIA_PATH=$MEDIA_PATH
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -314,6 +319,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << JOPLIN_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -340,6 +346,7 @@ ${_CADDY_NET_BLOCK}
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
JOPLIN_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << JOPLIN_ENV
|
||||
# Joplin Server configuration
|
||||
APP_PORT=22300
|
||||
|
||||
@@ -174,6 +174,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -459,6 +464,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists "$JS99ER_DIR/docker-compose.yml"
|
||||
cat > "$JS99ER_DIR/docker-compose.yml" << COMPOSE
|
||||
name: js99er
|
||||
|
||||
|
||||
@@ -188,6 +188,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -410,6 +415,7 @@ install_koha() {
|
||||
fi
|
||||
|
||||
# ── docker-compose.yml ────────────────────────────────────────────────────
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << KOHA_COMPOSE
|
||||
name: koha
|
||||
|
||||
@@ -481,6 +487,7 @@ ${_CADDY_NET_SECTION}
|
||||
KOHA_COMPOSE
|
||||
|
||||
# ── config-main.env ───────────────────────────────────────────────────────
|
||||
backup_if_exists config-main.env
|
||||
cat > config-main.env << KOHA_ENV
|
||||
# Koha ILS configuration — generated at install time
|
||||
MYSQL_SERVER=koha-db
|
||||
|
||||
@@ -388,6 +388,7 @@ for a in data.get('assets', []):
|
||||
mkdir -p "$DIR"
|
||||
ensure_docker_dir_ownership "$DIR"
|
||||
|
||||
backup_if_exists "$DIR/docker-compose.yml"
|
||||
cat > "$DIR/docker-compose.yml" << EOF
|
||||
name: kyber-server
|
||||
services:
|
||||
@@ -411,6 +412,7 @@ EOF
|
||||
# Write .env with restricted permissions
|
||||
# Values are single-quoted so special characters ($, !, &, etc.) are safe.
|
||||
# Exception: single quotes inside a value would still break — avoid them.
|
||||
backup_if_exists "$DIR/.env"
|
||||
cat > "$DIR/.env" << EOF
|
||||
MAXIMA_CREDENTIALS='${EA_EMAIL}:${EA_PASSWORD}'
|
||||
KYBER_TOKEN='${KYBER_TOKEN}'
|
||||
@@ -558,6 +560,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
ACTUAL_USER="${SUDO_USER:-$USER}"
|
||||
ACTUAL_HOME=$(eval echo "~$ACTUAL_USER")
|
||||
|
||||
@@ -188,6 +188,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -353,6 +358,7 @@ install_lyrion() {
|
||||
_HTTP_PORT_INTERNAL="9000"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << LYRION_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -375,6 +381,7 @@ ${_NETWORK_BLOCK}${_PORTS_BLOCK} environment:
|
||||
|
||||
LYRION_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << LYRION_ENV
|
||||
MUSIC_PATH=$MUSIC_PATH
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -174,6 +174,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -254,6 +259,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << MM_COMPOSE
|
||||
name: mm-$MM_PORT
|
||||
|
||||
|
||||
@@ -170,6 +170,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -244,6 +249,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << MA_COMPOSE
|
||||
name: mail-archiver
|
||||
|
||||
@@ -279,6 +285,7 @@ ${_CADDY_NET_BLOCK}
|
||||
${_CADDY_NET_SECTION}
|
||||
MA_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << MA_ENV
|
||||
# ── General ───────────────────────────────────────────────────────────────────
|
||||
TZ=$TZ_VAL
|
||||
|
||||
@@ -213,6 +213,11 @@ CBLOCK
|
||||
[[ "${DRY_RUN:-false}" == "true" ]] && return 0
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
local _len="${1:-32}"
|
||||
@@ -518,6 +523,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EOF
|
||||
name: ${PROJECT}
|
||||
|
||||
@@ -580,6 +586,7 @@ ${_CADDY_NET_BLOCK} healthcheck:
|
||||
${_CADDY_NET_BLOCK}${_COTURN_SERVICE}${_CADDY_NET_SECTION}
|
||||
EOF
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << EOF
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -180,6 +180,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -459,6 +464,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << MEALIE_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -483,6 +489,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
MEALIE_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << MEALIE_ENV
|
||||
# Public URL Mealie is served on — used for email links and OAuth redirects.
|
||||
# Update if you change your domain or switch from HTTP to HTTPS.
|
||||
|
||||
@@ -188,6 +188,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -323,6 +328,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << MC_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -350,6 +356,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
MC_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << MC_ENV
|
||||
MC_HOSTNAME=$MC_HOSTNAME
|
||||
MC_REVERSE_PROXY=false
|
||||
|
||||
@@ -1519,6 +1519,7 @@ PREGENINFOEOF
|
||||
- ./config:/data/config"
|
||||
fi
|
||||
|
||||
backup_if_exists "$MC_DIR/docker-compose.yml"
|
||||
cat > "$MC_DIR/docker-compose.yml" << COMPOSEEOF
|
||||
name: ${MC_NAME}
|
||||
|
||||
@@ -1902,6 +1903,7 @@ EXPOSE 80
|
||||
CLIENTDOCKEREOF
|
||||
|
||||
# Standalone compose for the client-mods page (its own folder).
|
||||
backup_if_exists "$CLIENT_MODS_DIR/docker-compose.yml"
|
||||
cat > "$CLIENT_MODS_DIR/docker-compose.yml" << CMCOMPOSEEOF
|
||||
name: ${CM_NAME}
|
||||
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -242,6 +247,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << N8N_COMPOSE
|
||||
name: n8n
|
||||
|
||||
@@ -266,6 +272,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
N8N_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << N8N_ENV
|
||||
# n8n environment — edit before starting if needed
|
||||
N8N_HOST=n8n
|
||||
|
||||
@@ -70,6 +70,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
# Match common.sh's eval-based pattern so local vars in install_* are set correctly
|
||||
prompt_text() {
|
||||
@@ -260,6 +265,7 @@ networks:
|
||||
fi
|
||||
|
||||
# ── docker-compose.yml ──────────────────────────────────────────────────
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << NCCOMPOSE
|
||||
name: nextcloud
|
||||
services:
|
||||
@@ -290,6 +296,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
NCCOMPOSE
|
||||
|
||||
# ── .env ────────────────────────────────────────────────────────────────
|
||||
backup_if_exists .env
|
||||
cat > .env << NCENV
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -184,6 +184,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -309,6 +314,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << NTFY_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -329,6 +335,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
NTFY_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << NTFY_ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -70,6 +70,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
|
||||
# Match common.sh's eval-based pattern so local vars in install_* are set correctly
|
||||
prompt_text() {
|
||||
@@ -280,6 +285,7 @@ networks:
|
||||
fi
|
||||
|
||||
# ── docker-compose.yml ──────────────────────────────────────────────────
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << OOCOMPOSE
|
||||
name: onlyoffice
|
||||
services:
|
||||
@@ -299,6 +305,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
OOCOMPOSE
|
||||
|
||||
# ── .env ────────────────────────────────────────────────────────────────
|
||||
backup_if_exists .env
|
||||
cat > .env << OOENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
# JWT authentication — keep JWT_SECRET private
|
||||
|
||||
@@ -210,6 +210,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -325,6 +330,7 @@ networks:
|
||||
# example — NET_ADMIN specifically is only needed if this instance is
|
||||
# ever used as a DHCP server too, which it isn't here, but the other two
|
||||
# (SYS_TIME, SYS_NICE) are part of that same documented baseline.
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << PIHOLE_COMPOSE
|
||||
name: pihole
|
||||
|
||||
@@ -351,6 +357,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
PIHOLE_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << PIHOLE_ENV
|
||||
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
|
||||
# Admin web UI password (System Console / login screen).
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -242,6 +247,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << PORTAINER_COMPOSE
|
||||
name: portainer
|
||||
|
||||
|
||||
@@ -1746,6 +1746,7 @@ _pstn_apply_settings() {
|
||||
# Direct") — unquoted, bash's `source` would treat the second word of
|
||||
# any such value as a command to run ("Direct: command not found"),
|
||||
# confirmed live while testing the multi-IP change.
|
||||
backup_if_exists "$EA_DIR/.pstn-trunk.env"
|
||||
cat > "$EA_DIR/.pstn-trunk.env" << ENV
|
||||
PROVIDER_NAME="${PROVIDER_NAME}"
|
||||
TRUNK_SERVER="${SERVER}"
|
||||
|
||||
@@ -91,6 +91,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -230,6 +235,7 @@ install_rustdesk() {
|
||||
prompt_yn "Require encrypted connections only? (recommended) (y/n):" "y" _enc
|
||||
[ "$_enc" = "n" ] || [ "$_enc" = "N" ] && ENCRYPTED_ONLY="0"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << RD_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -251,6 +257,7 @@ services:
|
||||
- ./rustdesk_data:/data
|
||||
RD_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << RD_ENV
|
||||
# ── General ───────────────────────────────────────────────────────────────────
|
||||
TZ=$TZ_VAL
|
||||
|
||||
@@ -60,6 +60,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -176,6 +181,7 @@ EOF
|
||||
prompt_text " WEB_EXT_API_KEY (e.g. user:12345678:901), blank to skip:" "" FF_KEY
|
||||
prompt_text " WEB_EXT_API_SECRET, blank to skip:" "" FF_SECRET
|
||||
if [ -n "$FF_KEY" ] && [ -n "$FF_SECRET" ]; then
|
||||
backup_if_exists "$SS_DIR/.env"
|
||||
cat > "$SS_DIR/.env" << ENVEOF
|
||||
WEB_EXT_API_KEY="$FF_KEY"
|
||||
WEB_EXT_API_SECRET="$FF_SECRET"
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -242,6 +247,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << PDF_COMPOSE
|
||||
name: stirling-pdf
|
||||
|
||||
@@ -261,6 +267,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
PDF_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << PDF_ENV
|
||||
# Stirling PDF configuration
|
||||
|
||||
|
||||
@@ -147,6 +147,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -225,6 +230,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EOF
|
||||
name: syncthing
|
||||
|
||||
@@ -249,6 +255,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
EOF
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << ENV
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
PUID=$PUID
|
||||
|
||||
@@ -180,6 +180,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -474,6 +479,7 @@ networks:
|
||||
- \"${PROTO_MIN}-${PROTO_MAX}:${PROTO_MIN}-${PROTO_MAX}/udp\""
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << TRACCAR_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -560,6 +566,7 @@ SMS_HTTP_PASSWORD=$SMS_HTTP_PASSWORD
|
||||
fi
|
||||
fi
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << TRACCAR_ENV
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
@@ -89,6 +89,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -238,6 +243,7 @@ networks:
|
||||
fi
|
||||
|
||||
# Unquoted heredoc; ${...} used for caddy_net vars; all Docker Compose vars escaped with \$
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << UNIFI_COMPOSE
|
||||
name: $PROJECT
|
||||
|
||||
@@ -286,6 +292,7 @@ configs:
|
||||
db.getSiblingDB("\${MONGO_DBNAME}_stat").createUser({user: "\${MONGO_USER}", pwd: "\${MONGO_PASS}", roles: [{role: "\${MONGO_ROLE}", db: "\${MONGO_DBNAME}_stat"}]});
|
||||
UNIFI_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << UNIFI_ENV
|
||||
# ── General ───────────────────────────────────────────────────────────────────
|
||||
TZ=$TZ_VAL
|
||||
|
||||
@@ -177,6 +177,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -277,6 +282,7 @@ ${UPTIME_ENV_BLOCK}
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << UPTIME_COMPOSE
|
||||
name: uptime-kuma
|
||||
|
||||
|
||||
@@ -194,6 +194,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -420,6 +425,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << VW_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -437,6 +443,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
VW_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << VW_ENV
|
||||
# ── General ───────────────────────────────────────────────────────────────────
|
||||
TZ=$TZ_VAL
|
||||
|
||||
@@ -64,6 +64,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -151,6 +156,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << WT_COMPOSE
|
||||
name: watchtower
|
||||
|
||||
@@ -179,6 +185,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
WT_COMPOSE
|
||||
|
||||
# Create .env
|
||||
backup_if_exists .env
|
||||
cat > .env << WT_ENV
|
||||
# Watchtower Configuration
|
||||
# =========================
|
||||
|
||||
@@ -68,6 +68,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -126,6 +131,7 @@ install_watchyourlan() {
|
||||
prompt_text "GUI port [8840]:" "8840" GUI_PORT
|
||||
[ -z "$GUI_PORT" ] && GUI_PORT="8840"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << 'WYL_COMPOSE'
|
||||
name: watchyourlan
|
||||
|
||||
@@ -141,6 +147,7 @@ services:
|
||||
- ./watchyourlan_data:/data
|
||||
WYL_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << WYL_ENV
|
||||
# ── General ───────────────────────────────────────────────────────────────────
|
||||
TZ=$TZ_VAL
|
||||
|
||||
@@ -199,6 +199,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -302,6 +307,7 @@ networks:
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << WGEASY_COMPOSE
|
||||
name: wg-easy
|
||||
|
||||
@@ -332,6 +338,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
WGEASY_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << WGEASY_ENV
|
||||
WG_HOST=$WG_HOST
|
||||
# Plain-text password — used only if PASSWORD_HASH could not be generated above
|
||||
|
||||
@@ -169,6 +169,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -404,6 +409,7 @@ DOCKERFILE
|
||||
# network_mode: host — server.py reaches Wolf at localhost:47989 directly.
|
||||
# Docker socket (ro) — server.py calls `docker logs wolf` to read secrets.
|
||||
log_info "Writing docker-compose.yml..."
|
||||
backup_if_exists "$WOLFPAIR_DIR/docker-compose.yml"
|
||||
cat > "$WOLFPAIR_DIR/docker-compose.yml" << 'COMPOSE'
|
||||
name: wolf-pair
|
||||
|
||||
|
||||
@@ -179,6 +179,11 @@ CBLOCK
|
||||
mkdir -p "$_dir"
|
||||
cat > "$_dir/README.md"
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
|
||||
@@ -875,6 +880,7 @@ UDEV
|
||||
# if the drive is remounted at a different path. IP/MAC/render-node are
|
||||
# baked in at install time because they're hardware-specific and not stored
|
||||
# in .env — use 'manage.sh update-network' to regenerate if they change.
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << EOF
|
||||
name: wolf
|
||||
|
||||
@@ -922,6 +928,7 @@ EOF
|
||||
log_success "docker-compose.yml created"
|
||||
|
||||
# Save the game storage path so it's visible and editable later
|
||||
backup_if_exists .env
|
||||
cat > .env << EOF
|
||||
# Wolf game/ROM storage root — edit this and run ./manage.sh update-storage to apply
|
||||
GAME_STORAGE_DIR=${GAME_STORAGE_DIR}
|
||||
|
||||
@@ -207,6 +207,11 @@ CBLOCK
|
||||
cat > "$_dir/README.md"
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$_dir/README.md" 2>/dev/null || true
|
||||
}
|
||||
backup_if_exists() {
|
||||
local _file="$1"
|
||||
[ -f "$_file" ] || return 0
|
||||
cp -p "$_file" "${_file}.bak.$(date +%Y%m%d-%H%M%S)" 2>/dev/null
|
||||
}
|
||||
fi
|
||||
|
||||
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
|
||||
@@ -361,6 +366,7 @@ PHPINI
|
||||
"
|
||||
fi
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << WPCOMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -398,6 +404,7 @@ networks:
|
||||
${_CADDY_NET_SECTION}
|
||||
WPCOMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << WPENV
|
||||
TZ=$TZ_VAL
|
||||
CADDY_NET=$SITE_CADDY_NET
|
||||
|
||||
Reference in New Issue
Block a user