#!/bin/bash
# h42 vhost – nginx-vhost anlegen: Reverse-Proxy auf 127.0.0.1:<port> oder statische Dateien aus <verzeichnis>.
#   h42 vhost add <fqdn> <port|/verzeichnis> [zugriff]   zugriff:
#       lan+auth  (Default) LAN/VPN direkt, Internet nur mit Basic-Auth
#       lan       nur LAN/VPN, Internet gesperrt
#       auth      immer Basic-Auth
#       public    offen (App hat eigenen Login)
#   h42 vhost del <fqdn> [--purge]           deaktivieren (--purge: auch vhost-Datei und Basic-Auth-Datei löschen)
#   h42 vhost exists <fqdn> [--purge]        Exit 0: gäbe für del etwas zu tun (aktiv; mit --purge auch: Dateien
#                                            liegen noch da), 3: nichts vorhanden
#   h42 vhost passwd <fqdn> <user>          Basic-Auth-Benutzer anlegen/Passwort ändern
#   h42 vhost check <fqdn> <port|/verz.>    Port bzw. Verzeichnis prüfen (macht add auch selbst)
# Statt eines Ports ein absolutes Verzeichnis: nginx liefert die Dateien darin aus (mit Verzeichnisliste),
# Vorlage /etc/h42/vhost-static.conf.tmpl – z. B. h42 new pkg /srv/pkg public.
# Port-Prüfung: Fehler bei ungültigem Port, nginx-eigenem Port oder wenn ein anderer aktiver vhost
# schon auf den Port zeigt (FORCE=1 erlaubt das trotzdem); Warnung, wenn nichts oder nicht nur
# auf 127.0.0.1 lauscht.
# LAN-Netze: /etc/nginx/snippets/h42-lan.conf, TLS: /etc/nginx/snippets/h42-tls.conf
set -euo pipefail
. "${H42_LIB:-/usr/lib/h42}/common.sh"
need_root

