Merge pull request #396 from outis1one/claude/frigate-authelia-openid-0l1htj
Claude/frigate authelia openid 0l1htj
This commit is contained in:
@@ -493,6 +493,7 @@ right (Portainer, ntfy), not general familiarity with the product:
|
||||
| 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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+67
-5
@@ -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
|
||||
echo ""
|
||||
prompt_yn "Protect Frigate with Authelia SSO (disables Frigate's own login)? (y/n):" "y" FRIGATE_USE_AUTHELIA
|
||||
fi
|
||||
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):" "$_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}}"
|
||||
@@ -196,6 +201,124 @@ fi
|
||||
|
||||
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() {
|
||||
require_docker || return 1
|
||||
log_info "Installing Homebox..."
|
||||
@@ -256,6 +379,8 @@ install_homebox() {
|
||||
( cd "$HB_DIR" && docker compose pull && docker compose up -d ) \
|
||||
&& log_success "Homebox image refreshed" \
|
||||
|| 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
|
||||
;;
|
||||
cancel)
|
||||
@@ -311,6 +436,7 @@ networks:
|
||||
local HB_PEPPER
|
||||
HB_PEPPER="$(generate_password 48)"
|
||||
|
||||
backup_if_exists docker-compose.yml
|
||||
cat > docker-compose.yml << HB_COMPOSE
|
||||
name: $CONTAINER
|
||||
|
||||
@@ -320,6 +446,7 @@ services:
|
||||
container_name: $CONTAINER
|
||||
hostname: $CONTAINER
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
- HBOX_LOG_LEVEL=info
|
||||
- HBOX_WEB_MAX_UPLOAD_SIZE=10
|
||||
@@ -331,6 +458,7 @@ services:
|
||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||
HB_COMPOSE
|
||||
|
||||
backup_if_exists .env
|
||||
cat > .env << HB_ENV
|
||||
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}"
|
||||
|
||||
_homebox_offer_authelia_oidc "$HB_DIR" "$CONTAINER"
|
||||
|
||||
write_readme "$HB_DIR" << MD
|
||||
# Homebox${INSTANCE_SUFFIX:+ — $INSTANCE_SUFFIX}
|
||||
|
||||
|
||||
@@ -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