From 7877faa786cd61900b50416d81d23a29e323de1a Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 28 Oct 2026 10:19:27 +0100 Subject: [PATCH] Fehlerhandling ausgearbeitet --- app/cli.py | 22 ++++++++++++++++++---- scripts/dev.sh | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/app/cli.py b/app/cli.py index 77e653c..d1eec86 100644 --- a/app/cli.py +++ b/app/cli.py @@ -163,11 +163,25 @@ def main() -> None: sys.exit(demo_workspace.run(settings, args.names, args.reset)) elif args.command == "web": import uvicorn - # Logging wie oben eingerichtet; die Zugriffszeile schreibt die App selbst (mit Request-ID) - uvicorn.run("app.main:create_app", factory=True, host=args.host, port=args.port, reload=args.reload, - log_config=None, access_log=False) + # Start und Abbruch immer auf stderr, unabhängig von AIW_LOG_LEVEL: sonst endet ein Fehlstart still + print(f"aiw web: starte auf http://{args.host}:{args.port} (Log-Level {settings.log_level})", file=sys.stderr) + try: + # Logging wie oben eingerichtet; die Zugriffszeile schreibt die App selbst (mit Request-ID) + uvicorn.run("app.main:create_app", factory=True, host=args.host, port=args.port, reload=args.reload, + log_config=None, access_log=False) + except SystemExit as exit: + if exit.code: + print(f"aiw web: Start fehlgeschlagen (Exit-Code {exit.code}), z. B. Port {args.port} belegt oder " + "Fehler beim Laden der App. Details mit AIW_LOG_LEVEL=DEBUG.", file=sys.stderr) + raise elif args.command == "worker": - run_worker(settings) + port = settings.worker_http_port or "aus" + print(f"aiw worker: starte (Health-Port {port}, Log-Level {settings.log_level})", file=sys.stderr) + try: + run_worker(settings) + except Exception as error: + print(f"aiw worker: abgebrochen: {type(error).__name__}: {error}", file=sys.stderr) + raise elif args.command == "create-admin": create_admin_user(settings, args.username) elif args.command in ("backup", "restore", "check"): diff --git a/scripts/dev.sh b/scripts/dev.sh index 336bd58..b4fdcfd 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -49,7 +49,20 @@ fail() { printf '\033[31m%s\033[0m\n' "$*" >&2; exit 1; } # Zeilen eines Prozesses mit Namen versehen (portabel, auch mit der Bash von macOS) prefix() { while IFS= read -r line; do printf '[%-6s] %s\n' "$1" "$line"; done; } -port_busy() { lsof -nP -iTCP:"$1" -sTCP:LISTEN >/dev/null 2>&1; } +# Lauscht etwas auf dem Port? Mit lsof, sonst (z. B. Linux ohne lsof) per Verbindungsversuch +port_busy() { + if command -v lsof >/dev/null 2>&1; then + lsof -nP -iTCP:"$1" -sTCP:LISTEN >/dev/null 2>&1 + else + (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null + fi +} + +# Wer belegt den Port? (für die Fehlermeldung; leer, wenn es sich nicht feststellen lässt) +port_owner() { + command -v lsof >/dev/null 2>&1 || return 0 + lsof -nP -iTCP:"$1" -sTCP:LISTEN 2>/dev/null | awk 'NR == 2 { print $1 " (PID " $2 ")" }' +} env_value() { grep -E "^$1=" .env 2>/dev/null | tail -1 | cut -d= -f2-; } @@ -108,7 +121,11 @@ fi [ -f .env ] || fail "Keine .env. Ersteinrichtung mit: scripts/dev.sh --setup" [ -n "${AIW_SECRET_KEY:-$(env_value AIW_SECRET_KEY)}" ] || fail "AIW_SECRET_KEY fehlt in .env (scripts/dev.sh --setup)." -port_busy "$PORT" && fail "Port $PORT ist belegt (läuft Bifröst schon?). Anderer Port: --port 8000" +port_busy "$PORT" && fail "Port $PORT ist belegt $(port_owner "$PORT") (läuft Bifröst schon?). Anderer Port: --port 8000" +WORKER_PORT=${AIW_WORKER_HTTP_PORT:-$(env_value AIW_WORKER_HTTP_PORT)} +WORKER_PORT=${WORKER_PORT:-9101} +port_busy "$WORKER_PORT" && fail "Port $WORKER_PORT (Health des Workers) ist belegt $(port_owner "$WORKER_PORT") \ +– läuft noch ein Worker? Stoppen oder AIW_WORKER_HTTP_PORT in .env ändern." say "PostgreSQL starten (127.0.0.1:55433)" docker compose up -d --wait postgres @@ -126,7 +143,11 @@ export AIW_COOKIE_SECURE=${AIW_COOKIE_SECURE:-false} export PYTHONUNBUFFERED=1 trap 'trap - INT TERM EXIT; echo; say "Stoppe Web, Worker und DB-Dienste"; kill 0 2>/dev/null; wait 2>/dev/null' INT TERM EXIT +# Gestartete Prozesse: PID, Name und Befehl zum Einzelstart (parallele Listen, Bash 3 von macOS kennt keine Maps) PIDS=() +NAMES=() +COMMANDS=() +started() { PIDS+=("$1"); NAMES+=("$2"); COMMANDS+=("$3"); } HINTS=() @@ -140,7 +161,8 @@ db_service() { say "DB-Dienst $engine starten (Port $port)" (cd services/db-mcp && DBMCP_API_KEYS="dev:$DEV_KEY" DBMCP_ALLOW_LOCAL=true DBMCP_PORT=$port \ DBMCP_BIND=127.0.0.1 "$ROOT/.venv/bin/python" -m "dbmcp.$engine" 2>&1 | prefix "$engine") & - PIDS+=($!) + started $! "DB-Dienst $engine (Port $port)" \ + "cd services/db-mcp && DBMCP_API_KEYS=dev:$DEV_KEY DBMCP_ALLOW_LOCAL=true DBMCP_PORT=$port ../../.venv/bin/python -m dbmcp.$engine" for _ in $(seq 50); do curl -sf "http://127.0.0.1:$port/ready" >/dev/null 2>&1 && break; sleep 0.2; done fi HINTS+=("$engine: URL http://localhost:$port/mcp Schlüssel des Dienstes $DEV_KEY") @@ -160,10 +182,10 @@ fi say "Web starten: http://127.0.0.1:$PORT" (uv run aiw web --port "$PORT" 2>&1 | prefix web) & -PIDS+=($!) +started $! "Web (Port $PORT)" "uv run aiw web --port $PORT" say "Worker starten" (uv run aiw worker 2>&1 | prefix worker) & -PIDS+=($!) +started $! "Worker" "uv run aiw worker" if $RESET; then say "Fertig von null. Noch offen: als admin ein Modell einrichten (Administration → Modelle und Zugänge)." @@ -174,11 +196,17 @@ else fi for hint in "${HINTS[@]}"; do echo " $hint"; done -# Endet ein Prozess (z. B. Fehler beim Start), alles stoppen +# Endet ein Prozess (z. B. Fehler beim Start), sagen welcher und alles stoppen while true; do - for pid in "${PIDS[@]}"; do - if ! kill -0 "$pid" 2>/dev/null; then - fail "Ein Prozess hat sich beendet (siehe Log oben)." + for i in "${!PIDS[@]}"; do + if ! kill -0 "${PIDS[$i]}" 2>/dev/null; then + code=0 + wait "${PIDS[$i]}" 2>/dev/null || code=$? + echo + printf '\033[31m%s hat sich beendet (Exit-Code %s).\033[0m\n' "${NAMES[$i]}" "$code" >&2 + echo "Seine Ausgabe steht oben mit Präfix; fehlt sie, den Prozess einzeln starten und die Meldung ansehen:" >&2 + echo " ${COMMANDS[$i]}" >&2 + exit 1 fi done sleep 1