#!/usr/bin/env bash set -euo pipefail DIR="/opt/hx-ki/com2-stack" F="$DIR/docker-compose.yml" ENVF="$DIR/.env" NET="hxki-internal" TS="$(date +%Y%m%d-%H%M%S)" BK="$DIR/backup-$TS" echo "=== HXKI · COM2 ONE-SHOT TEST+FIX ===" echo "Autorität: $F" echo "Backup-Dir: $BK" mkdir -p "$BK" [ -f "$F" ] || { echo "FEHLT: $F"; exit 1; } cp -a "$F" "$BK/docker-compose.yml.orig" # 0) Host-Caddy stoppen (nur wenn aktiv) if systemctl is-active --quiet caddy 2>/dev/null; then echo "[0] Stoppe host-caddy (systemd)" systemctl stop caddy fi # 1) hxki-internal sicherstellen (external) docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET" >/dev/null # 2) Compose: Top-level networks sauber setzen: hxki-internal external:true python3 - <<'PY' from pathlib import Path import re p = Path("/opt/hx-ki/com2-stack/docker-compose.yml") s = p.read_text() # entferne vorhandenen TOP-level networks block s = re.sub(r'(?ms)^networks:\s*\n(.*?)(?=^\S|\Z)', '', s) if not re.search(r'(?m)^services:\s*$', s): raise SystemExit("FEHLT: top-level 'services:' Block") s = s.rstrip() + "\n\nnetworks:\n hxki-internal:\n external: true\n" p.write_text(s) print("[2] OK: networks/hxki-internal external:true gesetzt") PY # 3) Host-Ports für interne Services entfernen (Caddy bleibt außen) python3 - <<'PY' from pathlib import Path import re p = Path("/opt/hx-ki/com2-stack/docker-compose.yml") s = p.read_text() def drop_ports(service: str, text: str) -> str: # entfernt "ports:" block innerhalb eines service blocks pat = rf'(?ms)^(\s{{2}}{re.escape(service)}:\s*\n.*?)(^\s{{4}}ports:\s*\n(?:^\s{{6}}-.*\n)+)(?=^\s{{4}}[A-Za-z0-9_.-]+:|\Z)' return re.sub(pat, r'\1', text) services = re.findall(r'(?m)^\s{2}([A-Za-z0-9_.-]+):\s*$', s) for svc in services: if re.match(r'^(hxki-n8n|hxki-mautic|hxki-mariadb|hxki-postgres)$', svc): s = drop_ports(svc, s) p.write_text(s) print("[3] OK: Host-Ports für interne Services entfernt (falls vorhanden)") PY # 4) .env anlegen falls fehlt (keine Passwörter raten) if [ ! -f "$ENVF" ]; then cat > "$ENVF" <<'ENV' PG_USER=hxki PG_PASSWORD=CHANGE_ME_STRONG PG_DB=n8n ENV echo "[4] INFO: $ENVF angelegt (PG_PASSWORD setzen)" fi cp -a "$ENVF" "$BK/.env.orig" 2>/dev/null || true # 5) n8n: Postgres ENV erzwingen (nur wenn Service existiert) python3 - <<'PY' from pathlib import Path import re p = Path("/opt/hx-ki/com2-stack/docker-compose.yml") s = p.read_text() if not re.search(r'(?m)^\s{2}hxki-n8n:\s*$', s): print("[5] INFO: hxki-n8n Service nicht gefunden -> skip") raise SystemExit(0) m = re.search(r'(?ms)^\s{2}hxki-n8n:\s*\n(.*?)(?=^\s{2}[A-Za-z0-9_.-]+:\s*$|\Z)', s) block = m.group(0) # environment block sicherstellen if not re.search(r'(?m)^\s{4}environment:\s*$', block): lines = block.splitlines(True) lines.insert(1, " environment:\n") block = "".join(lines) def ensure(block: str, key: str, val: str) -> str: if re.search(rf'(?m)^\s{{6}}{re.escape(key)}:\s*', block): return re.sub(rf'(?m)^(\s{{6}}{re.escape(key)}:\s*).*$', rf'\1{val}', block) return re.sub(r'(?m)^\s{4}environment:\s*$', f" environment:\n {key}: {val}", block, count=1) block2 = block block2 = ensure(block2, "DB_TYPE", "postgresdb") block2 = ensure(block2, "DB_POSTGRESDB_HOST", "hxki-postgres") block2 = ensure(block2, "DB_POSTGRESDB_PORT", "5432") block2 = ensure(block2, "DB_POSTGRESDB_DATABASE", "${PG_DB}") block2 = ensure(block2, "DB_POSTGRESDB_USER", "${PG_USER}") block2 = ensure(block2, "DB_POSTGRESDB_PASSWORD", "${PG_PASSWORD}") s = s.replace(block, block2) p.write_text(s) print("[5] OK: n8n Postgres ENV gesetzt/gesichert") PY # 6) Validate echo "[6] Validate: docker compose config" docker compose -f "$F" --env-file "$ENVF" config >/dev/null echo "OK: Compose valide." # 7) Up echo "[7] Up" docker compose -f "$F" --env-file "$ENVF" up -d --remove-orphans # 8) n8n config JSON fix (nur wenn Parse-Error) if docker logs --tail=200 hxki-n8n 2>/dev/null | grep -q 'Error parsing n8n-config file'; then echo "[8] FIX: n8n-config ist kein JSON -> reset minimal" MOUNT="/data/HXKI_WORKSPACE/router" CFG="$MOUNT/config" mkdir -p "$MOUNT" [ -f "$CFG" ] && cp -a "$CFG" "$BK/n8n.config.bak" || true cat > "$CFG" <<'JSON' {"encryptionKey":""} JSON chown -R 1000:1000 "$MOUNT" || true chmod -R u+rwX,g+rwX "$MOUNT" || true docker restart hxki-n8n >/dev/null fi echo echo "=== STATUS ===" docker ps --format 'NAME={{.Names}} STATUS={{.Status}} PORTS={{.Ports}}' | egrep '^(hx|hxki)' || true echo echo "=== NET MEMBERS ($NET) ===" docker network inspect "$NET" --format '{{range $id,$c := .Containers}}{{println $c.Name}}{{end}}' | sort || true echo echo "=== TEST: Caddy -> n8n intern ===" docker exec -it hx-caddy sh -lc 'wget -qO- http://hxki-n8n:5678/ >/dev/null && echo OK_CADDY_TO_N8N || (echo FAIL_CADDY_TO_N8N; exit 1)' echo echo "=== TEST: n8n DB ENV ===" docker exec -it hxki-n8n sh -lc 'printenv | egrep "^DB_TYPE=|^DB_POSTGRESDB_HOST=" | sort' echo echo "=== ENDE · ONE-SHOT ===" echo "Backups: $BK"