Merge pull request #275 from outis1one/claude/ionos-script-integration-x32ofw
Claude/ionos script integration x32ofw
This commit is contained in:
@@ -245,6 +245,28 @@ install_actualbudget() {
|
|||||||
AB_DIR="$DOCKER_DIR/actualbudget-$_suffix"
|
AB_DIR="$DOCKER_DIR/actualbudget-$_suffix"
|
||||||
CONTAINER="actualbudget-$_suffix"
|
CONTAINER="actualbudget-$_suffix"
|
||||||
log_info "New instance: $AB_DIR"
|
log_info "New instance: $AB_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$AB_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Actual Budget image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$AB_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Actual Budget image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $AB_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,28 @@ install_audiobookshelf() {
|
|||||||
CONTAINER="audiobookshelf-$_suffix"
|
CONTAINER="audiobookshelf-$_suffix"
|
||||||
DEFAULT_AUDIOBOOKS="$ACTUAL_HOME/audiobooks-$_suffix"
|
DEFAULT_AUDIOBOOKS="$ACTUAL_HOME/audiobooks-$_suffix"
|
||||||
log_info "New instance: $ABS_DIR"
|
log_info "New instance: $ABS_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$ABS_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Audiobookshelf image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$ABS_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Audiobookshelf image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $ABS_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -262,6 +262,28 @@ install_emby() {
|
|||||||
CONTAINER="emby-$_suffix"
|
CONTAINER="emby-$_suffix"
|
||||||
DEFAULT_MEDIA="$ACTUAL_HOME/media-$_suffix"
|
DEFAULT_MEDIA="$ACTUAL_HOME/media-$_suffix"
|
||||||
log_info "New instance: $EMBY_DIR"
|
log_info "New instance: $EMBY_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$EMBY_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Emby image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$EMBY_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Emby image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $EMBY_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,28 @@ install_filebrowser() {
|
|||||||
FB_DIR="$DOCKER_DIR/filebrowser-$_suffix"
|
FB_DIR="$DOCKER_DIR/filebrowser-$_suffix"
|
||||||
CONTAINER="filebrowser-$_suffix"
|
CONTAINER="filebrowser-$_suffix"
|
||||||
log_info "New instance: $FB_DIR"
|
log_info "New instance: $FB_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$FB_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the FileBrowser image only — files-directory mount and Caddy config are left as-is."
|
||||||
|
( cd "$FB_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "FileBrowser image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $FB_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+54
-16
@@ -8,7 +8,18 @@
|
|||||||
#
|
#
|
||||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- FINDMYDEVICE ----).
|
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- FINDMYDEVICE ----).
|
||||||
# Own ~/docker/fmd/ with a standalone docker-compose.yml + .env.
|
# Own ~/docker/fmd/ with a standalone docker-compose.yml + .env.
|
||||||
# Mobile app: "FindMyDevice" on F-Droid — not the Play Store version.
|
# Mobile app: "FMD" (by Nulide) on F-Droid — not the Google Find My Device app.
|
||||||
|
#
|
||||||
|
# Image: registry.gitlab.com/fmd-foss/fmd-server (GitLab's own registry, no
|
||||||
|
# Docker Hub image exists). The project has moved twice — originally
|
||||||
|
# nulide/findmydevice on Docker Hub, then gitlab.com/Nulide/findmydeviceserver,
|
||||||
|
# now gitlab.com/fmd-foss/fmd-server — and was rewritten from Node.js to
|
||||||
|
# Go+React along the way. Confirmed live: the old nulide/findmydevice image
|
||||||
|
# no longer exists at all ("pull access denied"), so this service never
|
||||||
|
# actually started on any box that installed it before this fix. The old
|
||||||
|
# single shared FMD_ADMIN_PASSWORD model is gone too — the current server
|
||||||
|
# uses per-user self-registration gated by an optional FMD_REGISTRATIONTOKEN,
|
||||||
|
# and stores its database at /var/lib/fmd-server/db, not /fmd/data.
|
||||||
|
|
||||||
# ── Standalone bootstrap ──────────────────────────────────────────────────────
|
# ── Standalone bootstrap ──────────────────────────────────────────────────────
|
||||||
# Detected when the script is executed directly rather than sourced by setup.sh.
|
# Detected when the script is executed directly rather than sourced by setup.sh.
|
||||||
@@ -224,7 +235,7 @@ install_fmd() {
|
|||||||
echo "[DRY-RUN] FindMyDevice would:"
|
echo "[DRY-RUN] FindMyDevice would:"
|
||||||
echo " - Offer to add a new, separate instance if one already exists"
|
echo " - Offer to add a new, separate instance if one already exists"
|
||||||
echo " - Create \$DOCKER_DIR/fmd(-<name>) with docker-compose.yml + .env (data/)"
|
echo " - Create \$DOCKER_DIR/fmd(-<name>) with docker-compose.yml + .env (data/)"
|
||||||
echo " - Generate a random admin password"
|
echo " - Generate a random registration token"
|
||||||
echo " - Auto-scan for a free host port if this is an additional instance"
|
echo " - Auto-scan for a free host port if this is an additional instance"
|
||||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||||
return 0
|
return 0
|
||||||
@@ -256,6 +267,28 @@ install_fmd() {
|
|||||||
FMD_DIR="$DOCKER_DIR/fmd-$_suffix"
|
FMD_DIR="$DOCKER_DIR/fmd-$_suffix"
|
||||||
CONTAINER="fmd-$_suffix"
|
CONTAINER="fmd-$_suffix"
|
||||||
log_info "New instance: $FMD_DIR"
|
log_info "New instance: $FMD_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$FMD_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the FindMyDevice image only — token, port, and Caddy config are left as-is."
|
||||||
|
( cd "$FMD_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "FindMyDevice image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $FMD_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -269,8 +302,8 @@ install_fmd() {
|
|||||||
ensure_docker_dir_ownership "$FMD_DIR"
|
ensure_docker_dir_ownership "$FMD_DIR"
|
||||||
cd "$FMD_DIR" || return 1
|
cd "$FMD_DIR" || return 1
|
||||||
|
|
||||||
local FMD_PASS
|
local FMD_TOKEN
|
||||||
FMD_PASS=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
FMD_TOKEN=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
||||||
|
|
||||||
# Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh):
|
# Mirrors configure_caddy_for_service's own mode resolution (lib/common.sh):
|
||||||
# explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy,
|
# explicit CADDY_MODE from the site config wins, then a local ~/docker/caddy,
|
||||||
@@ -300,21 +333,21 @@ name: $CONTAINER
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
fmd:
|
fmd:
|
||||||
image: nulide/findmydevice
|
image: registry.gitlab.com/fmd-foss/fmd-server:0
|
||||||
container_name: $CONTAINER
|
container_name: $CONTAINER
|
||||||
hostname: $CONTAINER
|
hostname: $CONTAINER
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- FMD_ADMIN_PASSWORD=\${FMD_ADMIN_PASSWORD}
|
- FMD_REGISTRATIONTOKEN=\${FMD_REGISTRATIONTOKEN}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/fmd/data
|
- ./data:/var/lib/fmd-server/db
|
||||||
ports:
|
ports:
|
||||||
- "${WEB_PORT}:8080"
|
- "${WEB_PORT}:8080"
|
||||||
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
${_CADDY_NET_BLOCK}${_CADDY_NET_SECTION}
|
||||||
FMD_COMPOSE
|
FMD_COMPOSE
|
||||||
|
|
||||||
cat > .env << FMD_ENV
|
cat > .env << FMD_ENV
|
||||||
FMD_ADMIN_PASSWORD=$FMD_PASS
|
FMD_REGISTRATIONTOKEN=$FMD_TOKEN
|
||||||
CADDY_NET=$SITE_CADDY_NET
|
CADDY_NET=$SITE_CADDY_NET
|
||||||
FMD_ENV
|
FMD_ENV
|
||||||
|
|
||||||
@@ -334,8 +367,11 @@ This is a separate, fully isolated instance (own server, own password, own
|
|||||||
port) — not shared devices with another FindMyDevice instance.")
|
port) — not shared devices with another FindMyDevice instance.")
|
||||||
|
|
||||||
- Web UI: http://localhost:${WEB_PORT}
|
- Web UI: http://localhost:${WEB_PORT}
|
||||||
- Admin password: stored in \`.env\` (\`FMD_ADMIN_PASSWORD\`)
|
- Registration token: stored in \`.env\` (\`FMD_REGISTRATIONTOKEN\`) — the FMD
|
||||||
- App data: \`data/\`
|
app must supply this when creating an account on this server, so a
|
||||||
|
stranger who finds the URL can't just self-register. Leave the \`.env\`
|
||||||
|
value blank and restart if you'd rather allow open registration.
|
||||||
|
- App data: \`data/\` (mounted at the container's own db path)
|
||||||
|
|
||||||
## Manage
|
## Manage
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
@@ -347,9 +383,11 @@ docker compose pull && docker compose up -d # update
|
|||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
## Mobile app
|
## Mobile app
|
||||||
Install **FindMyDevice** from **F-Droid** (not the Play Store version):
|
Install **FMD** from **F-Droid** (search "Find My Device", by Nulide — not
|
||||||
1. Open the app → Settings → Server URL → \`http://YOUR-SERVER-IP:${WEB_PORT}\`
|
the Google "Find My Device" app):
|
||||||
2. Enter your admin password from \`.env\`
|
1. Open the app → create/register an account → Server URL →
|
||||||
|
\`http://YOUR-SERVER-IP:${WEB_PORT}\`
|
||||||
|
2. Enter the registration token from \`.env\` when prompted
|
||||||
3. Grant location and accessibility permissions
|
3. Grant location and accessibility permissions
|
||||||
MD
|
MD
|
||||||
|
|
||||||
@@ -360,9 +398,9 @@ MD
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Access at: http://localhost:${WEB_PORT}"
|
echo " Access at: http://localhost:${WEB_PORT}"
|
||||||
echo " Password: $FMD_PASS (saved in .env)"
|
echo " Registration token: $FMD_TOKEN (saved in .env)"
|
||||||
echo " Mobile app: FindMyDevice on F-Droid"
|
echo " Mobile app: FMD (by Nulide) on F-Droid"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,28 @@ install_homebox() {
|
|||||||
HB_DIR="$DOCKER_DIR/homebox-$_suffix"
|
HB_DIR="$DOCKER_DIR/homebox-$_suffix"
|
||||||
CONTAINER="homebox-$_suffix"
|
CONTAINER="homebox-$_suffix"
|
||||||
log_info "New instance: $HB_DIR"
|
log_info "New instance: $HB_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$HB_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Homebox image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( 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"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,28 @@ install_immich() {
|
|||||||
C_DB="immich_postgres_$_suffix"
|
C_DB="immich_postgres_$_suffix"
|
||||||
DEFAULT_PHOTOS="$ACTUAL_HOME/photos-$_suffix"
|
DEFAULT_PHOTOS="$ACTUAL_HOME/photos-$_suffix"
|
||||||
log_info "New instance: $IMMICH_DIR"
|
log_info "New instance: $IMMICH_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$IMMICH_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Immich image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$IMMICH_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Immich image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $IMMICH_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,28 @@ install_jellyfin() {
|
|||||||
log_warning "DLNA/discovery (1900/udp, 7359/udp) are fixed, host-wide ports already"
|
log_warning "DLNA/discovery (1900/udp, 7359/udp) are fixed, host-wide ports already"
|
||||||
log_warning "claimed by the first instance — this instance skips them (web UI and"
|
log_warning "claimed by the first instance — this instance skips them (web UI and"
|
||||||
log_warning "app-based streaming are unaffected; DLNA auto-discovery is not)."
|
log_warning "app-based streaming are unaffected; DLNA auto-discovery is not)."
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$JELLYFIN_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Jellyfin image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$JELLYFIN_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Jellyfin image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $JELLYFIN_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,28 @@ install_joplin() {
|
|||||||
CONTAINER="joplin-$_suffix"
|
CONTAINER="joplin-$_suffix"
|
||||||
DB_CONTAINER="joplin-db-$_suffix"
|
DB_CONTAINER="joplin-db-$_suffix"
|
||||||
log_info "New instance: $JOPLIN_DIR"
|
log_info "New instance: $JOPLIN_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$JOPLIN_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Joplin Server image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$JOPLIN_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Joplin Server image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $JOPLIN_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -279,6 +279,28 @@ install_lyrion() {
|
|||||||
log_warning "loses zero-config Chromecast/Squeezebox discovery — enter its address"
|
log_warning "loses zero-config Chromecast/Squeezebox discovery — enter its address"
|
||||||
log_warning "manually in the Squeezer app / Squeezebox firmware instead."
|
log_warning "manually in the Squeezer app / Squeezebox firmware instead."
|
||||||
log_info "New instance: $LYRION_DIR"
|
log_info "New instance: $LYRION_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$LYRION_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Lyrion image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$LYRION_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Lyrion image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $LYRION_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -246,6 +246,28 @@ install_mealie() {
|
|||||||
MEALIE_DIR="$DOCKER_DIR/mealie-$_suffix"
|
MEALIE_DIR="$DOCKER_DIR/mealie-$_suffix"
|
||||||
CONTAINER="mealie-$_suffix"
|
CONTAINER="mealie-$_suffix"
|
||||||
log_info "New instance: $MEALIE_DIR"
|
log_info "New instance: $MEALIE_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$MEALIE_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Mealie image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$MEALIE_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Mealie image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $MEALIE_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,28 @@ install_meshcentral() {
|
|||||||
MC_DIR="$DOCKER_DIR/meshcentral-$_suffix"
|
MC_DIR="$DOCKER_DIR/meshcentral-$_suffix"
|
||||||
CONTAINER="meshcentral-$_suffix"
|
CONTAINER="meshcentral-$_suffix"
|
||||||
log_info "New instance: $MC_DIR"
|
log_info "New instance: $MC_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$MC_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the MeshCentral image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$MC_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "MeshCentral image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $MC_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,28 @@ install_ntfy() {
|
|||||||
NTFY_DIR="$DOCKER_DIR/ntfy-$_suffix"
|
NTFY_DIR="$DOCKER_DIR/ntfy-$_suffix"
|
||||||
CONTAINER="ntfy-$_suffix"
|
CONTAINER="ntfy-$_suffix"
|
||||||
log_info "New instance: $NTFY_DIR"
|
log_info "New instance: $NTFY_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$NTFY_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the ntfy image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$NTFY_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "ntfy image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $NTFY_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,28 @@ install_rustdesk() {
|
|||||||
RD_DIR="$DOCKER_DIR/rustdesk-$_suffix"
|
RD_DIR="$DOCKER_DIR/rustdesk-$_suffix"
|
||||||
CONTAINER="rustdesk-$_suffix"
|
CONTAINER="rustdesk-$_suffix"
|
||||||
log_info "New instance: $RD_DIR"
|
log_info "New instance: $RD_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$RD_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the RustDesk image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$RD_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "RustDesk image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $RD_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,28 @@ install_traccar() {
|
|||||||
PROTO_MAX=$((5150 + _offset))
|
PROTO_MAX=$((5150 + _offset))
|
||||||
|
|
||||||
log_info "New instance: $TRACCAR_DIR (device protocols $PROTO_MIN-$PROTO_MAX)"
|
log_info "New instance: $TRACCAR_DIR (device protocols $PROTO_MIN-$PROTO_MAX)"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$TRACCAR_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Traccar image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$TRACCAR_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Traccar image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $TRACCAR_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,28 @@ install_unifi() {
|
|||||||
DB_CONTAINER="unifi-db-$_suffix"
|
DB_CONTAINER="unifi-db-$_suffix"
|
||||||
APP_CONTAINER="unifi-app-$_suffix"
|
APP_CONTAINER="unifi-app-$_suffix"
|
||||||
log_info "New instance: $UNIFI_DIR"
|
log_info "New instance: $UNIFI_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$UNIFI_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the UniFi image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$UNIFI_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "UniFi image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $UNIFI_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,28 @@ install_vaultwarden() {
|
|||||||
VW_DIR="$DOCKER_DIR/vaultwarden-$_suffix"
|
VW_DIR="$DOCKER_DIR/vaultwarden-$_suffix"
|
||||||
CONTAINER="vaultwarden-$_suffix"
|
CONTAINER="vaultwarden-$_suffix"
|
||||||
log_info "New instance: $VW_DIR"
|
log_info "New instance: $VW_DIR"
|
||||||
|
else
|
||||||
|
# "Manage that install" on THIS instance — the banner above promises
|
||||||
|
# update/fresh/cancel, so actually offer it instead of falling straight
|
||||||
|
# through into the same unconditional-overwrite flow as a new install.
|
||||||
|
if [[ -f "$VW_DIR/docker-compose.yml" ]]; then
|
||||||
|
local MODE=""
|
||||||
|
prompt_reinstall_mode MODE
|
||||||
|
case "$MODE" in
|
||||||
|
update)
|
||||||
|
log_info "Refreshing the Vaultwarden image only — existing config, port, and Caddy setup are left as-is."
|
||||||
|
( cd "$VW_DIR" && docker compose pull && docker compose up -d ) \
|
||||||
|
&& log_success "Vaultwarden image refreshed" \
|
||||||
|
|| log_warning "Refresh failed — check: docker compose -f $VW_DIR/docker-compose.yml logs"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
cancel)
|
||||||
|
log_info "Leaving the existing install as-is."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
fresh) ;; # fall through to the full install flow below
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# category menu you loop through
|
# category menu you loop through
|
||||||
# sudo ./setup.sh <service> ... install one or more services directly
|
# sudo ./setup.sh <service> ... install one or more services directly
|
||||||
# ./setup.sh --list list available services (grouped)
|
# ./setup.sh --list list available services (grouped)
|
||||||
|
# ./setup.sh --status list services with install status (no whiptail)
|
||||||
# ./setup.sh --version print version
|
# ./setup.sh --version print version
|
||||||
#
|
#
|
||||||
# Flags:
|
# Flags:
|
||||||
@@ -33,13 +34,14 @@ declare -A SERVICE_PRIORITY=( [caddy]=1 [crowdsec]=2 [authelia]=3 )
|
|||||||
declare -A SERVICE_ALIAS=( [asterisk-digital-ocean]=asterisk )
|
declare -A SERVICE_ALIAS=( [asterisk-digital-ocean]=asterisk )
|
||||||
|
|
||||||
# ── Parse flags / collect service names ──────────────────────────────────────
|
# ── Parse flags / collect service names ──────────────────────────────────────
|
||||||
DRY_RUN=false; UNATTENDED=false; DO_LIST=false
|
DRY_RUN=false; UNATTENDED=false; DO_LIST=false; DO_STATUS=false
|
||||||
REQUESTED=()
|
REQUESTED=()
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--dry-run) DRY_RUN=true ;;
|
--dry-run) DRY_RUN=true ;;
|
||||||
--unattended) UNATTENDED=true ;;
|
--unattended) UNATTENDED=true ;;
|
||||||
--list|-l) DO_LIST=true ;;
|
--list|-l) DO_LIST=true ;;
|
||||||
|
--status) DO_STATUS=true ;;
|
||||||
--version|-V) cat "$HERE/VERSION" 2>/dev/null || echo "unknown"; exit 0 ;;
|
--version|-V) cat "$HERE/VERSION" 2>/dev/null || echo "unknown"; exit 0 ;;
|
||||||
-h|--help) sed -n '2,18p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
-h|--help) sed -n '2,18p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||||
-*) echo "Unknown flag: $arg" >&2; exit 1 ;;
|
-*) echo "Unknown flag: $arg" >&2; exit 1 ;;
|
||||||
@@ -130,6 +132,25 @@ list_services() {
|
|||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Plain-text version of the whiptail checklist's [installed] marker — same
|
||||||
|
# is_installed() calls, no whiptail involved. Exists so "is X actually
|
||||||
|
# installed" can be answered by reading terminal output directly instead of
|
||||||
|
# a checklist screen, where a narrow/resized terminal can truncate or wrap
|
||||||
|
# the "[installed]" suffix off-screen without it being obvious that's what
|
||||||
|
# happened.
|
||||||
|
print_status() {
|
||||||
|
local g name marker
|
||||||
|
while IFS= read -r g; do
|
||||||
|
echo ""; echo "── ${g^^} ──"
|
||||||
|
while IFS= read -r name; do
|
||||||
|
marker="not installed"
|
||||||
|
is_installed "$name" && marker="INSTALLED"
|
||||||
|
printf " %-20s %-12s %s\n" "$name" "$marker" "${SERVICE_DESC[$name]}"
|
||||||
|
done < <(services_in_group "$g")
|
||||||
|
done < <(groups_present)
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
# ── Site defaults wizard ──────────────────────────────────────────────────────
|
# ── Site defaults wizard ──────────────────────────────────────────────────────
|
||||||
# Prompts for timezone, base domain, and Caddy network name; saves to .config.
|
# Prompts for timezone, base domain, and Caddy network name; saves to .config.
|
||||||
# Run directly: sudo ./setup.sh configure
|
# Run directly: sudo ./setup.sh configure
|
||||||
@@ -206,6 +227,9 @@ run_site_configure() {
|
|||||||
# ── --list ───────────────────────────────────────────────────────────────────
|
# ── --list ───────────────────────────────────────────────────────────────────
|
||||||
if [ "$DO_LIST" = true ]; then list_services; exit 0; fi
|
if [ "$DO_LIST" = true ]; then list_services; exit 0; fi
|
||||||
|
|
||||||
|
# ── --status ─────────────────────────────────────────────────────────────────
|
||||||
|
if [ "$DO_STATUS" = true ]; then print_status; exit 0; fi
|
||||||
|
|
||||||
# ── configure: show/update site-wide defaults ────────────────────────────────
|
# ── configure: show/update site-wide defaults ────────────────────────────────
|
||||||
if [ "${REQUESTED[*]:-}" = "configure" ]; then
|
if [ "${REQUESTED[*]:-}" = "configure" ]; then
|
||||||
require_root
|
require_root
|
||||||
|
|||||||
Reference in New Issue
Block a user