fqdn=${2:-}; [ -n "$fqdn" ] || usage
name=${fqdn//./_}.conf
avail=/etc/nginx/sites-available/$name
enabled=/etc/nginx/sites-enabled/$name
htpasswd_file=$HTPASSWD_DIR/$fqdn
TEMPLATE_STATIC=${TEMPLATE_STATIC:-/etc/h42/vhost-static.conf.tmpl}

check_target() {  # check_target <port|/verzeichnis>
  case $1 in
    /*) [ -d "$1" ] || { echo "Verzeichnis fehlt: $1" >&2; return 1; }
        [[ $1 != *[[:space:]\;\{\}\'\"]* ]] || { echo "ungültiges Verzeichnis: $1" >&2; return 1; } ;;
    *) check_port "$1" ;;
  esac
}

check_port() {  # check_port <port>
  local port=$1 others listen addrs
  [[ $port =~ ^[1-9][0-9]{0,4}$ ]] && (( port <= 65535 )) || { echo "ungültiger Port: $port" >&2; return 1; }
  others=$(grep -lE "proxy_pass +https?://(127\.0\.0\.1|localhost|\[::1\]):$port([/;]|$)" /etc/nginx/sites-enabled/* 2>/dev/null \
    | grep -vxF "/etc/nginx/sites-enabled/$name" || true)
  if [ -n "$others" ]; then
    echo "Port $port ist schon vergeben an: $(xargs -n1 basename <<<"$others" | tr '\n' ' ')" >&2
    [ "${FORCE:-}" = 1 ] || { echo "(FORCE=1 erzwingt es)" >&2; return 1; }
  fi
  listen=$(ss -Hltnp "sport = :$port" 2>/dev/null || true)
  if [ -z "$listen" ]; then
    echo "Warnung: auf Port $port lauscht nichts – Container gestartet?" >&2
    return 0
  fi
  if grep -q '"nginx"' <<<"$listen"; then echo "Port $port gehört nginx selbst" >&2; return 1; fi
  addrs=$(awk '{print $4}' <<<"$listen")
  if ! grep -qE '^(127\.0\.0\.1|0\.0\.0\.0|\*|\[::\]):' <<<"$addrs"; then
    echo "Warnung: Port $port lauscht nicht auf 127.0.0.1 ($(tr '\n' ' ' <<<"$addrs")) – nginx erreicht den Dienst so nicht" >&2
  elif grep -qE '^(0\.0\.0\.0|\*|\[::\]):' <<<"$addrs"; then
    echo "Warnung: Port $port lauscht auf allen Adressen – an nginx vorbei erreichbar; besser ports: \"127.0.0.1:$port:<containerport>\"" >&2
  fi
}

case $1 in
  add)
    target=${3:?port oder verzeichnis fehlt}; access=${4:-lan+auth}
    check_target "$target"
    [ -e "$DEHYDRATED_CERTS/$fqdn/fullchain.pem" ] || die "kein Zertifikat für $fqdn – erst h42 cert add $fqdn"
    acl=$(h42_acl "$access" "$fqdn" "$htpasswd_file") || usage
    case $target in
      /*) tmpl=$TEMPLATE_STATIC; dest=$target ;;
      *)  tmpl=$TEMPLATE; dest=127.0.0.1:$target ;;
    esac
    new_conf=$(FQDN=$fqdn PORT=$target ROOT=$target ACCESS=$access ACL=$acl perl -pe 's/\{\{(\w+)\}\}/$ENV{$1}/g' "$tmpl")
    changed=1
    if [ -e "$avail" ]; then
      if [ "$(cat "$avail")" != "$new_conf" ]; then
        echo "vhost ist schon vorhanden und wird ersetzt (Sicherung: $avail.bak): $(sed -n '1s/^# *//p' "$avail")"
        cp -a "$avail" "$avail.bak"
        printf '%s\n' "$new_conf" > "$avail"
      elif [ -e "$enabled" ]; then
        echo "vhost ist schon vorhanden, aktiv und unverändert"
        changed=
      else
        echo "vhost-Datei ist schon vorhanden und unverändert, wird wieder aktiviert"
      fi
    else
      printf '%s\n' "$new_conf" > "$avail"
    fi
    ln -sf "$avail" "$enabled"
    if [[ $access == *auth && ! -s $htpasswd_file ]]; then
      echo "Hinweis: noch kein Basic-Auth-Benutzer – h42 vhost passwd $fqdn <user>"
      install -d -m 750 -o root -g www-data "$HTPASSWD_DIR"
      install -m 640 -o root -g www-data /dev/null "$htpasswd_file"
    fi
    if [ -n "$changed" ]; then nginx -t && systemctl reload nginx; fi
    echo "https://$fqdn -> $dest ($access)"
    ;;
  del)
    case ${3:-} in ""|--purge) ;; *) usage ;; esac
    if [ -e "$enabled" ] || [ -L "$enabled" ]; then
      rm -f "$enabled"
      nginx -t && systemctl reload nginx
      echo "$fqdn deaktiviert"
    else
      echo "$fqdn ist nicht aktiv"
    fi
    if [ "${3:-}" = --purge ]; then
      files=()
      for f in "$avail" "$avail.bak" "$htpasswd_file"; do
        if [ -e "$f" ]; then files+=("$f"); fi
      done
      if [ ${#files[@]} -gt 0 ]; then
        rm -fv "${files[@]}"
        echo "$fqdn: vhost-Dateien gelöscht"
      else
        echo "$fqdn: keine vhost-Dateien vorhanden"
      fi
    elif [ -e "$avail" ]; then
      echo "Datei bleibt: $avail"
    else
      echo "keine vhost-Datei vorhanden: $avail"
    fi
    ;;
  exists)
    case ${3:-} in ""|--purge) ;; *) usage ;; esac
    if [ -e "$enabled" ] || [ -L "$enabled" ]; then exit 0; fi
    if [ "${3:-}" = --purge ]; then
      for f in "$avail" "$avail.bak" "$htpasswd_file"; do
        if [ -e "$f" ]; then exit 0; fi
      done
    fi
    exit 3
    ;;
  check)
    check_target "${3:?port oder verzeichnis fehlt}"
    echo "${3} ok für $fqdn"
    ;;
  passwd)
    user=${3:?user fehlt}
    install -d -m 750 -o root -g www-data "$HTPASSWD_DIR"
    [ -e "$htpasswd_file" ] || install -m 640 -o root -g www-data /dev/null "$htpasswd_file"
    htpasswd -B "$htpasswd_file" "$user"
    ;;
  *) usage ;;
esac
