Files
hx-ki.com2/COM2_FIX_ONE_SHOT.sh
2026-03-06 15:22:40 +00:00

200 lines
7.4 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-fix-$TS"
mkdir -p "$BK"
echo "=== COM2 FIX ONE-SHOT ==="
echo "Autorität: $F"
[ -f "$F" ] || { echo "FEHLT: $F"; exit 1; }
cp -a "$F" "$BK/docker-compose.yml.pre"
# Netzwerk muss als external existieren (Host)
docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET" >/dev/null
# .env laden (falls vorhanden)
if [ -f "$ENVF" ]; then
set -a
# shellcheck disable=SC1090
. "$ENVF"
set +a
fi
# Helper: ENV aus Container ziehen
get_env_from_container() {
local c="$1" key="$2"
docker inspect "$c" --format '{{range .Config.Env}}{{println .}}{{end}}' \
| awk -F= -v k="$key" '$1==k{print substr($0, index($0,"=")+1)}' \
| head -n1
}
echo
echo "[1] Container vorhanden?"
for c in hxki-postgres hxki-mariadb hxki-n8n hxki-mautic; do
docker inspect "$c" >/dev/null 2>&1 || { echo "FEHLT Container: $c"; exit 1; }
done
echo
echo "[2] Postgres: setze/angleiche User/DB/PW (damit n8n startet)"
# Werte entweder aus .env oder aus n8n-Container ENV
PG_USER="${PG_USER:-$(get_env_from_container hxki-n8n DB_POSTGRESDB_USER)}"
PG_PASSWORD="${PG_PASSWORD:-$(get_env_from_container hxki-n8n DB_POSTGRESDB_PASSWORD)}"
PG_DB="${PG_DB:-$(get_env_from_container hxki-n8n DB_POSTGRESDB_DATABASE)}"
[ -n "${PG_USER:-}" ] || { echo "FEHLT: PG_USER (in $ENVF oder n8n ENV)"; exit 1; }
[ -n "${PG_PASSWORD:-}" ] || { echo "FEHLT: PG_PASSWORD (in $ENVF oder n8n ENV)"; exit 1; }
[ -n "${PG_DB:-}" ] || { echo "FEHLT: PG_DB (in $ENVF oder n8n ENV)"; exit 1; }
docker exec -u postgres -i hxki-postgres psql -v ON_ERROR_STOP=1 -d postgres <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='${PG_USER}') THEN
CREATE ROLE ${PG_USER} LOGIN PASSWORD '${PG_PASSWORD}';
ELSE
ALTER ROLE ${PG_USER} WITH LOGIN PASSWORD '${PG_PASSWORD}';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname='${PG_DB}') THEN
CREATE DATABASE ${PG_DB} OWNER ${PG_USER};
END IF;
END
\$\$;
GRANT ALL PRIVILEGES ON DATABASE ${PG_DB} TO ${PG_USER};
SQL
echo "OK: Postgres User/DB/PW angeglichen."
echo
echo "[3] MariaDB: setze/angleiche Mautic DB/User/PW (damit mautic startet)"
# Root-PW aus MariaDB Container ENV
MDB_ROOT_PW="$(get_env_from_container hxki-mariadb MYSQL_ROOT_PASSWORD || true)"
[ -n "${MDB_ROOT_PW:-}" ] || MDB_ROOT_PW="$(get_env_from_container hxki-mariadb MARIADB_ROOT_PASSWORD || true)"
[ -n "${MDB_ROOT_PW:-}" ] || { echo "FEHLT: MariaDB Root-Passwort (MYSQL_ROOT_PASSWORD/MARIADB_ROOT_PASSWORD im Container)"; exit 1; }
# Mautic Zielwerte: aus Mautic Container ENV (Quelle der Wahrheit)
MAUTIC_DB_HOST="$(get_env_from_container hxki-mautic MAUTIC_DB_HOST || true)"
MAUTIC_DB_USER="$(get_env_from_container hxki-mautic MAUTIC_DB_USER || true)"
MAUTIC_DB_PASSWORD="$(get_env_from_container hxki-mautic MAUTIC_DB_PASSWORD || true)"
MAUTIC_DB_NAME="$(get_env_from_container hxki-mautic MAUTIC_DB_NAME || true)"
# Fallbacks (falls Image andere Keys nutzt)
[ -n "${MAUTIC_DB_USER:-}" ] || MAUTIC_DB_USER="$(get_env_from_container hxki-mautic MYSQL_USER || true)"
[ -n "${MAUTIC_DB_PASSWORD:-}" ] || MAUTIC_DB_PASSWORD="$(get_env_from_container hxki-mautic MYSQL_PASSWORD || true)"
[ -n "${MAUTIC_DB_NAME:-}" ] || MAUTIC_DB_NAME="$(get_env_from_container hxki-mautic MYSQL_DATABASE || true)"
[ -n "${MAUTIC_DB_USER:-}" ] || { echo "FEHLT: MAUTIC_DB_USER (im hxki-mautic ENV)"; exit 1; }
[ -n "${MAUTIC_DB_PASSWORD:-}" ] || { echo "FEHLT: MAUTIC_DB_PASSWORD (im hxki-mautic ENV)"; exit 1; }
[ -n "${MAUTIC_DB_NAME:-}" ] || { echo "FEHLT: MAUTIC_DB_NAME (im hxki-mautic ENV)"; exit 1; }
docker exec -i hxki-mariadb mysql -uroot -p"${MDB_ROOT_PW}" <<SQL
CREATE DATABASE IF NOT EXISTS \`${MAUTIC_DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${MAUTIC_DB_USER}'@'%' IDENTIFIED BY '${MAUTIC_DB_PASSWORD}';
ALTER USER '${MAUTIC_DB_USER}'@'%' IDENTIFIED BY '${MAUTIC_DB_PASSWORD}';
GRANT ALL PRIVILEGES ON \`${MAUTIC_DB_NAME}\`.* TO '${MAUTIC_DB_USER}'@'%';
FLUSH PRIVILEGES;
SQL
echo "OK: MariaDB User/DB/PW angeglichen."
echo
echo "[4] hxki-web: repariere Service-Definition aus vorhandenem Backup (damit /app/package.json wieder stimmt)"
# Quelle suchen (du hast welche gefunden)
SRC=""
for cand in \
/opt/hx-ki/com-stack/docker-compose.yml \
/opt/hx-ki/com-stack/docker-compose.yml.bak.* \
/opt/hx-ki/docker/docker-compose.yml.bak_* \
/opt/hx-ki/com2-stack/docker-compose.yml.bak.* ; do
for f in $cand; do
[ -f "$f" ] || continue
if grep -qE '^[[:space:]]{2}hxki-web:[[:space:]]*$' "$f"; then
SRC="$f"
break 2
fi
done
done
if [ -z "$SRC" ]; then
echo "WARN: Keine Quelle mit hxki-web gefunden -> überspringe web-restore."
else
echo "SRC(web)=$SRC"
cp -a "$SRC" "$BK/docker-compose.yml.web-src"
python3 - <<PY
import re
from pathlib import Path
dst = Path("$F")
src = Path("$SRC")
d = dst.read_text()
s = src.read_text()
def extract_block(text, name):
# block: " name:" bis zum nächsten " <service>:"
m = re.search(rf'(?ms)^\\s{{2}}{re.escape(name)}:\\s*\\n.*?(?=^\\s{{2}}[A-Za-z0-9_.-]+:\\s*$|\\Z)', text)
return m.group(0) if m else None
web = extract_block(s, "hxki-web")
if not web:
raise SystemExit("SRC hat keinen hxki-web Block obwohl grep ihn fand (unerwartet).")
# dst services-block finden
ms = re.search(r'(?ms)^services:\\s*\\n', d)
if not ms:
raise SystemExit("DST hat keinen top-level services:-Block.")
# wenn dst schon hxki-web hat -> ersetzen, sonst einfügen
if re.search(r'(?m)^\\s{2}hxki-web:\\s*$', d):
d2 = re.sub(r'(?ms)^\\s{2}hxki-web:\\s*\\n.*?(?=^\\s{2}[A-Za-z0-9_.-]+:\\s*$|\\Z)', web, d)
else:
# nach services: direkt einfügen (oben)
d2 = re.sub(r'(?ms)^(services:\\s*\\n)', r'\\1' + web + "\\n", d, count=1)
# sicherstellen, dass hxki-web im hxki-internal hängt (wenn nicht, adden)
if "hxki-web:" in d2 and "networks: [hxki-internal]" not in web and "networks:" not in web:
# falls web-block gar keine networks hat: am Ende des blocks ergänzen
d2 = re.sub(r'(?ms)(^\\s{2}hxki-web:\\s*\\n.*?)(?=^\\s{2}[A-Za-z0-9_.-]+:\\s*$|\\Z)',
lambda m: m.group(1).rstrip()+"\\n networks: [hxki-internal]\\n",
d2, count=1)
dst.write_text(d2)
PY
echo "OK: hxki-web Block restored/patched."
fi
echo
echo "[5] Validate + Restart Orchester"
cd "$DIR"
docker compose -f "$F" config >/dev/null
docker compose down --remove-orphans
docker compose up -d --remove-orphans
echo
echo "[6] Hard Checks"
docker ps --format 'NAME={{.Names}} STATUS={{.Status}} PORTS={{.Ports}}' | egrep 'hxki-|hx-caddy' || true
docker network inspect "$NET" --format '{{range $id,$c := .Containers}}{{println $c.Name}}{{end}}' | sort
echo
echo "[7] Caddy -> Upstreams"
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
docker exec -it hx-caddy sh -lc 'wget -qO- http://hxki-mautic/ >/dev/null && echo OK_CADDY_TO_MAUTIC || echo FAIL_CADDY_TO_MAUTIC' || true
# web: probiere 80 und 3000
docker exec -it hx-caddy sh -lc '(wget -qO- http://hxki-web/ >/dev/null && echo OK_CADDY_TO_WEB_80) || (wget -qO- http://hxki-web:3000/ >/dev/null && echo OK_CADDY_TO_WEB_3000) || echo FAIL_CADDY_TO_WEB' || true
echo
echo "[8] Logs kurz"
docker logs --tail=30 hxki-n8n 2>&1 || true
docker logs --tail=30 hxki-mautic 2>&1 || true
docker logs --tail=30 hxki-web 2>&1 || true
echo "=== ENDE ==="