initial COM2 system snapshot
This commit is contained in:
168
COM2_REPAIR_ONE_SHOT.sh
Executable file
168
COM2_REPAIR_ONE_SHOT.sh
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/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"
|
||||
mkdir -p "$BK"
|
||||
|
||||
echo "=== COM2 REPAIR ONE-SHOT ==="
|
||||
echo "Autorität: $F"
|
||||
[ -f "$F" ] || { echo "FEHLT: $F"; exit 1; }
|
||||
|
||||
cp -a "$F" "$BK/docker-compose.yml.pre"
|
||||
|
||||
# 0) hxki-internal muss als external existieren
|
||||
docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET" >/dev/null
|
||||
|
||||
# 1) Service hxki-web wiederherstellen aus letztem Backup das hxki-web enthält
|
||||
LATEST_WEB_SRC="$(ls -1t "$DIR"/backup-*/docker-compose.yml.orig 2>/dev/null | head -n1 || true)"
|
||||
if ! grep -qE '^\s{2}hxki-web:\s*$' "$F"; then
|
||||
echo "[1] hxki-web fehlt -> versuche Restore aus Backup"
|
||||
if [ -z "${LATEST_WEB_SRC:-}" ] || ! grep -qE '^\s{2}hxki-web:\s*$' "$LATEST_WEB_SRC"; then
|
||||
echo "FAIL: Kein Backup mit hxki-web gefunden. Lege eine Quelle bereit (z.B. altes Compose)."
|
||||
echo " Erwartet: $DIR/backup-*/docker-compose.yml.orig mit ' hxki-web:' drin."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - <<PY
|
||||
import re, pathlib
|
||||
dst = pathlib.Path("$F").read_text()
|
||||
src = pathlib.Path("$LATEST_WEB_SRC").read_text()
|
||||
|
||||
def extract_service(text, name):
|
||||
m = re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', text)
|
||||
if not m: raise SystemExit("FEHLT services: im Source")
|
||||
block = m.group(1)
|
||||
pat = rf'(?ms)^\s{{2}}{re.escape(name)}:\s*\n(?:^\s{{4}}.*\n|^\s{{6}}.*\n|^\s{{8}}.*\n|^\s*\n)*?(?=^\s{{2}}[A-Za-z0-9_.-]+:\s*$|\Z)'
|
||||
mm = re.search(pat, block)
|
||||
if not mm: raise SystemExit(f"Service {name} nicht im Source gefunden")
|
||||
return mm.group(0)
|
||||
|
||||
web = extract_service(src, "hxki-web")
|
||||
|
||||
m = re.search(r'(?ms)^services:\s*\n', dst)
|
||||
if not m: raise SystemExit("FEHLT services: im Ziel")
|
||||
# direkt nach "services:\n" einfügen
|
||||
dst2 = re.sub(r'(?ms)^(services:\s*\n)', r'\1' + web + "\n", dst, count=1)
|
||||
pathlib.Path("$F").write_text(dst2)
|
||||
print("OK: hxki-web aus Backup wieder eingefügt.")
|
||||
PY
|
||||
else
|
||||
echo "[1] OK: hxki-web ist vorhanden"
|
||||
fi
|
||||
|
||||
# 2) Caddy Ports nach außen sicherstellen (damit es "reagiert")
|
||||
python3 - <<PY
|
||||
import re, pathlib
|
||||
p = pathlib.Path("$F")
|
||||
s = p.read_text()
|
||||
|
||||
def ensure_ports_for(service, ports_lines):
|
||||
# finde service block
|
||||
m = re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', s)
|
||||
if not m: raise SystemExit("FEHLT services:")
|
||||
block = m.group(1)
|
||||
|
||||
pat = rf'(?ms)^\s{{2}}{re.escape(service)}:\s*\n(?:^\s{{4}}.*\n|^\s{{6}}.*\n|^\s{{8}}.*\n|^\s*\n)*?(?=^\s{{2}}[A-Za-z0-9_.-]+:\s*$|\Z)'
|
||||
mm = re.search(pat, block)
|
||||
if not mm: raise SystemExit(f"FEHLT Service {service}")
|
||||
sb = mm.group(0)
|
||||
|
||||
if re.search(r'(?m)^\s{4}ports:\s*$', sb):
|
||||
# ports block vorhanden -> ergänze fehlende mappings
|
||||
def has(line): return re.search(r'(?m)^\s{6}-\s*' + re.escape(line) + r'\s*$', sb)
|
||||
add = [l for l in ports_lines if not has(l)]
|
||||
if add:
|
||||
sb2 = re.sub(r'(?m)^(\s{4}ports:\s*\n)', r'\1' + ''.join([f' - "{l}"\n' for l in add]), sb, count=1)
|
||||
else:
|
||||
sb2 = sb
|
||||
else:
|
||||
# kein ports block -> vor networks oder am ende einfügen
|
||||
ins = " ports:\n" + ''.join([f' - "{l}"\n' for l in ports_lines])
|
||||
if re.search(r'(?m)^\s{4}networks:\s*', sb):
|
||||
sb2 = re.sub(r'(?m)^(\s{4}networks:\s*)', ins + r'\1', sb, count=1)
|
||||
else:
|
||||
sb2 = sb.rstrip() + "\n" + ins
|
||||
return pat, sb, sb2
|
||||
|
||||
ports = ["80:80","443:443","443:443/udp","2019:2019"]
|
||||
pat, old, new = ensure_ports_for("hx-caddy", ports)
|
||||
if old != new:
|
||||
s2 = re.sub(pat, new, s, count=1)
|
||||
p.write_text(s2)
|
||||
print("OK: hx-caddy ports nach außen gesetzt/ergänzt.")
|
||||
else:
|
||||
print("OK: hx-caddy ports waren schon da.")
|
||||
PY
|
||||
|
||||
# 3) n8n muss im Container auf 0.0.0.0 lauschen (damit Caddy reinkommt)
|
||||
python3 - <<PY
|
||||
import re, pathlib
|
||||
p = pathlib.Path("$F")
|
||||
s = p.read_text()
|
||||
|
||||
def patch_env(service, key, val):
|
||||
m = re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', s)
|
||||
if not m: raise SystemExit("FEHLT services:")
|
||||
block = m.group(1)
|
||||
pat = rf'(?ms)^\s{{2}}{re.escape(service)}:\s*\n(?:^\s{{4}}.*\n|^\s{{6}}.*\n|^\s{{8}}.*\n|^\s*\n)*?(?=^\s{{2}}[A-Za-z0-9_.-]+:\s*$|\Z)'
|
||||
mm = re.search(pat, block)
|
||||
if not mm: raise SystemExit(f"FEHLT Service {service}")
|
||||
sb = mm.group(0)
|
||||
|
||||
if re.search(r'(?m)^\s{6}'+re.escape(key)+r':', sb):
|
||||
sb2 = re.sub(r'(?m)^(\s{6}'+re.escape(key)+r':\s*).*$' , r'\1'+val, sb)
|
||||
else:
|
||||
if re.search(r'(?m)^\s{4}environment:\s*$', sb):
|
||||
sb2 = re.sub(r'(?m)^(\s{4}environment:\s*\n)', r'\1' + f' {key}: {val}\n', sb, count=1)
|
||||
else:
|
||||
# env block vor volumes/networks einfügen
|
||||
env = " environment:\n" + f" {key}: {val}\n"
|
||||
if re.search(r'(?m)^\s{4}volumes:\s*', sb):
|
||||
sb2 = re.sub(r'(?m)^(\s{4}volumes:\s*)', env + r'\1', sb, count=1)
|
||||
elif re.search(r'(?m)^\s{4}networks:\s*', sb):
|
||||
sb2 = re.sub(r'(?m)^(\s{4}networks:\s*)', env + r'\1', sb, count=1)
|
||||
else:
|
||||
sb2 = sb.rstrip() + "\n" + env
|
||||
return pat, sb, sb2
|
||||
|
||||
pat, old, new = patch_env("hxki-n8n", "N8N_LISTEN_ADDRESS", "0.0.0.0")
|
||||
if old != new:
|
||||
s2 = re.sub(pat, new, s, count=1)
|
||||
p.write_text(s2)
|
||||
print("OK: hxki-n8n N8N_LISTEN_ADDRESS=0.0.0.0 gesetzt.")
|
||||
else:
|
||||
print("OK: hxki-n8n N8N_LISTEN_ADDRESS war schon ok.")
|
||||
PY
|
||||
|
||||
# 4) Validate (harte Wahrheit)
|
||||
echo "[V] Validate: docker compose config"
|
||||
docker compose -f "$F" config >/dev/null
|
||||
echo "OK: Compose valide."
|
||||
|
||||
# 5) Orchester neu starten
|
||||
cd "$DIR"
|
||||
docker compose down --remove-orphans || true
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
# 6) n8n Listen-Check im Container
|
||||
echo "[T] n8n lauscht?"
|
||||
for i in {1..60}; do
|
||||
if docker exec hxki-n8n sh -lc "ss -lnt | grep -q ':5678'"; then
|
||||
echo "OK: n8n lauscht auf 5678."
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 7) Caddy -> n8n intern
|
||||
echo "[T] Caddy -> n8n?"
|
||||
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'
|
||||
|
||||
# 8) Status
|
||||
docker ps --format 'NAME={{.Names}} STATUS={{.Status}} PORTS={{.Ports}}'
|
||||
|
||||
echo "=== ENDE COM2 REPAIR ONE-SHOT ==="
|
||||
Reference in New Issue
Block a user