164 lines
5.0 KiB
Bash
Executable File
164 lines
5.0 KiB
Bash
Executable File
#!/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"
|