#!/usr/bin/env bash
# BrowserDesk Relay — native Linux-Installation ohne Docker (KB-0017).
#
# Richtet auf einem frischen Server alles ein, was der Relay braucht:
#   1. PostgreSQL aus den Distributions-Paketen (+ eigene Datenbank/Rolle mit Zufallspasswort)
#   2. das aktuelle Relay-Release aus updates.browserdesk.de (self-contained, kein .NET noetig)
#      nach /opt/browserdesk-relay, SHA-256-geprueft
#   3. ein selbstsigniertes TLS-Zertifikat (openssl) — fuer Produktion spaeter durch ein echtes ersetzen
#   4. eine systemd-Einheit "browserdesk-relay" mit Autostart und Wiederanlauf
#
# Getestet fuer: Ubuntu 22.04 / 24.04 LTS und Debian 12 (x86_64). Andere Distributionen mit
# systemd + apt laufen meist auch, sind aber nicht geprueft — das Skript warnt dann nur.
#
# Aufruf (als root):
#   bash install-browserdesk-relay-linux.sh                # neueste Version
#   bash install-browserdesk-relay-linux.sh 20260809.1     # feste Version
#   (per Pipe: curl -fsSL https://browserdesk.de/kb/install-browserdesk-relay-linux.sh | bash)
#
# Wiederholt aufrufbar: Datenbank, relay.json und Zertifikat bleiben erhalten, nur die
# Programmdateien werden erneuert. Updates spaeter: update-browserdesk-relay-linux.sh (KB-0017).
set -euo pipefail

FEED="${BD_FEED:-https://updates.browserdesk.de/browserdesk-relay}"

# Audit 04.09.2026 (S34): die Index-Datei des Feeds ist mit dem BrowserDesk-Feed-Schluessel signiert (ECDSA
# P-256, SHA-256). Ohne gueltige Signatur wird nichts installiert - ein untergeschobener Feed bleibt folgenlos.
BD_FEED_PUBKEY='-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEr0HItMnzx1CbztVnFFrIz2NJKr9b
hpYmU+VJ5AVCtoUni5zuMnzAu9DbKo8JeEBDQIMdj76zAbPuLz+Rk5QO7Q==
-----END PUBLIC KEY-----'
pruefe_feed_signatur() { # $1 = lokale Datei, $2 = URL der Datei (Signatur liegt unter URL.sig)
  command -v openssl >/dev/null 2>&1 || { echo "FEHLER: openssl fehlt - Feed-Signatur nicht pruefbar. Abbruch."; return 1; }
  local sig pub der
  sig="$(mktemp)"; pub="$(mktemp)"; der="$(mktemp)"
  if ! curl -fsSL --max-time 60 "$2.sig" -o "$sig"; then rm -f "$sig" "$pub" "$der"; echo "FEHLER: Signatur $2.sig fehlt - Abbruch."; return 1; fi
  printf '%s\n' "$BD_FEED_PUBKEY" > "$pub"
  if ! base64 -d "$sig" > "$der" 2>/dev/null; then rm -f "$sig" "$pub" "$der"; echo "FEHLER: Signatur $2.sig unlesbar - Abbruch."; return 1; fi
  if openssl dgst -sha256 -verify "$pub" -signature "$der" "$1" >/dev/null 2>&1; then rm -f "$sig" "$pub" "$der"; return 0; fi
  rm -f "$sig" "$pub" "$der"; echo "FEHLER: Signatur von $2 UNGUELTIG - Abbruch."; return 1
}
WANT="${1:-}"
APP=/opt/browserdesk-relay
DATA=/var/lib/triline/BrowserDesk/Relay     # fester Vorgabepfad des Relays (RelayConfig.SettingsDir)
ENVFILE=/etc/browserdesk-relay.env
UNIT=/etc/systemd/system/browserdesk-relay.service

[ "$(id -u)" = 0 ] || { echo "FEHLER: bitte als root ausfuehren (sudo)."; exit 1; }
[ "$(uname -m)" = x86_64 ] || { echo "FEHLER: der Feed fuehrt nur x86_64-Pakete (gefunden: $(uname -m))."; exit 1; }
command -v systemctl >/dev/null 2>&1 || { echo "FEHLER: systemd nicht gefunden."; exit 1; }
command -v curl >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y -qq curl; }

# --- Distribution pruefen (Warnung, kein Abbruch — der Rest ist Standard-Kost) ------------------
if [ -r /etc/os-release ]; then
  . /etc/os-release
  case "${ID:-}-${VERSION_ID:-}" in
    ubuntu-22.04|ubuntu-24.04|debian-12) echo "==> ${PRETTY_NAME}" ;;
    *) echo "WARNUNG: ${PRETTY_NAME:-unbekannte Distribution} ist nicht getestet (geprueft: Ubuntu 22.04/24.04, Debian 12). Es geht auf eigene Verantwortung weiter." ;;
  esac
