Merge pull request #396 from outis1one/claude/frigate-authelia-openid-0l1htj

Claude/frigate authelia openid 0l1htj
This commit is contained in:
Outis
2026-08-27 08:17:01 -04:00
committed by GitHub
62 changed files with 610 additions and 5 deletions
+1
View File
@@ -493,6 +493,7 @@ right (Portainer, ntfy), not general familiarity with the product:
| Service | Native OIDC? | Notes | | Service | Native OIDC? | Notes |
|---|---|---| |---|---|---|
| `mealie` | Yes — wired up | Pure env vars (`OIDC_AUTH_ENABLED`, `OIDC_CLIENT_ID/SECRET`, `OIDC_CONFIGURATION_URL`), see `_mealie_offer_authelia_oidc()`. Redirect URI is `<BASE_URL>/login`. Needs a `--forwarded-allow-ips` entrypoint override when Caddy-fronted, or the generated redirect URI comes out `http://` even when actually served over `https://` — see the function's own comment. | | `mealie` | Yes — wired up | Pure env vars (`OIDC_AUTH_ENABLED`, `OIDC_CLIENT_ID/SECRET`, `OIDC_CONFIGURATION_URL`), see `_mealie_offer_authelia_oidc()`. Redirect URI is `<BASE_URL>/login`. Needs a `--forwarded-allow-ips` entrypoint override when Caddy-fronted, or the generated redirect URI comes out `http://` even when actually served over `https://` — see the function's own comment. |
| `homebox` | Yes — wired up | Pure env vars (`HBOX_OIDC_ENABLED`, `HBOX_OIDC_ISSUER_URL`, `HBOX_OIDC_CLIENT_ID/SECRET`, `HBOX_OIDC_SCOPE`), see `_homebox_offer_authelia_oidc()`. Confirmed against homebox.software's own OIDC docs and authelia.com's Homebox integration page — needs PKCE (unlike Mealie/ActualBudget). Redirect path is `/api/v1/users/login/oidc/callback`; issuer URL is reportedly sensitive to a trailing slash (a real upstream bug), so it's written from this repo's own portal-URL value as-is, never with one appended. The stock compose template didn't have `env_file: .env` (vars were listed individually in `environment:` instead) — added to the template, and patched onto any pre-existing install's compose file the first time this offer runs, or the written `.env` additions would silently never reach the container. `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false`/`HBOX_OIDC_AUTO_REDIRECT=true` are real, documented env vars for fully replacing local login, offered as a separate step gated behind the same "have you tested the button first" confirmation as Mealie/Beszel. |
| `actualbudget` | Yes — wired up | Pure env vars (`ACTUAL_OPENID_DISCOVERY_URL`, `ACTUAL_OPENID_CLIENT_ID/SECRET`, `ACTUAL_OPENID_SERVER_HOSTNAME`), see `_actualbudget_offer_authelia_oidc()`. Redirect path `/openid/callback` (matches the existing preset in `_authelia_add_oidc_client()`'s menu). First OIDC login becomes the server owner if none is set yet — Actual's own behavior. | | `actualbudget` | Yes — wired up | Pure env vars (`ACTUAL_OPENID_DISCOVERY_URL`, `ACTUAL_OPENID_CLIENT_ID/SECRET`, `ACTUAL_OPENID_SERVER_HOSTNAME`), see `_actualbudget_offer_authelia_oidc()`. Redirect path `/openid/callback` (matches the existing preset in `_authelia_add_oidc_client()`'s menu). First OIDC login becomes the server owner if none is set yet — Actual's own behavior. |
| `immich` | Yes — wired up | Real OAuth2/OIDC settings under Administration → Settings, backed by `GET`/`PUT /api/system-config` — confirmed the exact JSON field names against Immich's own `config-file.md` and source directly (the `oauth` sub-object: `enabled`/`issuerUrl`/`clientId`/`clientSecret`/`scope`/`buttonText`, etc.), not guessed. See `_immich_offer_authelia_oidc()`. GET/PUT exchange the *whole* config object (no partial-patch endpoint), so it round-trips everything else — storage template, library settings — completely unchanged; the same shape already proven by `import-photos.sh`'s own storage-template step in this file. Needs an admin API key, which doesn't exist until the user creates their account on first web visit — this offer runs from both the fresh-install path (usually a no-op that first time) and the "update" rerun path, which is the realistic way most people finish this. | | `immich` | Yes — wired up | Real OAuth2/OIDC settings under Administration → Settings, backed by `GET`/`PUT /api/system-config` — confirmed the exact JSON field names against Immich's own `config-file.md` and source directly (the `oauth` sub-object: `enabled`/`issuerUrl`/`clientId`/`clientSecret`/`scope`/`buttonText`, etc.), not guessed. See `_immich_offer_authelia_oidc()`. GET/PUT exchange the *whole* config object (no partial-patch endpoint), so it round-trips everything else — storage template, library settings — completely unchanged; the same shape already proven by `import-photos.sh`'s own storage-template step in this file. Needs an admin API key, which doesn't exist until the user creates their account on first web visit — this offer runs from both the fresh-install path (usually a no-op that first time) and the "update" rerun path, which is the realistic way most people finish this. |
| `audiobookshelf` | Yes — wired up (Authelia side only) | Checked against audiobookshelf.org's own OIDC docs: config is UI-only (Settings → Authentication), no env var or config API — so `_audiobookshelf_offer_authelia_oidc()` registers the Authelia client (needs PKCE, confirmed via authelia.com's own integration page for it) and prints the exact individual-endpoint values to paste in, since Audiobookshelf wants those rather than a discovery URL. Three redirect URIs: web callback, mobile-redirect, and the `audiobookshelf://oauth` app-scheme callback. | | `audiobookshelf` | Yes — wired up (Authelia side only) | Checked against audiobookshelf.org's own OIDC docs: config is UI-only (Settings → Authentication), no env var or config API — so `_audiobookshelf_offer_authelia_oidc()` registers the Authelia client (needs PKCE, confirmed via authelia.com's own integration page for it) and prints the exact individual-endpoint values to paste in, since Audiobookshelf wants those rather than a discovery URL. Three redirect URIs: web callback, mobile-redirect, and the `audiobookshelf://oauth` app-scheme callback. |
+26
View File
@@ -802,6 +802,32 @@ write_readme() {
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/README.md" 2>/dev/null || true 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 # ── Host port collision avoidance (shared by every service that publishes a
# fixed host port) ──────────────────────────────────────────────────────────── # fixed host port) ────────────────────────────────────────────────────────────
# With 70+ services in this repo, several ship the same default port (e.g. # With 70+ services in this repo, several ship the same default port (e.g.
+7
View File
@@ -180,6 +180,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -388,6 +393,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << AB_COMPOSE cat > docker-compose.yml << AB_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -405,6 +411,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
AB_COMPOSE AB_COMPOSE
backup_if_exists .env
cat > .env << AB_ENV cat > .env << AB_ENV
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+8
View File
@@ -170,6 +170,11 @@ CBLOCK
[[ "${DRY_RUN:-false}" == "true" ]] && return 0 [[ "${DRY_RUN:-false}" == "true" ]] && return 0
cat > "$_dir/README.md" 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() { generate_password() {
local _len="${1:-32}" local _len="${1:-32}"
@@ -368,6 +373,7 @@ install_ai-gpu() {
sed -i "s|America/New_York|$TZ_VAL|g" {} \; sed -i "s|America/New_York|$TZ_VAL|g" {} \;
fi fi
backup_if_exists "$IMAGE_GEN_DIR/.env"
cat > "$IMAGE_GEN_DIR/.env" << IMGENV cat > "$IMAGE_GEN_DIR/.env" << IMGENV
# InvokeAI — image generation # InvokeAI — image generation
TZ=${TZ_VAL} TZ=${TZ_VAL}
@@ -407,6 +413,7 @@ IMGENV
local WEBUI_SECRET local WEBUI_SECRET
WEBUI_SECRET="$(generate_password 32)" WEBUI_SECRET="$(generate_password 32)"
backup_if_exists "$LLM_DIR/.env"
cat > "$LLM_DIR/.env" << LLMENV cat > "$LLM_DIR/.env" << LLMENV
# Ollama + Open WebUI + SearXNG # Ollama + Open WebUI + SearXNG
TZ=${TZ_VAL} TZ=${TZ_VAL}
@@ -441,6 +448,7 @@ LLMENV
fi fi
fi fi
backup_if_exists "$PORTAL_DIR/.env"
cat > "$PORTAL_DIR/.env" << PORTALENV cat > "$PORTAL_DIR/.env" << PORTALENV
# AI Portal — GPU stack swap controller # AI Portal — GPU stack swap controller
TZ=${TZ_VAL} TZ=${TZ_VAL}
+7
View File
@@ -174,6 +174,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -240,6 +245,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << ABCOMPOSE cat > docker-compose.yml << ABCOMPOSE
name: archivebox name: archivebox
@@ -262,6 +268,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
ABCOMPOSE ABCOMPOSE
backup_if_exists .env
cat > .env << ABENV cat > .env << ABENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
ABENV ABENV
+7
View File
@@ -188,6 +188,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -278,6 +283,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << ARM_COMPOSE cat > docker-compose.yml << ARM_COMPOSE
name: arm name: arm
@@ -306,6 +312,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
ARM_COMPOSE ARM_COMPOSE
backup_if_exists .env
cat > .env << ARM_ENV cat > .env << ARM_ENV
ARM_OUTPUT=$ARM_OUTPUT ARM_OUTPUT=$ARM_OUTPUT
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -255,6 +255,11 @@ CBLOCK
[[ "${DRY_RUN:-false}" == "true" ]] && return 0 [[ "${DRY_RUN:-false}" == "true" ]] && return 0
cat > "$_dir/README.md" 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() { generate_password() {
local _len="${1:-32}" local _len="${1:-32}"
@@ -1588,6 +1593,7 @@ _asterisk_write_compose() {
" "
[[ "$USE_EMBEDDED_COTURN" != true ]] && _COTURN_DEPENDS="" && _COTURN_SERVICE="" [[ "$USE_EMBEDDED_COTURN" != true ]] && _COTURN_DEPENDS="" && _COTURN_SERVICE=""
backup_if_exists docker-compose.yml
cat > docker-compose.yml << EOF cat > docker-compose.yml << EOF
name: PROJECT_NAME_PLACEHOLDER name: PROJECT_NAME_PLACEHOLDER
@@ -2727,6 +2733,7 @@ install_asterisk() {
# WireGuard/Tailscale) on a subnet this box isn't directly attached to." # WireGuard/Tailscale) on a subnet this box isn't directly attached to."
fi fi
backup_if_exists .env
cat > .env << ENV cat > .env << ENV
# ── Domain ──────────────────────────────────────────────────── # ── Domain ────────────────────────────────────────────────────
# ${_domain_comment} # ${_domain_comment}
+7
View File
@@ -180,6 +180,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -379,6 +384,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << ABS_COMPOSE cat > docker-compose.yml << ABS_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -400,6 +406,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
ABS_COMPOSE ABS_COMPOSE
backup_if_exists .env
cat > .env << ABS_ENV cat > .env << ABS_ENV
AUDIOBOOKS_PATH=$AUDIOBOOKS_PATH AUDIOBOOKS_PATH=$AUDIOBOOKS_PATH
PODCASTS_PATH=./podcasts PODCASTS_PATH=./podcasts
+7
View File
@@ -186,6 +186,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -408,6 +413,7 @@ install_authelia() {
cd "$AUTHELIA_DIR" || return 1 cd "$AUTHELIA_DIR" || return 1
# ── .env ───────────────────────────────────────────────────────────────── # ── .env ─────────────────────────────────────────────────────────────────
backup_if_exists .env
cat > .env << AUTHELIA_ENV cat > .env << AUTHELIA_ENV
MY_DOMAIN=${AUTHELIA_DOMAIN} MY_DOMAIN=${AUTHELIA_DOMAIN}
SMTP_USER=${AUTHELIA_SMTP_USER} SMTP_USER=${AUTHELIA_SMTP_USER}
@@ -416,6 +422,7 @@ TZ=${AUTHELIA_TZ}
AUTHELIA_ENV AUTHELIA_ENV
# ── docker-compose.yml (quoted heredoc: ${SMTP_USER} resolved by compose/.env) ── # ── docker-compose.yml (quoted heredoc: ${SMTP_USER} resolved by compose/.env) ──
backup_if_exists docker-compose.yml
cat > docker-compose.yml << 'AUTHELIA_COMPOSE' cat > docker-compose.yml << 'AUTHELIA_COMPOSE'
name: authelia name: authelia
+9
View File
@@ -120,6 +120,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
local _companion; _companion="$(dirname "${BASH_SOURCE[0]}")/beszel.md" local _companion; _companion="$(dirname "${BASH_SOURCE[0]}")/beszel.md"
[ -f "$_companion" ] && cat "$_companion" >> "$_dir/README.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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -458,6 +463,7 @@ networks:
# profile recognizes. security_opt: apparmor:unconfined below is # profile recognizes. security_opt: apparmor:unconfined below is
# Beszel's own documented fix (beszel.dev/guide/systemd#apparmor-error) # Beszel's own documented fix (beszel.dev/guide/systemd#apparmor-error)
# — confirmed live, this exact error on a real box. # — confirmed live, this exact error on a real box.
backup_if_exists docker-compose.yml
cat > docker-compose.yml << BESZEL_COMPOSE cat > docker-compose.yml << BESZEL_COMPOSE
name: beszel name: beszel
@@ -504,6 +510,7 @@ BESZEL_COMPOSE
# README). Not threaded through automatically here because Caddy setup # README). Not threaded through automatically here because Caddy setup
# (below) happens after this file is written, same ordering every # (below) happens after this file is written, same ordering every
# other service in this repo uses for its own Caddy prompt. # other service in this repo uses for its own Caddy prompt.
backup_if_exists .env
cat > .env << BESZEL_ENV cat > .env << BESZEL_ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
CADDY_NET=$SITE_CADDY_NET 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 # 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 # 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). # systemd/dbus/sensor mounts below matter (Services/Temp columns).
backup_if_exists docker-compose.yml
cat > docker-compose.yml << AGENT_COMPOSE cat > docker-compose.yml << AGENT_COMPOSE
name: beszel-agent name: beszel-agent
@@ -703,6 +711,7 @@ services:
- /sys/class/thermal:/sys/class/thermal:ro - /sys/class/thermal:/sys/class/thermal:ro
AGENT_COMPOSE AGENT_COMPOSE
backup_if_exists .env
cat > .env << AGENT_ENV cat > .env << AGENT_ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
HUB_URL=$HUB_URL HUB_URL=$HUB_URL
+6
View File
@@ -192,6 +192,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -279,6 +284,7 @@ install_caddy() {
cd "$CADDY_DIR" || return 1 cd "$CADDY_DIR" || return 1
backup_if_exists docker-compose.yml
cat > docker-compose.yml << 'CADDY_COMPOSE' cat > docker-compose.yml << 'CADDY_COMPOSE'
name: caddy name: caddy
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -242,6 +247,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << CW_COMPOSE cat > docker-compose.yml << CW_COMPOSE
name: calibre-web name: calibre-web
@@ -264,6 +270,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
CW_COMPOSE CW_COMPOSE
backup_if_exists .env
cat > .env << CW_ENV cat > .env << CW_ENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
CW_ENV CW_ENV
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -243,6 +248,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << CD_COMPOSE cat > docker-compose.yml << CD_COMPOSE
name: changedetection name: changedetection
@@ -272,6 +278,7 @@ ${_CADDY_NET_BLOCK} depends_on:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
CD_COMPOSE CD_COMPOSE
backup_if_exists .env
cat > .env << CD_ENV cat > .env << CD_ENV
# Changedetection.io environment — edit before starting if needed # Changedetection.io environment — edit before starting if needed
BASE_URL=https://changes.${SITE_DOMAIN} BASE_URL=https://changes.${SITE_DOMAIN}
+7
View File
@@ -174,6 +174,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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)}" 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' cat > docker-compose.yml << 'DDCLIENT_COMPOSE'
name: ddclient name: ddclient
@@ -229,6 +235,7 @@ services:
- ./config:/config - ./config:/config
DDCLIENT_COMPOSE DDCLIENT_COMPOSE
backup_if_exists .env
cat > .env << DDCLIENT_ENV cat > .env << DDCLIENT_ENV
TZ=$TZ_VAL TZ=$TZ_VAL
DDCLIENT_ENV DDCLIENT_ENV
+7
View File
@@ -178,6 +178,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -477,6 +482,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << DRUM_COMPOSE cat > docker-compose.yml << DRUM_COMPOSE
name: drum-rhythm-game name: drum-rhythm-game
@@ -492,6 +498,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
DRUM_COMPOSE DRUM_COMPOSE
backup_if_exists .env
cat > .env << DRUM_ENV cat > .env << DRUM_ENV
CADDY_NET=${SITE_CADDY_NET} CADDY_NET=${SITE_CADDY_NET}
DRUM_ENV DRUM_ENV
+7
View File
@@ -189,6 +189,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -352,6 +357,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << EMBY_COMPOSE cat > docker-compose.yml << EMBY_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -377,6 +383,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
EMBY_COMPOSE EMBY_COMPOSE
backup_if_exists .env
cat > .env << EMBY_ENV cat > .env << EMBY_ENV
MEDIA_PATH=$MEDIA_PATH MEDIA_PATH=$MEDIA_PATH
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -315,6 +320,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << FB_COMPOSE cat > docker-compose.yml << FB_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -334,6 +340,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
FB_COMPOSE FB_COMPOSE
backup_if_exists .env
cat > .env << FB_ENV cat > .env << FB_ENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
FB_ENV FB_ENV
+7
View File
@@ -199,6 +199,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -339,6 +344,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << FMD_COMPOSE cat > docker-compose.yml << FMD_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -357,6 +363,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
FMD_COMPOSE FMD_COMPOSE
backup_if_exists .env
cat > .env << FMD_ENV cat > .env << FMD_ENV
FMD_REGISTRATIONTOKEN=$FMD_TOKEN FMD_REGISTRATIONTOKEN=$FMD_TOKEN
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -207,6 +207,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -365,6 +370,7 @@ install_frigate-audio() {
# ── .env ────────────────────────────────────────────────────────────────── # ── .env ──────────────────────────────────────────────────────────────────
log_info "Writing .env..." log_info "Writing .env..."
backup_if_exists "$DIR/.env"
cat > "$DIR/.env" << ENVEOF cat > "$DIR/.env" << ENVEOF
# Frigate audio stack — generated by setup.sh # Frigate audio stack — generated by setup.sh
# DO NOT commit this file — it contains credentials. # DO NOT commit this file — it contains credentials.
@@ -432,6 +438,7 @@ networks:
" "
fi fi
backup_if_exists "$DIR/docker-compose.yml"
cat > "$DIR/docker-compose.yml" << 'COMPOSEEOF' cat > "$DIR/docker-compose.yml" << 'COMPOSEEOF'
# Frigate NVR + Mosquitto MQTT + frigate-notify # Frigate NVR + Mosquitto MQTT + frigate-notify
# Generated by ubuntu-post-install setup.sh # Generated by ubuntu-post-install setup.sh
+6
View File
@@ -174,6 +174,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -234,6 +239,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << FN_COMPOSE cat > docker-compose.yml << FN_COMPOSE
name: frigate-notify name: frigate-notify
+67 -5
View File
@@ -202,6 +202,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -626,6 +631,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << FRIGATE_COMPOSE cat > docker-compose.yml << FRIGATE_COMPOSE
name: frigate name: frigate
@@ -673,16 +679,71 @@ FRIGATE_COMPOSE
# reverse_proxy block) stops that trust from being spoofed by a # reverse_proxy block) stops that trust from being spoofed by a
# request that reaches Frigate's published host port directly, # request that reaches Frigate's published host port directly,
# bypassing Caddy/Authelia entirely. # 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="" local FRIGATE_USE_AUTHELIA="n" FRIGATE_PROXY_SECRET="" AUTH_CONFIG_BLOCK=""
if [ -d "$DOCKER_DIR/authelia" ]; then local _frigate_local_authelia="n"
echo "" [ -d "$DOCKER_DIR/authelia" ] && _frigate_local_authelia="y"
prompt_yn "Protect Frigate with Authelia SSO (disables Frigate's own login)? (y/n):" "y" FRIGATE_USE_AUTHELIA echo ""
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 if [[ "$FRIGATE_USE_AUTHELIA" =~ ^[Yy]$ ]]; then
FRIGATE_PROXY_SECRET="${ENV_MAP[FRIGATE_PROXY_AUTH_SECRET]:-$(generate_password 32)}" 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" \ configure_caddy_for_service "Frigate" "frigate:5000" "frigate" \
" import authelia" \ "$_frigate_auth_block" \
" header_up X-Proxy-Secret ${FRIGATE_PROXY_SECRET}" " header_up X-Proxy-Secret ${FRIGATE_PROXY_SECRET}"
if [ "${CADDY_SERVICE_CONFIGURED:-false}" = true ]; then if [ "${CADDY_SERVICE_CONFIGURED:-false}" = true ]; then
AUTH_CONFIG_BLOCK="auth: AUTH_CONFIG_BLOCK="auth:
@@ -780,6 +841,7 @@ snapshots:
FRIGATE_CONFIG FRIGATE_CONFIG
fi fi
backup_if_exists .env
cat > .env << FRIGATE_ENV cat > .env << FRIGATE_ENV
FRIGATE_MEDIA=$FRIGATE_MEDIA FRIGATE_MEDIA=$FRIGATE_MEDIA
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -92,6 +92,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -199,6 +204,7 @@ install_garage-webui() {
ensure_docker_dir_ownership "$DIR" ensure_docker_dir_ownership "$DIR"
cd "$DIR" || return 1 cd "$DIR" || return 1
backup_if_exists docker-compose.yml
cat > docker-compose.yml << COMPOSE cat > docker-compose.yml << COMPOSE
name: garage-webui name: garage-webui
@@ -219,6 +225,7 @@ services:
- "${WEB_PORT}:3909" - "${WEB_PORT}:3909"
COMPOSE COMPOSE
backup_if_exists .env
cat > .env << ENV cat > .env << ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
+7
View File
@@ -108,6 +108,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -277,6 +282,7 @@ api_bind_addr = "[::]:${ADMIN_PORT}"
admin_token = "${ADMIN_TOKEN}" admin_token = "${ADMIN_TOKEN}"
TOML TOML
backup_if_exists docker-compose.yml
cat > docker-compose.yml << COMPOSE cat > docker-compose.yml << COMPOSE
name: garage name: garage
@@ -297,6 +303,7 @@ services:
- "${ADMIN_PORT}:${ADMIN_PORT}" - "${ADMIN_PORT}:${ADMIN_PORT}"
COMPOSE COMPOSE
backup_if_exists .env
cat > .env << ENV cat > .env << ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
+7
View File
@@ -187,6 +187,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -405,6 +410,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << GATUS_COMPOSE cat > docker-compose.yml << GATUS_COMPOSE
name: gatus name: gatus
@@ -423,6 +429,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
GATUS_COMPOSE GATUS_COMPOSE
backup_if_exists .env
cat > .env << GATUS_ENV cat > .env << GATUS_ENV
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -104,6 +104,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" 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}." [[ "$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}." [[ "$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 cat > docker-compose.yml << EOF
name: gitea name: gitea
services: services:
@@ -699,6 +705,7 @@ EOF
cp -f "$SYNC_SRC" "$DIR/gitea-github-sync.sh" cp -f "$SYNC_SRC" "$DIR/gitea-github-sync.sh"
chmod +x "$DIR/gitea-github-sync.sh" chmod +x "$DIR/gitea-github-sync.sh"
backup_if_exists "$DIR/.env"
cat > "$DIR/.env" << ENV cat > "$DIR/.env" << ENV
# Written by services/gitea.sh — re-run that (update mode) to change any of this. # Written by services/gitea.sh — re-run that (update mode) to change any of this.
GITEA_URL='http://localhost:${WEB_PORT}' GITEA_URL='http://localhost:${WEB_PORT}'
+1
View File
@@ -274,6 +274,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << HOMEASSISTANT_COMPOSE cat > docker-compose.yml << HOMEASSISTANT_COMPOSE
name: homeassistant name: homeassistant
+130
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -196,6 +201,124 @@ fi
register_service homebox utilities "Home inventory and asset management (Homebox)" 7745 register_service homebox utilities "Home inventory and asset management (Homebox)" 7745
# Offers to wire Homebox's own native OIDC support to Authelia — real
# server-side automation via env vars, not paste-in instructions, same
# shape as Mealie/ActualBudget. Confirmed against homebox.software's own
# OIDC docs and authelia.com's Homebox integration page directly: PKCE is
# required, redirect path is /api/v1/users/login/oidc/callback, and the
# issuer URL is sensitive to a trailing slash (a real reported bug) — the
# portal URL this repo already stores never has one, so left as-is here.
#
# Args: DIR CONTAINER
_homebox_offer_authelia_oidc() {
local DIR="$1" CONTAINER="$2"
declare -F _authelia_provision_oidc_client >/dev/null 2>&1 || return 0
[ -d "$DOCKER_DIR/authelia" ] || return 0
echo ""
local USE_SSO=""
prompt_yn " Add \"Sign in with Authelia\" (OpenID Connect) to Homebox? (y/n):" "n" USE_SSO
[[ "$USE_SSO" =~ ^[Yy]$ ]] || return 0
if grep -q '^HBOX_OIDC_ENABLED=' "$DIR/.env" 2>/dev/null; then
echo ""
log_info "Authelia SSO is already configured for Homebox (HBOX_OIDC_* already set in $DIR/.env)."
local RECONFIGURE=""
prompt_yn " Reconfigure it (registers a fresh Authelia client + secret)? (y/n):" "n" RECONFIGURE
[[ "$RECONFIGURE" =~ ^[Yy]$ ]] || return 0
sed -i '/^HBOX_OIDC_/d; /^HBOX_OPTIONS_TRUST_PROXY=/d' "$DIR/.env"
fi
# Existing installs from before this offer existed won't have env_file
# picked up their .env's OIDC additions otherwise — this repo's own
# compose template gained it above; a pre-existing compose file needs
# the same one-line patch to actually load what's about to be written.
if ! grep -q '^\s*env_file: \.env\s*$' "$DIR/docker-compose.yml" 2>/dev/null; then
sed -i "/^ hostname: /a\\ env_file: .env" "$DIR/docker-compose.yml"
fi
local APP_DOMAIN
APP_DOMAIN="$(_authelia_pick_domain "Domain Homebox is reachable at (number or domain)")"
if [ -z "$APP_DOMAIN" ]; then
log_warning "No domain entered — skipping SSO setup."
return 0
fi
local _2fa="" AUTH_POLICY="two_factor"
prompt_yn " Require two-factor for Homebox logins via Authelia too? (y/n):" "y" _2fa
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
if ! _authelia_provision_oidc_client "Homebox" "homebox" "$AUTH_POLICY" "y" "y" \
"https://${APP_DOMAIN}/api/v1/users/login/oidc/callback"; then
log_warning "Couldn't register Homebox as an OIDC client in Authelia — skipping SSO setup."
return 0
fi
cat >> "$DIR/.env" << ENV
# Written by services/homebox.sh's Authelia SSO step — adds "Sign in with
# Authelia" alongside local login; local accounts keep working unchanged.
HBOX_OIDC_ENABLED=true
HBOX_OIDC_ISSUER_URL=${OIDC_AUTHELIA_PORTAL_URL}
HBOX_OIDC_CLIENT_ID=homebox
HBOX_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET_PLAIN}
HBOX_OIDC_SCOPE=openid profile email groups
HBOX_OPTIONS_TRUST_PROXY=true
ENV
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
(cd "$DIR" && docker compose up -d) \
&& log_success "\"Sign in with Authelia\" added to Homebox — local login still works too." \
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "homebox" "$APP_DOMAIN"
echo ""
log_info "Test the \"Login with Authelia\" button on Homebox's own login page before"
log_info "disabling local login — re-run 'sudo ./setup.sh homebox' (choose update) once"
log_info "you've confirmed it works, and you'll be offered that as a separate step."
}
# Split out from _homebox_offer_authelia_oidc so disabling local login is
# never offered in the same breath as first setting SSO up — same
# reasoning as Mealie/Beszel's equivalent split (confirmed live on Beszel:
# saying yes before actually testing the button leaves both login paths
# broken at once). Only reached from a later "update" rerun once OIDC is
# already configured and the admin declines to reconfigure.
_homebox_offer_disable_local_login() {
local DIR="$1"
grep -q '^HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false' "$DIR/.env" 2>/dev/null && return 0
echo ""
local _tested=""
prompt_yn " Have you ALREADY logged into Homebox successfully using the Authelia button (not just enabled it)? (y/n):" "n" _tested
if [[ ! "$_tested" =~ ^[Yy]$ ]]; then
log_info "Skipped. Test the Authelia login button first, then re-run 'sudo ./setup.sh homebox' (choose update) to come back to this."
return 0
fi
local _disable_local=""
prompt_yn " Also disable Homebox's own local login, so Authelia is the only way in? (y/n):" "n" _disable_local
[[ "$_disable_local" =~ ^[Yy]$ ]] || return 0
log_warning "Anyone without an Authelia account (only a local Homebox one) will no longer be able to log in."
log_info "Reversible any time: set HBOX_OPTIONS_ALLOW_LOCAL_LOGIN back to true in $DIR/.env and 'docker compose up -d'."
local _auto_redirect=""
prompt_yn " Skip Homebox's login page entirely and jump straight to Authelia? (y/n):" "y" _auto_redirect
sed -i '/^HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=/d; /^HBOX_OIDC_AUTO_REDIRECT=/d' "$DIR/.env"
{
echo "HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false"
[[ "$_auto_redirect" =~ ^[Yy]$ ]] && echo "HBOX_OIDC_AUTO_REDIRECT=true"
} >> "$DIR/.env"
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
(cd "$DIR" && docker compose up -d) \
&& log_success "Local login is now disabled — Authelia is the only way into Homebox." \
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
}
install_homebox() { install_homebox() {
require_docker || return 1 require_docker || return 1
log_info "Installing Homebox..." log_info "Installing Homebox..."
@@ -256,6 +379,8 @@ install_homebox() {
( cd "$HB_DIR" && docker compose pull && docker compose up -d ) \ ( cd "$HB_DIR" && docker compose pull && docker compose up -d ) \
&& log_success "Homebox image refreshed" \ && log_success "Homebox image refreshed" \
|| log_warning "Refresh failed — check: docker compose -f $HB_DIR/docker-compose.yml logs" || log_warning "Refresh failed — check: docker compose -f $HB_DIR/docker-compose.yml logs"
_homebox_offer_authelia_oidc "$HB_DIR" "$CONTAINER"
_homebox_offer_disable_local_login "$HB_DIR"
return 0 return 0
;; ;;
cancel) cancel)
@@ -311,6 +436,7 @@ networks:
local HB_PEPPER local HB_PEPPER
HB_PEPPER="$(generate_password 48)" HB_PEPPER="$(generate_password 48)"
backup_if_exists docker-compose.yml
cat > docker-compose.yml << HB_COMPOSE cat > docker-compose.yml << HB_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -320,6 +446,7 @@ services:
container_name: $CONTAINER container_name: $CONTAINER
hostname: $CONTAINER hostname: $CONTAINER
restart: unless-stopped restart: unless-stopped
env_file: .env
environment: environment:
- HBOX_LOG_LEVEL=info - HBOX_LOG_LEVEL=info
- HBOX_WEB_MAX_UPLOAD_SIZE=10 - HBOX_WEB_MAX_UPLOAD_SIZE=10
@@ -331,6 +458,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
HB_COMPOSE HB_COMPOSE
backup_if_exists .env
cat > .env << HB_ENV cat > .env << HB_ENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
@@ -346,6 +474,8 @@ HB_ENV
configure_caddy_for_service "Homebox${INSTANCE_SUFFIX:+ ($INSTANCE_SUFFIX)}" "${CONTAINER}:7745" "homebox${INSTANCE_SUFFIX:+-$INSTANCE_SUFFIX}" configure_caddy_for_service "Homebox${INSTANCE_SUFFIX:+ ($INSTANCE_SUFFIX)}" "${CONTAINER}:7745" "homebox${INSTANCE_SUFFIX:+-$INSTANCE_SUFFIX}"
_homebox_offer_authelia_oidc "$HB_DIR" "$CONTAINER"
write_readme "$HB_DIR" << MD write_readme "$HB_DIR" << MD
# Homebox${INSTANCE_SUFFIX:+ — $INSTANCE_SUFFIX} # Homebox${INSTANCE_SUFFIX:+ — $INSTANCE_SUFFIX}
+8
View File
@@ -183,6 +183,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" 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 [ -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 cat > docker-compose.yml << IMMICH_COMPOSE
name: $PROJECT name: $PROJECT
@@ -686,6 +692,7 @@ S3_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY
fi fi
if [ "$IMMICH_STRATEGY" = "2" ]; then if [ "$IMMICH_STRATEGY" = "2" ]; then
backup_if_exists .env
cat > .env << IMMICH_ENV cat > .env << IMMICH_ENV
# IMMICH CONFIGURATION — External Library Mode # IMMICH CONFIGURATION — External Library Mode
# #
@@ -713,6 +720,7 @@ TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
IMMICH_ENV IMMICH_ENV
else else
backup_if_exists .env
cat > .env << IMMICH_ENV cat > .env << IMMICH_ENV
# IMMICH CONFIGURATION — Unified Library # IMMICH CONFIGURATION — Unified Library
# #
+8
View File
@@ -185,6 +185,11 @@ CBLOCK
[[ "${DRY_RUN:-false}" == "true" ]] && return 0 [[ "${DRY_RUN:-false}" == "true" ]] && return 0
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -314,6 +319,7 @@ networks:
fi fi
if [[ "$USE_GPU" =~ ^[Yy]$ ]]; then if [[ "$USE_GPU" =~ ^[Yy]$ ]]; then
backup_if_exists docker-compose.yml
cat > docker-compose.yml << IOPAINT_GPU cat > docker-compose.yml << IOPAINT_GPU
name: iopaint name: iopaint
@@ -346,6 +352,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
IOPAINT_GPU IOPAINT_GPU
else else
backup_if_exists docker-compose.yml
cat > docker-compose.yml << IOPAINT_CPU cat > docker-compose.yml << IOPAINT_CPU
name: iopaint name: iopaint
@@ -373,6 +380,7 @@ IOPAINT_CPU
fi fi
# ── .env ───────────────────────────────────────────────────────────────── # ── .env ─────────────────────────────────────────────────────────────────
backup_if_exists .env
cat > .env << IOPAINT_ENV cat > .env << IOPAINT_ENV
# IOPaint — change MODEL and restart to switch (no need to edit docker-compose.yml) # IOPaint — change MODEL and restart to switch (no need to edit docker-compose.yml)
+7
View File
@@ -182,6 +182,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -347,6 +352,7 @@ networks:
' '
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << JELLYFIN_COMPOSE cat > docker-compose.yml << JELLYFIN_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -368,6 +374,7 @@ $HWACCEL_BLOCK
${_DISCOVERY_PORTS}${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_DISCOVERY_PORTS}${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
JELLYFIN_COMPOSE JELLYFIN_COMPOSE
backup_if_exists .env
cat > .env << JELLYFIN_ENV cat > .env << JELLYFIN_ENV
MEDIA_PATH=$MEDIA_PATH MEDIA_PATH=$MEDIA_PATH
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -314,6 +319,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << JOPLIN_COMPOSE cat > docker-compose.yml << JOPLIN_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -340,6 +346,7 @@ ${_CADDY_NET_BLOCK}
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
JOPLIN_COMPOSE JOPLIN_COMPOSE
backup_if_exists .env
cat > .env << JOPLIN_ENV cat > .env << JOPLIN_ENV
# Joplin Server configuration # Joplin Server configuration
APP_PORT=22300 APP_PORT=22300
+6
View File
@@ -174,6 +174,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -459,6 +464,7 @@ networks:
" "
fi fi
backup_if_exists "$JS99ER_DIR/docker-compose.yml"
cat > "$JS99ER_DIR/docker-compose.yml" << COMPOSE cat > "$JS99ER_DIR/docker-compose.yml" << COMPOSE
name: js99er name: js99er
+7
View File
@@ -188,6 +188,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -410,6 +415,7 @@ install_koha() {
fi fi
# ── docker-compose.yml ──────────────────────────────────────────────────── # ── docker-compose.yml ────────────────────────────────────────────────────
backup_if_exists docker-compose.yml
cat > docker-compose.yml << KOHA_COMPOSE cat > docker-compose.yml << KOHA_COMPOSE
name: koha name: koha
@@ -481,6 +487,7 @@ ${_CADDY_NET_SECTION}
KOHA_COMPOSE KOHA_COMPOSE
# ── config-main.env ─────────────────────────────────────────────────────── # ── config-main.env ───────────────────────────────────────────────────────
backup_if_exists config-main.env
cat > config-main.env << KOHA_ENV cat > config-main.env << KOHA_ENV
# Koha ILS configuration — generated at install time # Koha ILS configuration — generated at install time
MYSQL_SERVER=koha-db MYSQL_SERVER=koha-db
+7
View File
@@ -388,6 +388,7 @@ for a in data.get('assets', []):
mkdir -p "$DIR" mkdir -p "$DIR"
ensure_docker_dir_ownership "$DIR" ensure_docker_dir_ownership "$DIR"
backup_if_exists "$DIR/docker-compose.yml"
cat > "$DIR/docker-compose.yml" << EOF cat > "$DIR/docker-compose.yml" << EOF
name: kyber-server name: kyber-server
services: services:
@@ -411,6 +412,7 @@ EOF
# Write .env with restricted permissions # Write .env with restricted permissions
# Values are single-quoted so special characters ($, !, &, etc.) are safe. # Values are single-quoted so special characters ($, !, &, etc.) are safe.
# Exception: single quotes inside a value would still break — avoid them. # Exception: single quotes inside a value would still break — avoid them.
backup_if_exists "$DIR/.env"
cat > "$DIR/.env" << EOF cat > "$DIR/.env" << EOF
MAXIMA_CREDENTIALS='${EA_EMAIL}:${EA_PASSWORD}' MAXIMA_CREDENTIALS='${EA_EMAIL}:${EA_PASSWORD}'
KYBER_TOKEN='${KYBER_TOKEN}' KYBER_TOKEN='${KYBER_TOKEN}'
@@ -558,6 +560,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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_USER="${SUDO_USER:-$USER}"
ACTUAL_HOME=$(eval echo "~$ACTUAL_USER") ACTUAL_HOME=$(eval echo "~$ACTUAL_USER")
+7
View File
@@ -188,6 +188,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -353,6 +358,7 @@ install_lyrion() {
_HTTP_PORT_INTERNAL="9000" _HTTP_PORT_INTERNAL="9000"
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << LYRION_COMPOSE cat > docker-compose.yml << LYRION_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -375,6 +381,7 @@ ${_NETWORK_BLOCK}${_PORTS_BLOCK} environment:
LYRION_COMPOSE LYRION_COMPOSE
backup_if_exists .env
cat > .env << LYRION_ENV cat > .env << LYRION_ENV
MUSIC_PATH=$MUSIC_PATH MUSIC_PATH=$MUSIC_PATH
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+6
View File
@@ -174,6 +174,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -254,6 +259,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << MM_COMPOSE cat > docker-compose.yml << MM_COMPOSE
name: mm-$MM_PORT name: mm-$MM_PORT
+7
View File
@@ -170,6 +170,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -244,6 +249,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << MA_COMPOSE cat > docker-compose.yml << MA_COMPOSE
name: mail-archiver name: mail-archiver
@@ -279,6 +285,7 @@ ${_CADDY_NET_BLOCK}
${_CADDY_NET_SECTION} ${_CADDY_NET_SECTION}
MA_COMPOSE MA_COMPOSE
backup_if_exists .env
cat > .env << MA_ENV cat > .env << MA_ENV
# ── General ─────────────────────────────────────────────────────────────────── # ── General ───────────────────────────────────────────────────────────────────
TZ=$TZ_VAL TZ=$TZ_VAL
+7
View File
@@ -213,6 +213,11 @@ CBLOCK
[[ "${DRY_RUN:-false}" == "true" ]] && return 0 [[ "${DRY_RUN:-false}" == "true" ]] && return 0
cat > "$_dir/README.md" 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() { generate_password() {
local _len="${1:-32}" local _len="${1:-32}"
@@ -518,6 +523,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << EOF cat > docker-compose.yml << EOF
name: ${PROJECT} name: ${PROJECT}
@@ -580,6 +586,7 @@ ${_CADDY_NET_BLOCK} healthcheck:
${_CADDY_NET_BLOCK}${_COTURN_SERVICE}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_COTURN_SERVICE}${_CADDY_NET_SECTION}
EOF EOF
backup_if_exists .env
cat > .env << EOF cat > .env << EOF
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -180,6 +180,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -459,6 +464,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << MEALIE_COMPOSE cat > docker-compose.yml << MEALIE_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -483,6 +489,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
MEALIE_COMPOSE MEALIE_COMPOSE
backup_if_exists .env
cat > .env << MEALIE_ENV cat > .env << MEALIE_ENV
# Public URL Mealie is served on — used for email links and OAuth redirects. # 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. # Update if you change your domain or switch from HTTP to HTTPS.
+7
View File
@@ -188,6 +188,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -323,6 +328,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << MC_COMPOSE cat > docker-compose.yml << MC_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -350,6 +356,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
MC_COMPOSE MC_COMPOSE
backup_if_exists .env
cat > .env << MC_ENV cat > .env << MC_ENV
MC_HOSTNAME=$MC_HOSTNAME MC_HOSTNAME=$MC_HOSTNAME
MC_REVERSE_PROXY=false MC_REVERSE_PROXY=false
+2
View File
@@ -1519,6 +1519,7 @@ PREGENINFOEOF
- ./config:/data/config" - ./config:/data/config"
fi fi
backup_if_exists "$MC_DIR/docker-compose.yml"
cat > "$MC_DIR/docker-compose.yml" << COMPOSEEOF cat > "$MC_DIR/docker-compose.yml" << COMPOSEEOF
name: ${MC_NAME} name: ${MC_NAME}
@@ -1902,6 +1903,7 @@ EXPOSE 80
CLIENTDOCKEREOF CLIENTDOCKEREOF
# Standalone compose for the client-mods page (its own folder). # 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 cat > "$CLIENT_MODS_DIR/docker-compose.yml" << CMCOMPOSEEOF
name: ${CM_NAME} name: ${CM_NAME}
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -242,6 +247,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << N8N_COMPOSE cat > docker-compose.yml << N8N_COMPOSE
name: n8n name: n8n
@@ -266,6 +272,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
N8N_COMPOSE N8N_COMPOSE
backup_if_exists .env
cat > .env << N8N_ENV cat > .env << N8N_ENV
# n8n environment — edit before starting if needed # n8n environment — edit before starting if needed
N8N_HOST=n8n N8N_HOST=n8n
+7
View File
@@ -70,6 +70,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 # Match common.sh's eval-based pattern so local vars in install_* are set correctly
prompt_text() { prompt_text() {
@@ -260,6 +265,7 @@ networks:
fi fi
# ── docker-compose.yml ────────────────────────────────────────────────── # ── docker-compose.yml ──────────────────────────────────────────────────
backup_if_exists docker-compose.yml
cat > docker-compose.yml << NCCOMPOSE cat > docker-compose.yml << NCCOMPOSE
name: nextcloud name: nextcloud
services: services:
@@ -290,6 +296,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
NCCOMPOSE NCCOMPOSE
# ── .env ──────────────────────────────────────────────────────────────── # ── .env ────────────────────────────────────────────────────────────────
backup_if_exists .env
cat > .env << NCENV cat > .env << NCENV
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -184,6 +184,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -309,6 +314,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << NTFY_COMPOSE cat > docker-compose.yml << NTFY_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -329,6 +335,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
NTFY_COMPOSE NTFY_COMPOSE
backup_if_exists .env
cat > .env << NTFY_ENV cat > .env << NTFY_ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -70,6 +70,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 # Match common.sh's eval-based pattern so local vars in install_* are set correctly
prompt_text() { prompt_text() {
@@ -280,6 +285,7 @@ networks:
fi fi
# ── docker-compose.yml ────────────────────────────────────────────────── # ── docker-compose.yml ──────────────────────────────────────────────────
backup_if_exists docker-compose.yml
cat > docker-compose.yml << OOCOMPOSE cat > docker-compose.yml << OOCOMPOSE
name: onlyoffice name: onlyoffice
services: services:
@@ -299,6 +305,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
OOCOMPOSE OOCOMPOSE
# ── .env ──────────────────────────────────────────────────────────────── # ── .env ────────────────────────────────────────────────────────────────
backup_if_exists .env
cat > .env << OOENV cat > .env << OOENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
# JWT authentication — keep JWT_SECRET private # JWT authentication — keep JWT_SECRET private
+7
View File
@@ -210,6 +210,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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 # 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 # 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. # (SYS_TIME, SYS_NICE) are part of that same documented baseline.
backup_if_exists docker-compose.yml
cat > docker-compose.yml << PIHOLE_COMPOSE cat > docker-compose.yml << PIHOLE_COMPOSE
name: pihole name: pihole
@@ -351,6 +357,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
PIHOLE_COMPOSE PIHOLE_COMPOSE
backup_if_exists .env
cat > .env << PIHOLE_ENV cat > .env << PIHOLE_ENV
TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}
# Admin web UI password (System Console / login screen). # Admin web UI password (System Console / login screen).
+6
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -242,6 +247,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << PORTAINER_COMPOSE cat > docker-compose.yml << PORTAINER_COMPOSE
name: portainer name: portainer
+1
View File
@@ -1746,6 +1746,7 @@ _pstn_apply_settings() {
# Direct") — unquoted, bash's `source` would treat the second word of # Direct") — unquoted, bash's `source` would treat the second word of
# any such value as a command to run ("Direct: command not found"), # any such value as a command to run ("Direct: command not found"),
# confirmed live while testing the multi-IP change. # confirmed live while testing the multi-IP change.
backup_if_exists "$EA_DIR/.pstn-trunk.env"
cat > "$EA_DIR/.pstn-trunk.env" << ENV cat > "$EA_DIR/.pstn-trunk.env" << ENV
PROVIDER_NAME="${PROVIDER_NAME}" PROVIDER_NAME="${PROVIDER_NAME}"
TRUNK_SERVER="${SERVER}" TRUNK_SERVER="${SERVER}"
+7
View File
@@ -91,6 +91,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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 prompt_yn "Require encrypted connections only? (recommended) (y/n):" "y" _enc
[ "$_enc" = "n" ] || [ "$_enc" = "N" ] && ENCRYPTED_ONLY="0" [ "$_enc" = "n" ] || [ "$_enc" = "N" ] && ENCRYPTED_ONLY="0"
backup_if_exists docker-compose.yml
cat > docker-compose.yml << RD_COMPOSE cat > docker-compose.yml << RD_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -251,6 +257,7 @@ services:
- ./rustdesk_data:/data - ./rustdesk_data:/data
RD_COMPOSE RD_COMPOSE
backup_if_exists .env
cat > .env << RD_ENV cat > .env << RD_ENV
# ── General ─────────────────────────────────────────────────────────────────── # ── General ───────────────────────────────────────────────────────────────────
TZ=$TZ_VAL TZ=$TZ_VAL
+6
View File
@@ -60,6 +60,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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_KEY (e.g. user:12345678:901), blank to skip:" "" FF_KEY
prompt_text " WEB_EXT_API_SECRET, blank to skip:" "" FF_SECRET prompt_text " WEB_EXT_API_SECRET, blank to skip:" "" FF_SECRET
if [ -n "$FF_KEY" ] && [ -n "$FF_SECRET" ]; then if [ -n "$FF_KEY" ] && [ -n "$FF_SECRET" ]; then
backup_if_exists "$SS_DIR/.env"
cat > "$SS_DIR/.env" << ENVEOF cat > "$SS_DIR/.env" << ENVEOF
WEB_EXT_API_KEY="$FF_KEY" WEB_EXT_API_KEY="$FF_KEY"
WEB_EXT_API_SECRET="$FF_SECRET" WEB_EXT_API_SECRET="$FF_SECRET"
+7
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -242,6 +247,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << PDF_COMPOSE cat > docker-compose.yml << PDF_COMPOSE
name: stirling-pdf name: stirling-pdf
@@ -261,6 +267,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
PDF_COMPOSE PDF_COMPOSE
backup_if_exists .env
cat > .env << PDF_ENV cat > .env << PDF_ENV
# Stirling PDF configuration # Stirling PDF configuration
+7
View File
@@ -147,6 +147,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -225,6 +230,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << EOF cat > docker-compose.yml << EOF
name: syncthing name: syncthing
@@ -249,6 +255,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
EOF EOF
backup_if_exists .env
cat > .env << ENV cat > .env << ENV
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
PUID=$PUID PUID=$PUID
+7
View File
@@ -180,6 +180,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -474,6 +479,7 @@ networks:
- \"${PROTO_MIN}-${PROTO_MAX}:${PROTO_MIN}-${PROTO_MAX}/udp\"" - \"${PROTO_MIN}-${PROTO_MAX}:${PROTO_MIN}-${PROTO_MAX}/udp\""
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << TRACCAR_COMPOSE cat > docker-compose.yml << TRACCAR_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -560,6 +566,7 @@ SMS_HTTP_PASSWORD=$SMS_HTTP_PASSWORD
fi fi
fi fi
backup_if_exists .env
cat > .env << TRACCAR_ENV cat > .env << TRACCAR_ENV
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET
+7
View File
@@ -89,6 +89,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -238,6 +243,7 @@ networks:
fi fi
# Unquoted heredoc; ${...} used for caddy_net vars; all Docker Compose vars escaped with \$ # 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 cat > docker-compose.yml << UNIFI_COMPOSE
name: $PROJECT 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"}]}); db.getSiblingDB("\${MONGO_DBNAME}_stat").createUser({user: "\${MONGO_USER}", pwd: "\${MONGO_PASS}", roles: [{role: "\${MONGO_ROLE}", db: "\${MONGO_DBNAME}_stat"}]});
UNIFI_COMPOSE UNIFI_COMPOSE
backup_if_exists .env
cat > .env << UNIFI_ENV cat > .env << UNIFI_ENV
# ── General ─────────────────────────────────────────────────────────────────── # ── General ───────────────────────────────────────────────────────────────────
TZ=$TZ_VAL TZ=$TZ_VAL
+6
View File
@@ -177,6 +177,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -277,6 +282,7 @@ ${UPTIME_ENV_BLOCK}
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << UPTIME_COMPOSE cat > docker-compose.yml << UPTIME_COMPOSE
name: uptime-kuma name: uptime-kuma
+7
View File
@@ -194,6 +194,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -420,6 +425,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << VW_COMPOSE cat > docker-compose.yml << VW_COMPOSE
name: $CONTAINER name: $CONTAINER
@@ -437,6 +443,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
VW_COMPOSE VW_COMPOSE
backup_if_exists .env
cat > .env << VW_ENV cat > .env << VW_ENV
# ── General ─────────────────────────────────────────────────────────────────── # ── General ───────────────────────────────────────────────────────────────────
TZ=$TZ_VAL TZ=$TZ_VAL
+7
View File
@@ -64,6 +64,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -151,6 +156,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << WT_COMPOSE cat > docker-compose.yml << WT_COMPOSE
name: watchtower name: watchtower
@@ -179,6 +185,7 @@ ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
WT_COMPOSE WT_COMPOSE
# Create .env # Create .env
backup_if_exists .env
cat > .env << WT_ENV cat > .env << WT_ENV
# Watchtower Configuration # Watchtower Configuration
# ========================= # =========================
+7
View File
@@ -68,6 +68,11 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -126,6 +131,7 @@ install_watchyourlan() {
prompt_text "GUI port [8840]:" "8840" GUI_PORT prompt_text "GUI port [8840]:" "8840" GUI_PORT
[ -z "$GUI_PORT" ] && GUI_PORT="8840" [ -z "$GUI_PORT" ] && GUI_PORT="8840"
backup_if_exists docker-compose.yml
cat > docker-compose.yml << 'WYL_COMPOSE' cat > docker-compose.yml << 'WYL_COMPOSE'
name: watchyourlan name: watchyourlan
@@ -141,6 +147,7 @@ services:
- ./watchyourlan_data:/data - ./watchyourlan_data:/data
WYL_COMPOSE WYL_COMPOSE
backup_if_exists .env
cat > .env << WYL_ENV cat > .env << WYL_ENV
# ── General ─────────────────────────────────────────────────────────────────── # ── General ───────────────────────────────────────────────────────────────────
TZ=$TZ_VAL TZ=$TZ_VAL
+7
View File
@@ -199,6 +199,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
@@ -302,6 +307,7 @@ networks:
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << WGEASY_COMPOSE cat > docker-compose.yml << WGEASY_COMPOSE
name: wg-easy name: wg-easy
@@ -332,6 +338,7 @@ services:
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION} ${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
WGEASY_COMPOSE WGEASY_COMPOSE
backup_if_exists .env
cat > .env << WGEASY_ENV cat > .env << WGEASY_ENV
WG_HOST=$WG_HOST WG_HOST=$WG_HOST
# Plain-text password — used only if PASSWORD_HASH could not be generated above # Plain-text password — used only if PASSWORD_HASH could not be generated above
+6
View File
@@ -169,6 +169,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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. # network_mode: host — server.py reaches Wolf at localhost:47989 directly.
# Docker socket (ro) — server.py calls `docker logs wolf` to read secrets. # Docker socket (ro) — server.py calls `docker logs wolf` to read secrets.
log_info "Writing docker-compose.yml..." log_info "Writing docker-compose.yml..."
backup_if_exists "$WOLFPAIR_DIR/docker-compose.yml"
cat > "$WOLFPAIR_DIR/docker-compose.yml" << 'COMPOSE' cat > "$WOLFPAIR_DIR/docker-compose.yml" << 'COMPOSE'
name: wolf-pair name: wolf-pair
+7
View File
@@ -179,6 +179,11 @@ CBLOCK
mkdir -p "$_dir" mkdir -p "$_dir"
cat > "$_dir/README.md" 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 fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # 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 # 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 # 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. # in .env — use 'manage.sh update-network' to regenerate if they change.
backup_if_exists docker-compose.yml
cat > docker-compose.yml << EOF cat > docker-compose.yml << EOF
name: wolf name: wolf
@@ -922,6 +928,7 @@ EOF
log_success "docker-compose.yml created" log_success "docker-compose.yml created"
# Save the game storage path so it's visible and editable later # Save the game storage path so it's visible and editable later
backup_if_exists .env
cat > .env << EOF cat > .env << EOF
# Wolf game/ROM storage root — edit this and run ./manage.sh update-storage to apply # Wolf game/ROM storage root — edit this and run ./manage.sh update-storage to apply
GAME_STORAGE_DIR=${GAME_STORAGE_DIR} GAME_STORAGE_DIR=${GAME_STORAGE_DIR}
+7
View File
@@ -207,6 +207,11 @@ CBLOCK
cat > "$_dir/README.md" cat > "$_dir/README.md"
chown "$ACTUAL_USER:$ACTUAL_USER" "$_dir/README.md" 2>/dev/null || true 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 fi
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
@@ -361,6 +366,7 @@ PHPINI
" "
fi fi
backup_if_exists docker-compose.yml
cat > docker-compose.yml << WPCOMPOSE cat > docker-compose.yml << WPCOMPOSE
name: $CONTAINER name: $CONTAINER
@@ -398,6 +404,7 @@ networks:
${_CADDY_NET_SECTION} ${_CADDY_NET_SECTION}
WPCOMPOSE WPCOMPOSE
backup_if_exists .env
cat > .env << WPENV cat > .env << WPENV
TZ=$TZ_VAL TZ=$TZ_VAL
CADDY_NET=$SITE_CADDY_NET CADDY_NET=$SITE_CADDY_NET