From d4e06914e70124b404c9e147b5291f9f3f9613ee Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 8 Jun 2026 21:10:37 +0000 Subject: [PATCH] =?UTF-8?q?Add=20remote=20Caddy=20support=20=E2=80=94=20ge?= =?UTF-8?q?nerate=20snippet=20files=20when=20Caddy=20is=20on=20another=20h?= =?UTF-8?q?ost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New site config key: CADDY_REMOTE_HOST (set via 'sudo ./setup.sh configure'). When set, configure_caddy_for_service operates in "remote" mode instead of writing to a local Caddyfile: - Upstream uses CADDY_REMOTE_HOST:PORT (host IP, not container name) - Snippet saved to ~/docker/caddy-snippets/.caddy - User is shown scp/rsync commands to copy it to the Caddy machine Three modes in configure_caddy_for_service (lib/common.sh and inline stubs): local: ~/docker/caddy/ exists → write Caddyfile + reload (existing behavior) remote: CADDY_REMOTE_HOST set → save snippet, print copy instructions none: neither configured → silent return (unchanged) All 31 service standalone bootstrap stubs updated with the new logic. CADDY_REMOTE_HOST global added to all 42 standalone bootstrap sections. setup.sh configure now prompts for CADDY_REMOTE_HOST with a clear explanation. wolf.sh: add missing stubs (configure_caddy_for_service, write_readme, prompt_yn, ensure_docker_dir_ownership) and the Authelia/Caddy/start calls that were missing from the install function. https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt --- lib/common.sh | 144 ++++++++++++++++++++++----------- services/actualbudget.sh | 106 ++++++++++++++++-------- services/arm.sh | 108 ++++++++++++++++--------- services/audiobookshelf.sh | 106 ++++++++++++++++-------- services/authelia.sh | 108 ++++++++++++++++--------- services/backup.sh | 108 ++++++++++++++++--------- services/borg-backup.sh | 108 ++++++++++++++++--------- services/caddy.sh | 108 ++++++++++++++++--------- services/crowdsec.sh | 1 + services/ddclient.sh | 108 ++++++++++++++++--------- services/emby.sh | 108 ++++++++++++++++--------- services/filebrowser.sh | 106 ++++++++++++++++-------- services/fmd.sh | 108 ++++++++++++++++--------- services/frigate-audio.sh | 108 ++++++++++++++++--------- services/frigate-notify.sh | 108 ++++++++++++++++--------- services/frigate.sh | 108 ++++++++++++++++--------- services/gaming-backup.sh | 1 + services/gatus.sh | 108 ++++++++++++++++--------- services/homeassistant.sh | 108 ++++++++++++++++--------- services/immich.sh | 106 ++++++++++++++++-------- services/jellyfin.sh | 106 ++++++++++++++++-------- services/js99er.sh | 107 +++++++++++++++--------- services/lyrion.sh | 108 ++++++++++++++++--------- services/magicmirror.sh | 108 ++++++++++++++++--------- services/mail-archiver.sh | 108 ++++++++++++++++--------- services/mealie.sh | 106 ++++++++++++++++-------- services/meshcentral.sh | 108 ++++++++++++++++--------- services/minecraft.sh | 1 + services/ntfy.sh | 108 ++++++++++++++++--------- services/portainer.sh | 106 ++++++++++++++++-------- services/rustdesk.sh | 1 + services/silent-send.sh | 1 + services/sky-cam.sh | 1 + services/sync-cc.sh | 1 + services/traccar.sh | 108 ++++++++++++++++--------- services/unifi.sh | 1 + services/uptimekuma.sh | 108 ++++++++++++++++--------- services/vaultwarden.sh | 108 ++++++++++++++++--------- services/watchtower.sh | 1 + services/watchyourlan.sh | 1 + services/wg-easy.sh | 108 ++++++++++++++++--------- services/wolf-pair.sh | 108 ++++++++++++++++--------- services/wolf.sh | 161 +++++++++++++++++++++++++++++++++++++ setup.sh | 11 ++- 44 files changed, 2482 insertions(+), 1177 deletions(-) diff --git a/lib/common.sh b/lib/common.sh index 45d1f40..cbf08f8 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -56,6 +56,7 @@ SITE_DOMAIN="" SITE_CADDY_NET="caddy_net" SITE_PUID="" SITE_PGID="" +CADDY_REMOTE_HOST="" # LAN IP/hostname of this machine, used when Caddy runs elsewhere load_site_config() { local cfg="$DOCKER_DIR/.config" @@ -67,13 +68,14 @@ load_site_config() { case "$key" in SITE_TZ) SITE_TZ="$val" ;; SITE_DOMAIN) SITE_DOMAIN="$val" ;; - SITE_CADDY_NET) SITE_CADDY_NET="$val" ;; - SITE_PUID) SITE_PUID="$val" ;; - SITE_PGID) SITE_PGID="$val" ;; - BASE_DOMAIN) [ -z "$SITE_DOMAIN" ] && SITE_DOMAIN="$val" ;; + SITE_CADDY_NET) SITE_CADDY_NET="$val" ;; + SITE_PUID) SITE_PUID="$val" ;; + SITE_PGID) SITE_PGID="$val" ;; + CADDY_REMOTE_HOST) CADDY_REMOTE_HOST="$val" ;; + BASE_DOMAIN) [ -z "$SITE_DOMAIN" ] && SITE_DOMAIN="$val" ;; esac done < "$cfg" - export SITE_TZ SITE_DOMAIN SITE_CADDY_NET SITE_PUID SITE_PGID + export SITE_TZ SITE_DOMAIN SITE_CADDY_NET SITE_PUID SITE_PGID CADDY_REMOTE_HOST } save_site_config() { @@ -85,10 +87,11 @@ save_site_config() { [ -n "$SITE_TZ" ] && echo "SITE_TZ=$SITE_TZ" [ -n "$SITE_DOMAIN" ] && echo "SITE_DOMAIN=$SITE_DOMAIN" [ -n "$SITE_CADDY_NET" ] && echo "SITE_CADDY_NET=$SITE_CADDY_NET" - [ -n "$SITE_PUID" ] && echo "SITE_PUID=$SITE_PUID" - [ -n "$SITE_PGID" ] && echo "SITE_PGID=$SITE_PGID" + [ -n "$SITE_PUID" ] && echo "SITE_PUID=$SITE_PUID" + [ -n "$SITE_PGID" ] && echo "SITE_PGID=$SITE_PGID" + [ -n "$CADDY_REMOTE_HOST" ] && echo "CADDY_REMOTE_HOST=$CADDY_REMOTE_HOST" # Backward-compat alias for services that still read BASE_DOMAIN directly - [ -n "$SITE_DOMAIN" ] && echo "BASE_DOMAIN=$SITE_DOMAIN" + [ -n "$SITE_DOMAIN" ] && echo "BASE_DOMAIN=$SITE_DOMAIN" } > "$cfg" chmod 600 "$cfg" } @@ -276,15 +279,27 @@ configure_caddy_for_service() { *) _UPSTREAM="localhost:$SERVICE_UPSTREAM"; _DISPLAY_PORT="$SERVICE_UPSTREAM" ;; esac - # Caddy not installed → nothing to do - [ -d "$DOCKER_DIR/caddy" ] || return 0 + # ── Determine Caddy mode ────────────────────────────────────────────────── + # local: Caddy container running on this machine → write Caddyfile + reload + # remote: Caddy on another machine → generate snippet file to copy over + # none: no Caddy anywhere → silent return + local _CADDY_MODE="none" + [ -d "$DOCKER_DIR/caddy" ] && _CADDY_MODE="local" + [ -n "$CADDY_REMOTE_HOST" ] && [ "$_CADDY_MODE" != "local" ] && _CADDY_MODE="remote" + [ "$_CADDY_MODE" = "none" ] && return 0 echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " CADDY REVERSE PROXY CONFIGURATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" - echo "Caddy is installed. You can configure a reverse proxy for $SERVICE_NAME." + if [ "$_CADDY_MODE" = "remote" ]; then + echo " Remote Caddy configured ($CADDY_REMOTE_HOST)." + echo " A snippet file will be saved to ~/docker/caddy-snippets/ for you to" + echo " copy to your Caddy machine." + else + echo "Caddy is installed. You can configure a reverse proxy for $SERVICE_NAME." + fi echo "" local CONFIGURE_CADDY="" @@ -295,43 +310,35 @@ configure_caddy_for_service() { return 0 fi + # Domain prompt — pre-fill from SITE_DOMAIN when available echo "" - echo "Enter the full domain for $SERVICE_NAME:" - echo " Examples: $DEFAULT_SUBDOMAIN.example.com, $DEFAULT_SUBDOMAIN.yourdomain.com" + local _default_domain="" + if [ -n "$SITE_DOMAIN" ] && [ "$SITE_DOMAIN" != "example.com" ]; then + _default_domain="${DEFAULT_SUBDOMAIN}.${SITE_DOMAIN}" + echo " Default: $_default_domain" + else + echo " Examples: ${DEFAULT_SUBDOMAIN}.example.com, ${DEFAULT_SUBDOMAIN}.yourdomain.com" + fi echo "" local SERVICE_DOMAIN="" - prompt_text "Domain:" "" SERVICE_DOMAIN + prompt_text "Domain [${_default_domain:-required}]:" "$_default_domain" SERVICE_DOMAIN if [ -z "$SERVICE_DOMAIN" ]; then echo " ⚠ No domain provided, skipping Caddy configuration."; return 0 fi - local CADDY_DIR="$DOCKER_DIR/caddy" - local CADDYFILE="$CADDY_DIR/Caddyfile" - local BACKUP_FILE="$CADDY_DIR/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - - if [ -f "$CADDYFILE" ]; then - echo " Backing up Caddyfile to: $(basename "$BACKUP_FILE")" - cp "$CADDYFILE" "$BACKUP_FILE" - else - echo " Creating new Caddyfile"; touch "$CADDYFILE" + # Build the site block — upstream differs by mode + local _BLOCK_UPSTREAM="$_UPSTREAM" + if [ "$_CADDY_MODE" = "remote" ]; then + # Remote Caddy can't resolve Docker container names — use host IP + published port + _BLOCK_UPSTREAM="${CADDY_REMOTE_HOST}:${_DISPLAY_PORT}" fi - if grep -q "^${SERVICE_DOMAIN}" "$CADDYFILE" 2>/dev/null; then - echo " ⚠ $SERVICE_DOMAIN already exists in Caddyfile" - local OVERWRITE="" - prompt_yn "Overwrite existing configuration? (y/n):" "n" OVERWRITE - if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then - echo " Keeping existing configuration."; return 0 - fi - sed -i "/^${SERVICE_DOMAIN}/,/^}/d" "$CADDYFILE" - fi - - echo " Adding $SERVICE_NAME configuration to Caddyfile..." - cat >> "$CADDYFILE" << CADDY_BLOCK + local _SITE_BLOCK + _SITE_BLOCK="$(cat << CADDY_BLOCK # $SERVICE_NAME -$SERVICE_DOMAIN { - reverse_proxy $_UPSTREAM +${SERVICE_DOMAIN} { + reverse_proxy ${_BLOCK_UPSTREAM} # Security headers header { @@ -346,18 +353,65 @@ $SERVICE_DOMAIN { output file /var/log/caddy/${SERVICE_DOMAIN}.log format json } -$EXTRA_CONFIG +${EXTRA_CONFIG} } CADDY_BLOCK +)" - echo " ✓ Configuration added to Caddyfile" - echo " Reloading Caddy configuration..." - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - echo " ✓ $SERVICE_NAME is now accessible at: https://$SERVICE_DOMAIN" + # ── Local Caddy: write to Caddyfile and reload ──────────────────────────── + if [ "$_CADDY_MODE" = "local" ]; then + local CADDY_DIR="$DOCKER_DIR/caddy" + local CADDYFILE="$CADDY_DIR/Caddyfile" + local BACKUP_FILE="$CADDY_DIR/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + + if [ -f "$CADDYFILE" ]; then + echo " Backing up Caddyfile to: $(basename "$BACKUP_FILE")" + cp "$CADDYFILE" "$BACKUP_FILE" + else + echo " Creating new Caddyfile"; touch "$CADDYFILE" + fi + + if grep -q "^${SERVICE_DOMAIN}" "$CADDYFILE" 2>/dev/null; then + echo " ⚠ $SERVICE_DOMAIN already exists in Caddyfile" + local OVERWRITE="" + prompt_yn "Overwrite existing configuration? (y/n):" "n" OVERWRITE + if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then + echo " Keeping existing configuration."; return 0 + fi + sed -i "/^${SERVICE_DOMAIN}/,/^}/d" "$CADDYFILE" + fi + + echo " Adding $SERVICE_NAME configuration to Caddyfile..." + printf '%s\n' "$_SITE_BLOCK" >> "$CADDYFILE" + + echo " ✓ Configuration added to Caddyfile" + echo " Reloading Caddy configuration..." + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + echo " ✓ $SERVICE_NAME is now accessible at: https://$SERVICE_DOMAIN" + else + echo " ⚠ Failed to reload Caddy. Check: docker logs caddy" + echo " You can restore from backup: $BACKUP_FILE" + fi + + # ── Remote Caddy: write snippet file ───────────────────────────────────── else - echo " ⚠ Failed to reload Caddy. Check: docker logs caddy" - echo " You can restore from backup: $BACKUP_FILE" + local SNIPPET_DIR="$DOCKER_DIR/caddy-snippets" + local SNIPPET_FILE="$SNIPPET_DIR/${DEFAULT_SUBDOMAIN}.caddy" + mkdir -p "$SNIPPET_DIR" + printf '%s\n' "$_SITE_BLOCK" > "$SNIPPET_FILE" + chown "$ACTUAL_USER:$ACTUAL_USER" "$SNIPPET_FILE" 2>/dev/null || true + + echo " ✓ Snippet saved: $SNIPPET_FILE" + echo "" + echo " Copy to your Caddy machine and append to its Caddyfile:" + echo " scp $SNIPPET_FILE caddy-host:~/caddy-snippets/" + echo " # then on the Caddy machine:" + echo " cat ~/caddy-snippets/${DEFAULT_SUBDOMAIN}.caddy >> /path/to/Caddyfile" + echo " docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + echo "" + echo " Or rsync all snippets at once:" + echo " rsync -av $SNIPPET_DIR/ caddy-host:~/caddy-snippets/" fi echo "" } diff --git a/services/actualbudget.sh b/services/actualbudget.sh index e839a6f..c72b718 100644 --- a/services/actualbudget.sh +++ b/services/actualbudget.sh @@ -59,45 +59,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -113,17 +120,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/arm.sh b/services/arm.sh index c2b68e1..b1eff1a 100644 --- a/services/arm.sh +++ b/services/arm.sh @@ -67,47 +67,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -123,17 +128,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/audiobookshelf.sh b/services/audiobookshelf.sh index 5971084..f1833df 100644 --- a/services/audiobookshelf.sh +++ b/services/audiobookshelf.sh @@ -59,45 +59,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -113,17 +120,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/authelia.sh b/services/authelia.sh index dddb59a..39625c2 100644 --- a/services/authelia.sh +++ b/services/authelia.sh @@ -65,47 +65,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -121,17 +126,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/backup.sh b/services/backup.sh index 66fae1c..23f5a84 100644 --- a/services/backup.sh +++ b/services/backup.sh @@ -76,47 +76,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -132,17 +137,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/borg-backup.sh b/services/borg-backup.sh index d2d1efb..912c8a3 100644 --- a/services/borg-backup.sh +++ b/services/borg-backup.sh @@ -76,47 +76,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -132,17 +137,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/caddy.sh b/services/caddy.sh index 9f1e348..0ef9e46 100644 --- a/services/caddy.sh +++ b/services/caddy.sh @@ -71,47 +71,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -127,17 +132,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/crowdsec.sh b/services/crowdsec.sh index e35a8d2..34d5f72 100644 --- a/services/crowdsec.sh +++ b/services/crowdsec.sh @@ -73,6 +73,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/ddclient.sh b/services/ddclient.sh index 0057e7d..d86d490 100644 --- a/services/ddclient.sh +++ b/services/ddclient.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/emby.sh b/services/emby.sh index fbce917..3ed4303 100644 --- a/services/emby.sh +++ b/services/emby.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/filebrowser.sh b/services/filebrowser.sh index 6494c12..1ad1e28 100644 --- a/services/filebrowser.sh +++ b/services/filebrowser.sh @@ -56,45 +56,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -110,17 +117,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/fmd.sh b/services/fmd.sh index 8787d23..5e5bbe7 100644 --- a/services/fmd.sh +++ b/services/fmd.sh @@ -67,47 +67,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -123,17 +128,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/frigate-audio.sh b/services/frigate-audio.sh index 4c1449c..a1bb8e2 100644 --- a/services/frigate-audio.sh +++ b/services/frigate-audio.sh @@ -86,47 +86,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -142,17 +147,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/frigate-notify.sh b/services/frigate-notify.sh index 81c75c0..072e7b5 100644 --- a/services/frigate-notify.sh +++ b/services/frigate-notify.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/frigate.sh b/services/frigate.sh index e5da4a0..9c2aaaf 100644 --- a/services/frigate.sh +++ b/services/frigate.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/gaming-backup.sh b/services/gaming-backup.sh index 6db2b1b..07ba869 100644 --- a/services/gaming-backup.sh +++ b/services/gaming-backup.sh @@ -78,6 +78,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/gatus.sh b/services/gatus.sh index 3c10186..92b0fc3 100644 --- a/services/gatus.sh +++ b/services/gatus.sh @@ -66,47 +66,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -122,17 +127,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/homeassistant.sh b/services/homeassistant.sh index 7087d15..92a08f9 100644 --- a/services/homeassistant.sh +++ b/services/homeassistant.sh @@ -63,47 +63,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -119,18 +124,49 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } fi + # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR # ($HOME under sudo is /root, not the real user's home) ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}" diff --git a/services/immich.sh b/services/immich.sh index 2145708..de84bb1 100644 --- a/services/immich.sh +++ b/services/immich.sh @@ -62,45 +62,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -116,17 +123,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/jellyfin.sh b/services/jellyfin.sh index a1b07fa..2be8386 100644 --- a/services/jellyfin.sh +++ b/services/jellyfin.sh @@ -61,45 +61,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -115,17 +122,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/js99er.sh b/services/js99er.sh index 5377027..1f90845 100644 --- a/services/js99er.sh +++ b/services/js99er.sh @@ -67,47 +67,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -123,14 +128,44 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } fi diff --git a/services/lyrion.sh b/services/lyrion.sh index 466573f..bd4396f 100644 --- a/services/lyrion.sh +++ b/services/lyrion.sh @@ -67,47 +67,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -123,17 +128,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/magicmirror.sh b/services/magicmirror.sh index ab78ba2..8614b76 100644 --- a/services/magicmirror.sh +++ b/services/magicmirror.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/mail-archiver.sh b/services/mail-archiver.sh index 2f4701b..8e1b4ea 100644 --- a/services/mail-archiver.sh +++ b/services/mail-archiver.sh @@ -64,47 +64,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -120,17 +125,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/mealie.sh b/services/mealie.sh index 05a6a27..ef025f4 100644 --- a/services/mealie.sh +++ b/services/mealie.sh @@ -59,45 +59,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -113,17 +120,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/meshcentral.sh b/services/meshcentral.sh index 42ceff1..8abd2de 100644 --- a/services/meshcentral.sh +++ b/services/meshcentral.sh @@ -67,47 +67,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -123,17 +128,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/minecraft.sh b/services/minecraft.sh index a855b56..0cc4ea1 100644 --- a/services/minecraft.sh +++ b/services/minecraft.sh @@ -83,6 +83,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/ntfy.sh b/services/ntfy.sh index 8ab453f..3c38038 100644 --- a/services/ntfy.sh +++ b/services/ntfy.sh @@ -63,47 +63,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -119,17 +124,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/portainer.sh b/services/portainer.sh index 71f51b2..3c2082a 100644 --- a/services/portainer.sh +++ b/services/portainer.sh @@ -56,45 +56,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -110,17 +117,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/rustdesk.sh b/services/rustdesk.sh index 6de59d2..c2060cb 100644 --- a/services/rustdesk.sh +++ b/services/rustdesk.sh @@ -88,6 +88,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/silent-send.sh b/services/silent-send.sh index b6076a7..fb127a1 100644 --- a/services/silent-send.sh +++ b/services/silent-send.sh @@ -72,6 +72,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/sky-cam.sh b/services/sky-cam.sh index 2ad98e7..1cbd87a 100644 --- a/services/sky-cam.sh +++ b/services/sky-cam.sh @@ -72,6 +72,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/sync-cc.sh b/services/sync-cc.sh index df41e06..2e03451 100644 --- a/services/sync-cc.sh +++ b/services/sync-cc.sh @@ -71,6 +71,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" HERE="${HERE:-$_SELF_DIR/..}" register_service() { :; } # no-op — no wizard to register into diff --git a/services/traccar.sh b/services/traccar.sh index 856ba42..c560c49 100644 --- a/services/traccar.sh +++ b/services/traccar.sh @@ -59,47 +59,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -115,17 +120,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/unifi.sh b/services/unifi.sh index a44315f..fe5d45d 100644 --- a/services/unifi.sh +++ b/services/unifi.sh @@ -86,6 +86,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/uptimekuma.sh b/services/uptimekuma.sh index b7324c3..32911e8 100644 --- a/services/uptimekuma.sh +++ b/services/uptimekuma.sh @@ -56,47 +56,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -112,17 +117,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/vaultwarden.sh b/services/vaultwarden.sh index 7aa8fad..e160134 100644 --- a/services/vaultwarden.sh +++ b/services/vaultwarden.sh @@ -73,47 +73,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -129,17 +134,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/watchtower.sh b/services/watchtower.sh index 74694ec..1d05908 100644 --- a/services/watchtower.sh +++ b/services/watchtower.sh @@ -76,6 +76,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/watchyourlan.sh b/services/watchyourlan.sh index 38753fd..25b2d1c 100644 --- a/services/watchyourlan.sh +++ b/services/watchyourlan.sh @@ -80,6 +80,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 diff --git a/services/wg-easy.sh b/services/wg-easy.sh index 22ba149..9915801 100644 --- a/services/wg-easy.sh +++ b/services/wg-easy.sh @@ -68,47 +68,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -124,17 +129,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/wolf-pair.sh b/services/wolf-pair.sh index e1acf02..818fe38 100644 --- a/services/wolf-pair.sh +++ b/services/wolf-pair.sh @@ -63,47 +63,52 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" local _caddy_dir="$DOCKER_DIR/caddy" local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" - if [[ ! -d "$_caddy_dir" ]]; then - log_info "Access $_name directly on port ${_upstream##*:}." - return 0 - fi - - echo "" - local _do_caddy="" - read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy - [[ "${_do_caddy,,}" == "y" ]] || { - log_info "Skipping — access at: http://localhost:${_upstream##*:}" + # Determine mode: local Caddy, remote Caddy, or none + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." return 0 } + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + # Domain prompt — pre-fill from SITE_DOMAIN when available + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi local _domain="" - read -r -p " Domain (e.g. ${_subdomain}.${SITE_DOMAIN:-example.com}): " _domain + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } - # Back up before touching - if [[ -f "$_caddyfile" ]]; then - local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" - cp "$_caddyfile" "$_bk" - log_info "Backed up Caddyfile to $(basename "$_bk")" - else - touch "$_caddyfile" + # Build upstream — remote Caddy uses host IP:port, not container name + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" fi - # Remove existing block for this domain if present - if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then - log_warning "$_domain already in Caddyfile" - local _ow="" - read -r -p " Overwrite? [y/N]: " _ow - [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } - sed -i "/^${_domain}/,/^}/d" "$_caddyfile" - fi - - cat >> "$_caddyfile" << CBLOCK + local _site_block + _site_block="$(cat << CBLOCK # $_name -$_domain { - reverse_proxy $_upstream +${_domain} { + reverse_proxy ${_block_upstream} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" @@ -119,17 +124,46 @@ $_domain { ${_extra} } CBLOCK +)" - log_success "Added $_domain to Caddyfile" - docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true - if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then - log_success "$_name accessible at: https://$_domain" + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi else - log_warning "Reload failed — check: docker logs caddy" - log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" fi } - write_readme() { local _dir="$1"; shift mkdir -p "$_dir" diff --git a/services/wolf.sh b/services/wolf.sh index 0573129..868675c 100644 --- a/services/wolf.sh +++ b/services/wolf.sh @@ -54,6 +54,10 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then chown -R "$ACTUAL_USER:$ACTUAL_USER" "$@" 2>/dev/null || true } + ensure_docker_dir_ownership() { + chown -R "$ACTUAL_USER:$ACTUAL_USER" "$@" 2>/dev/null || true + } + # Match common.sh's eval-based pattern so local vars in install_* are set correctly prompt_text() { local _q="$1" _def="$2" _var="$3" _r @@ -61,6 +65,120 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then read -r -p " $_q " _r eval "$_var='${_r:-$_def}'" } + + prompt_yn() { + local _q="$1" _def="$2" _var="$3" _r + [[ "${UNATTENDED:-false}" == "true" ]] && { eval "$_var='$_def'"; return; } + read -r -p " $_q " _r + eval "$_var='${_r:-$_def}'" + } + + configure_caddy_for_service() { + local _name="$1" _upstream="$2" _subdomain="$3" _extra="${4:-}" + local _caddy_dir="$DOCKER_DIR/caddy" + local _caddyfile="$_caddy_dir/Caddyfile" + local _display_port="${_upstream##*:}" + + local _mode="none" + [[ -d "$_caddy_dir" ]] && _mode="local" + [[ -n "${CADDY_REMOTE_HOST:-}" ]] && [[ "$_mode" != "local" ]] && _mode="remote" + [[ "$_mode" == "none" ]] && { + log_info "Access $_name directly on port $_display_port." + return 0 + } + + echo "" + local _do_caddy="" + if [[ "$_mode" == "remote" ]]; then + log_info "Remote Caddy configured (${CADDY_REMOTE_HOST})." + log_info "A snippet file will be saved to ~/docker/caddy-snippets/." + fi + read -r -p " Configure Caddy reverse proxy for $_name? [y/N]: " _do_caddy + [[ "${_do_caddy,,}" == "y" ]] || { + log_info "Skipping — access at: http://localhost:$_display_port" + return 0 + } + + local _default_domain="" + if [[ -n "${SITE_DOMAIN:-}" ]] && [[ "$SITE_DOMAIN" != "example.com" ]]; then + _default_domain="${_subdomain}.${SITE_DOMAIN}" + log_info "Default: $_default_domain" + fi + local _domain="" + read -r -p " Domain [${_default_domain:-required}]: " _domain + _domain="${_domain:-$_default_domain}" + [[ -n "$_domain" ]] || { log_warning "No domain entered — skipping Caddy."; return 0; } + + local _block_upstream="$_upstream" + if [[ "$_mode" == "remote" ]]; then + _block_upstream="${CADDY_REMOTE_HOST}:${_display_port}" + fi + + local _site_block + _site_block="$(cat << CBLOCK + +# $_name +${_domain} { + reverse_proxy ${_block_upstream} + + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-Content-Type-Options "nosniff" + X-Frame-Options "SAMEORIGIN" + Referrer-Policy "strict-origin-when-cross-origin" + } + + log { + output file /var/log/caddy/${_domain}.log + format json + } +${_extra} +} +CBLOCK +)" + + if [[ "$_mode" == "local" ]]; then + if [[ -f "$_caddyfile" ]]; then + local _bk="$_caddy_dir/Caddyfile.backup.$(date +%Y%m%d-%H%M%S)" + cp "$_caddyfile" "$_bk" + log_info "Backed up Caddyfile to $(basename "$_bk")" + else + touch "$_caddyfile" + fi + if grep -q "^${_domain}" "$_caddyfile" 2>/dev/null; then + log_warning "$_domain already in Caddyfile" + local _ow="" + read -r -p " Overwrite? [y/N]: " _ow + [[ "${_ow,,}" == "y" ]] || { log_info "Keeping existing entry."; return 0; } + sed -i "/^${_domain}/,/^}/d" "$_caddyfile" + fi + printf '%s\n' "$_site_block" >> "$_caddyfile" + log_success "Added $_domain to Caddyfile" + docker exec caddy caddy fmt --overwrite /etc/caddy/Caddyfile 2>/dev/null || true + if docker exec caddy caddy reload --config /etc/caddy/Caddyfile 2>/dev/null; then + log_success "$_name accessible at: https://$_domain" + else + log_warning "Reload failed — check: docker logs caddy" + log_info "Manual reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile" + fi + else + local _snippet_dir="$DOCKER_DIR/caddy-snippets" + local _snippet_file="$_snippet_dir/${_subdomain}.caddy" + mkdir -p "$_snippet_dir" + printf '%s\n' "$_site_block" > "$_snippet_file" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_snippet_file" 2>/dev/null || true + log_success "Snippet saved: $_snippet_file" + log_info "Copy to Caddy machine:" + log_info " scp $_snippet_file caddy-host:~/caddy-snippets/" + log_info " rsync -av $_snippet_dir/ caddy-host:~/caddy-snippets/ (all at once)" + fi + } + + write_readme() { + local _dir="$1"; shift + mkdir -p "$_dir" + cat > "$_dir/README.md" + } fi # Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR @@ -73,6 +191,7 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}" SITE_DOMAIN="${SITE_DOMAIN:-example.com}" SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}" + CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}" register_service() { :; } # no-op — no wizard to register into _RUN_STANDALONE=1 @@ -927,6 +1046,48 @@ PYEOF echo " Set up automatic backups with the backup module:" echo " sudo ./setup.sh backup" echo "" + # ── Caddy reverse proxy (Wolf web UI — no built-in auth, offer Authelia) ──── + local WOLF_EXTRA_BLOCK="" + if [ -d "$DOCKER_DIR/authelia" ]; then + local _use_auth="" + prompt_yn "Protect Wolf web UI with Authelia SSO? (y/n):" "y" _use_auth + [[ "$_use_auth" =~ ^[Yy]$ ]] && WOLF_EXTRA_BLOCK=" import authelia" + fi + configure_caddy_for_service "Wolf" "wolf:47990" "wolf" "$WOLF_EXTRA_BLOCK" + + write_readme "$WOLF_DIR" << MD +# Wolf — Cloud Gaming + +Cloud gaming via Moonlight. Stream any Moonlight-compatible game or app from +this server to any device on your network. + +## Manage +\`\`\`bash +cd $WOLF_DIR +./manage.sh start # start Wolf +./manage.sh stop # stop Wolf +./manage.sh logs # stream logs +./manage.sh pin # show pairing PIN +./manage.sh update # pull latest and restart +\`\`\` + +## Pairing +1. Open Moonlight on your device, select this server +2. Run \`./manage.sh pin\` to get the pairing PIN +3. Enter the PIN in Moonlight + +## Game storage: \`$GAME_STORAGE_DIR\` +- \`roms/\` → /ROMs (EmulationStation) +- \`steam/\` → Steam data +- \`saves/\` → RetroArch saves +MD + + local START_WOLF="" + prompt_yn "Start Wolf now? (y/n):" "y" START_WOLF + if [ "$START_WOLF" = "y" ] || [ "$START_WOLF" = "Y" ]; then + docker compose up -d && log_success "Wolf started" || log_warning "Failed to start — check: docker compose logs" + fi + log_success "Done. Pair Moonlight and play." } diff --git a/setup.sh b/setup.sh index 27b8a7b..dae66da 100755 --- a/setup.sh +++ b/setup.sh @@ -121,10 +121,19 @@ run_site_configure() { local _cur_tz="${SITE_TZ:-$_sys_tz}" local _cur_dom="${SITE_DOMAIN:-}" local _cur_net="${SITE_CADDY_NET:-caddy_net}" + local _cur_caddy_host="${CADDY_REMOTE_HOST:-}" + prompt_text " Timezone [${_cur_tz}]:" "$_cur_tz" SITE_TZ prompt_text " Base domain (e.g., example.com) [${_cur_dom:-}]:" "$_cur_dom" SITE_DOMAIN prompt_text " Caddy Docker network [${_cur_net}]:" "$_cur_net" SITE_CADDY_NET - export SITE_TZ SITE_DOMAIN SITE_CADDY_NET + + echo "" + echo " Caddy location: leave blank if Caddy runs on THIS machine (default)." + echo " Set to this machine's LAN IP or hostname if Caddy runs on a DIFFERENT" + echo " machine — service installers will generate snippet files to copy over." + prompt_text " Caddy remote host (LAN IP/hostname) [${_cur_caddy_host:-}]:" "$_cur_caddy_host" CADDY_REMOTE_HOST + + export SITE_TZ SITE_DOMAIN SITE_CADDY_NET CADDY_REMOTE_HOST mkdir -p "$DOCKER_DIR" save_site_config log_success "Saved to $DOCKER_DIR/.config"