fi

# --- 1. PostgreSQL ------------------------------------------------------------------------------
# Distributions-Paket genuegt (Ubuntu 22.04 = PG 14, 24.04 = PG 16, Debian 12 = PG 15).
if ! command -v psql >/dev/null 2>&1; then
  echo "==> PostgreSQL installieren (apt)"
  apt-get update -qq
  DEBIAN_FRONTEND=noninteractive apt-get install -y -qq postgresql
fi
systemctl enable --now postgresql >/dev/null 2>&1 || true
# Warten bis der Cluster antwortet (frische Installation braucht einen Moment).
for i in $(seq 1 30); do
  sudo -u postgres psql -qAtc "select 1" >/dev/null 2>&1 && break
  sleep 1
  [ "$i" = 30 ] && { echo "FEHLER: PostgreSQL antwortet nicht."; exit 1; }
done

# Rolle + Datenbank nur beim ersten Lauf anlegen; das Passwort landet in $ENVFILE (nur root lesbar).
if [ -f "$ENVFILE" ] && grep -q '^BROWSERDESK_DB=' "$ENVFILE"; then
  echo "==> Datenbank-Zugang vorhanden ($ENVFILE) — bleibt unangetastet."
else
  PGPW="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 24)"
  echo "==> Datenbank 'browserdesk' + Rolle anlegen"
  sudo -u postgres psql -qAtc "select 1 from pg_roles where rolname='browserdesk'" | grep -q 1 \
    || printf "CREATE ROLE browserdesk LOGIN PASSWORD '%s';\n" "$PGPW" | sudo -u postgres psql -q -v ON_ERROR_STOP=1 -f -
  # Audit 04.09.2026 (S28): Passwort ueber stdin, nicht als Prozessargument (fuer jeden lokalen Benutzer lesbar).
  printf "ALTER ROLE browserdesk LOGIN PASSWORD '%s';\n" "$PGPW" | sudo -u postgres psql -q -v ON_ERROR_STOP=1 -f -
  sudo -u postgres psql -qAtc "select 1 from pg_database where datname='browserdesk'" | grep -q 1 \
    || sudo -u postgres createdb -O browserdesk browserdesk
  umask 077
  cat > "$ENVFILE" <<EOF
# BrowserDesk Relay — Umgebung (nur root lesbar; enthaelt das Datenbank-Passwort).
BROWSERDESK_DB=Host=127.0.0.1;Port=5432;Database=browserdesk;Username=browserdesk;Password=$PGPW
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
EOF
  umask 022
fi

# --- 2. Relay aus dem Feed ----------------------------------------------------------------------
echo "==> Feed lesen: $FEED/updates.ini"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
curl -fsSL "$FEED/updates.ini" -o "$TMP/updates.ini"
pruefe_feed_signatur "$TMP/updates.ini" "$FEED/updates.ini" || exit 1
VER="$(sed -n 's/^version=//p' "$TMP/updates.ini" | tr -d '\r')"
SHA="$(sed -n 's/^sha256=//p' "$TMP/updates.ini" | tr -d '\r')"
URL="$(sed -n 's/^url=//p' "$TMP/updates.ini" | tr -d '\r' | sed 's#^http://#https://#')"
if [ -n "$WANT" ] && [ "$WANT" != "$VER" ]; then
  # Feste aeltere Version: URL deterministisch bilden; die SHA aus updates.ini gilt nur fuer die
  # neueste — bei einer anderen entfaellt die Pruefung (der Feed ist HTTPS).
  # Audit 04.09.2026 (S38): ohne Pruefsumme wird NICHTS installiert. Fuer eine gepinnte Version die SHA-256
  # des Zips aus RELEASE.md/SHA256SUMS.txt der Auslieferung mitgeben: BD_SHA256=<hex> bash install-... <version>
  VER="$WANT"; URL="$FEED/linux-x64/BrowserDesk.Relay-$VER.zip"; SHA="${BD_SHA256:-}"
  [ -n "$SHA" ] || { echo "FEHLER: Fuer die feste Version $VER fehlt die Pruefsumme. BD_SHA256=<sha256 des Zips> setzen (steht in SHA256SUMS.txt der Auslieferung)."; exit 1; }
fi
[ -n "$VER" ] || { echo "FEHLER: Feed unlesbar (version fehlt)."; exit 1; }
echo "==> Version: $VER"
curl -fL --progress-bar "$URL" -o "$TMP/relay.zip"
if [ -n "$SHA" ]; then
  echo "$SHA  $TMP/relay.zip" | sha256sum -c - >/dev/null || { echo "FEHLER: Pruefsumme stimmt NICHT — Abbruch."; exit 1; }
