diff --git a/lib/common.sh b/lib/common.sh index c2d1fe8..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,14 +310,14 @@ 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:" local _default_domain="" if [ -n "$SITE_DOMAIN" ] && [ "$SITE_DOMAIN" != "example.com" ]; then - _default_domain="$DEFAULT_SUBDOMAIN.$SITE_DOMAIN" + _default_domain="${DEFAULT_SUBDOMAIN}.${SITE_DOMAIN}" echo " Default: $_default_domain" else - echo " Examples: $DEFAULT_SUBDOMAIN.example.com, $DEFAULT_SUBDOMAIN.yourdomain.com" + echo " Examples: ${DEFAULT_SUBDOMAIN}.example.com, ${DEFAULT_SUBDOMAIN}.yourdomain.com" fi echo "" local SERVICE_DOMAIN="" @@ -311,33 +326,19 @@ configure_caddy_for_service() { 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 { @@ -352,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 56317c3..9a3bb77 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 3ee645b..3f380f1 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 6e1bd14..ac82200 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 b8fa9b2..c54fc1d 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 0c1d033..2ab2f1f 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 9a959f5..5a0f260 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 682218a..6a8d9e5 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 9ae48d3..177b7f8 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 f549472..19e3373 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 aee0d4d..a10c2dd 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 @@ -968,6 +1087,11 @@ cd $WOLF_DIR | 48010 | TCP | RTSP | | 47998–48000 | UDP | RTP video/audio/control | +## Game storage: \`$GAME_STORAGE_DIR\` +- \`roms/\` → /ROMs (EmulationStation) +- \`steam/\` → Steam data +- \`saves/\` → RetroArch saves + ## Backup \`\`\`bash sudo ./setup.sh backup # covers /etc/wolf saves and ES-DE settings diff --git a/setup.sh b/setup.sh index 15e2d98..2fa7162 100755 --- a/setup.sh +++ b/setup.sh @@ -122,10 +122,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"