138 lines
4.8 KiB
Bash
Executable File
138 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
DIR="/opt/hx-ki/com2-stack"
|
||
F="$DIR/docker-compose.yml"
|
||
NET="hxki-internal"
|
||
TS="$(date +%Y%m%d-%H%M%S)"
|
||
BK="$DIR/backup-$TS"
|
||
|
||
echo "=== COM2 DOCTOR (one-shot, deterministisch) ==="
|
||
echo "Autorität: $F"
|
||
echo "Backup: $BK"
|
||
mkdir -p "$BK"
|
||
|
||
# 0) Preconditions
|
||
[ -f "$F" ] || { echo "FEHLT: $F"; exit 1; }
|
||
cp -a "$F" "$BK/docker-compose.yml.orig"
|
||
|
||
# 1) Host-Caddy darf NICHT Port 80/443 blocken, wenn Docker-Caddy laufen soll
|
||
if systemctl is-active --quiet caddy 2>/dev/null; then
|
||
echo "[1] Stoppe host-caddy (systemd), damit Docker-Caddy 80/443 bekommt"
|
||
systemctl stop caddy
|
||
else
|
||
echo "[1] OK: host-caddy ist nicht aktiv"
|
||
fi
|
||
|
||
# 2) Docker Netzwerk sicherstellen (external)
|
||
docker network inspect "$NET" >/dev/null 2>&1 || { echo "[2] Erzeuge Docker-Netzwerk: $NET"; docker network create "$NET" >/dev/null; }
|
||
echo "[2] OK: Netzwerk existiert: $NET"
|
||
|
||
# 3) Compose YAML MUSS valide sein – sonst: Restore und STOP mit Zeilenanzeige
|
||
echo "[3] Validate Compose YAML"
|
||
if ! docker compose -f "$F" config >/dev/null 2>&1; then
|
||
echo "FAIL: Compose ist NICHT valide."
|
||
echo "---- Parser-Fehler ----"
|
||
docker compose -f "$F" config 2>&1 | tail -n 30 || true
|
||
echo "---- Zeilen um die vermutete Fehlerstelle (45-80) ----"
|
||
nl -ba "$F" | sed -n '45,80p' || true
|
||
echo
|
||
echo "Auto-Restore auf Backup und STOP."
|
||
cp -a "$BK/docker-compose.yml.orig" "$F"
|
||
exit 1
|
||
fi
|
||
echo "[3] OK: Compose ist valide"
|
||
|
||
# 4) Pflicht-Services auf COM2 (laut deiner Vorgabe: autark)
|
||
echo "[4] Prüfe Pflicht-Services im Compose (ohne Umbau)"
|
||
python3 - <<'PY'
|
||
import re, pathlib, sys
|
||
p = pathlib.Path("/opt/hx-ki/com2-stack/docker-compose.yml")
|
||
s = p.read_text()
|
||
m = re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', s)
|
||
if not m:
|
||
print("FAIL: Kein Top-Level 'services:' Block.")
|
||
sys.exit(1)
|
||
block = m.group(1)
|
||
names = re.findall(r'(?m)^\s{2}([A-Za-z0-9_.-]+):\s*$', block)
|
||
need = ["hx-caddy","hxki-n8n","hxki-postgres","hxki-mariadb","hxki-mautic"]
|
||
missing = [x for x in need if x not in names]
|
||
print("Services gefunden:", ", ".join(names))
|
||
if missing:
|
||
print("FAIL: Pflicht-Services fehlen:", ", ".join(missing))
|
||
sys.exit(2)
|
||
print("OK: Pflicht-Services vorhanden.")
|
||
PY
|
||
|
||
# 5) hxki-internal als external:true MUSS im Compose vorhanden sein (sonst knallt network-ref)
|
||
echo "[5] Prüfe: networks/hxki-internal external:true"
|
||
python3 - <<'PY'
|
||
from pathlib import Path
|
||
import re, sys
|
||
p = Path("/opt/hx-ki/com2-stack/docker-compose.yml")
|
||
s = p.read_text()
|
||
# sehr konservativ: nur prüfen, nicht "raten"
|
||
if not re.search(r'(?ms)^networks:\s*\n(?:.*\n)*?\s{2}hxki-internal:\s*\n(?:.*\n)*?\s{4}external:\s*true\s*$', s):
|
||
print("FAIL: networks: hxki-internal: external:true fehlt oder ist anders.")
|
||
print("Zeilen-Hinweis:")
|
||
import subprocess, shlex
|
||
subprocess.run(["bash","-lc",f"nl -ba {p} | grep -nE '^(\\s*networks:|\\s{{2}}hxki-internal:|\\s{{4}}external:)' -n || true"])
|
||
sys.exit(3)
|
||
print("OK: networks/hxki-internal external:true gefunden.")
|
||
PY
|
||
|
||
# 6) n8n config MUSS gültiges JSON sein (dein log beweist, dass es kaputt war)
|
||
# Wir reparieren NUR das deterministisch: config als JSON resetten, ohne Workflows/DB anzufassen.
|
||
echo "[6] Fix: n8n config JSON (nur wenn kaputt)"
|
||
MOUNT_SRC="/data/HXKI_WORKSPACE/router"
|
||
CFG="$MOUNT_SRC/config"
|
||
if [ -d "$MOUNT_SRC" ]; then
|
||
docker stop hxki-n8n >/dev/null 2>&1 || true
|
||
|
||
if [ -f "$CFG" ]; then
|
||
if python3 - <<PY >/dev/null 2>&1
|
||
import json
|
||
json.load(open("$CFG","r"))
|
||
PY
|
||
then
|
||
echo "OK: $CFG ist gültiges JSON (nichts zu tun)"
|
||
else
|
||
echo "WARN: $CFG ist KEIN gültiges JSON -> schreibe minimal gültig + Backup"
|
||
cp -a "$CFG" "$CFG.bak.$TS"
|
||
cat > "$CFG" <<JSON
|
||
{"encryptionKey":"","instanceId":""}
|
||
JSON
|
||
chown 1000:1000 "$CFG" || true
|
||
chmod 600 "$CFG" || true
|
||
fi
|
||
else
|
||
echo "INFO: $CFG existiert nicht -> wird von n8n beim Start erzeugt"
|
||
fi
|
||
|
||
# Rechte auf Mount (konservativ)
|
||
chown -R 1000:1000 "$MOUNT_SRC" || true
|
||
chmod -R u+rwX "$MOUNT_SRC" || true
|
||
chmod -R g+rwX "$MOUNT_SRC" || true
|
||
|
||
docker start hxki-n8n >/dev/null 2>&1 || true
|
||
else
|
||
echo "WARN: $MOUNT_SRC fehlt -> n8n Mount passt nicht (keine Reparatur)"
|
||
fi
|
||
|
||
# 7) Orchester hoch (alles oder nichts)
|
||
echo "[7] Orchester UP (alles zusammen)"
|
||
cd "$DIR"
|
||
docker compose up -d --remove-orphans
|
||
|
||
echo "[8] Status (Container)"
|
||
docker ps --format 'NAME={{.Names}} STATUS={{.Status}} PORTS={{.Ports}}' | egrep 'hxki-|hx-' || true
|
||
|
||
echo "[9] Netzwerk-Mitglieder: $NET"
|
||
docker network inspect "$NET" --format '{{range $id,$c := .Containers}}{{println $c.Name}}{{end}}' | sort || true
|
||
|
||
# 10) Caddy -> n8n intern test (nur intern, ohne "lokal 127.0.0.1")
|
||
echo "[10] Caddy->n8n Service-Test (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' || true
|
||
|
||
echo "=== ENDE ==="
|