fi
command -v unzip >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y -qq unzip; }

# Laufenden Dienst anhalten, Bestand sichern, neue Dateien einspielen.
systemctl stop browserdesk-relay 2>/dev/null || true
if [ -d "$APP" ]; then
  rm -rf "$APP.vorher"; cp -a "$APP" "$APP.vorher"   # Ruecksprungpunkt (der Feed kennt kein Rollback)
fi
mkdir -p "$APP"
unzip -o -q "$TMP/relay.zip" -d "$APP"
chmod +x "$APP/BrowserDesk.Relay"
rm -f "$APP"/Install-BrowserDeskRelay.ps1 2>/dev/null || true   # Windows-Beigabe, hier ohne Nutzen

# --- 3. Konfiguration + TLS ---------------------------------------------------------------------
mkdir -p "$DATA"
if [ ! -f "$DATA/relay.json" ]; then
  CERTPW="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20)"
  HOSTFQDN="$(hostname -f 2>/dev/null || hostname)"
  if command -v openssl >/dev/null 2>&1; then
    echo "==> Selbstsigniertes TLS-Zertifikat fuer '$HOSTFQDN' erzeugen (Browser warnt — fuer Produktion echtes Zertifikat einspielen, siehe KB)"
    openssl req -x509 -newkey rsa:2048 -sha256 -days 825 -nodes \
      -keyout "$TMP/key.pem" -out "$TMP/crt.pem" -subj "/CN=$HOSTFQDN" \
      -addext "subjectAltName=DNS:$HOSTFQDN" >/dev/null 2>&1
    openssl pkcs12 -export -out "$DATA/cert.pfx" -inkey "$TMP/key.pem" -in "$TMP/crt.pem" -passout "pass:$CERTPW"
    chmod 600 "$DATA/cert.pfx"
    cat > "$DATA/relay.json" <<EOF
{
  "Port": 9013,
  "BindAddress": "0.0.0.0",
  "CertificateFile": "$DATA/cert.pfx",
  "CertificatePassword": "$CERTPW",
  "Update": { "Enabled": false }
}
EOF
  else
    echo "==> Kein openssl — Relay startet ohne TLS (HTTP). Der Web-Client /app braucht HTTPS; spaeter nachruesten (KB-0017)."
    printf '{\n  "Port": 9013,\n  "BindAddress": "0.0.0.0",\n  "Update": { "Enabled": false }\n}\n' > "$DATA/relay.json"
  fi
  chmod 600 "$DATA/relay.json"
fi

# --- 4. systemd ---------------------------------------------------------------------------------
if [ ! -f "$UNIT" ]; then
  cat > "$UNIT" <<EOF
[Unit]
Description=BrowserDesk Relay (Signalisierung, Konsole, Web-Client, TURN)
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=$APP/BrowserDesk.Relay
WorkingDirectory=$APP
EnvironmentFile=$ENVFILE
Restart=always
# Audit 04.09.2026 (S41): Haertung. Root bleibt (Ports 80/443, /opt fuer das Selbstupdate), aber ohne
# Rechteausweitung, ohne Zugriff auf Home-Verzeichnisse und Kernel-Schalter, mit eigenem /tmp.
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
LockPersonality=yes
ProtectSystem=full
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  systemctl daemon-reload
fi
systemctl enable --now browserdesk-relay

# --- Nachweis: laeuft er wirklich? --------------------------------------------------------------
echo "==> Warten auf /health"
OK=""
for i in $(seq 1 60); do
  if curl -skf https://127.0.0.1:9013/health >/dev/null 2>&1 || curl -sf http://127.0.0.1:9013/health >/dev/null 2>&1; then OK=1; break; fi
  sleep 2
done
if [ -z "$OK" ]; then
  echo "FEHLER: der Relay meldet sich nicht. Protokoll: journalctl -u browserdesk-relay -n 50"; exit 1
fi

echo
echo "FERTIG — BrowserDesk Relay $VER laeuft."
echo "  Admin-Passwort : cat $DATA/admin-password.txt   (wird beim Erststart erzeugt)"
echo "  Admin-Konsole  : https://<host>:9013/admin/   (selbstsigniertes Zertifikat -> Browser-Warnung)"
echo "  Firewall       : eingehend oeffnen: 9013/tcp, 3478/udp, 5349/tcp (+ TURN-Relay-Ports, siehe KB)"
echo "  Backup         : $DATA (Konfiguration/Schluessel) + pg_dump der Datenbank 'browserdesk'"
echo "  Update spaeter : curl -fsSL https://browserdesk.de/kb/update-browserdesk-relay-linux.sh | bash"
