Installer: .env-Hinweis + Beispiel-.env + Backend-Health-Check; freundlichere API-Fehler
- install.sh: ensure_example legt .env.example an falls fehlend; env_hint weist klar auf die anpassbare .env hin; wait_for_backend pollt /api/health und meldet 502-Ursachen mit Log-Hinweis. - web/api.js: HTML-Fehlerseiten (z.B. nginx 502) nicht mehr roh anzeigen, stattdessen verständliche Meldung. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
ENV_FILE="$SCRIPT_DIR/.env"
|
ENV_FILE="$SCRIPT_DIR/.env"
|
||||||
|
EXAMPLE_FILE="$SCRIPT_DIR/.env.example"
|
||||||
ASSUME_YES=0
|
ASSUME_YES=0
|
||||||
[[ "${1:-}" == "--yes" || "${1:-}" == "-y" ]] && ASSUME_YES=1
|
[[ "${1:-}" == "--yes" || "${1:-}" == "-y" ]] && ASSUME_YES=1
|
||||||
|
|
||||||
@@ -105,14 +106,57 @@ ensure_docker() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Beispiel-.env sicherstellen ----------------------------------------------
|
||||||
|
ensure_example() {
|
||||||
|
if [[ -f "$EXAMPLE_FILE" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
info "Lege Beispiel-Konfiguration an: $EXAMPLE_FILE"
|
||||||
|
cat > "$EXAMPLE_FILE" <<'EOF'
|
||||||
|
# Kopiere diese Datei zu ".env" und passe die Werte an.
|
||||||
|
# install.sh erzeugt eine .env automatisch mit sicheren Zufallswerten.
|
||||||
|
|
||||||
|
# Port, unter dem die Web-UI erreichbar ist
|
||||||
|
WEB_PORT=8080
|
||||||
|
|
||||||
|
# Datenbank
|
||||||
|
POSTGRES_USER=project_good
|
||||||
|
POSTGRES_PASSWORD=project_good
|
||||||
|
POSTGRES_DB=project_good
|
||||||
|
|
||||||
|
# Sicherheit – im Betrieb unbedingt ändern!
|
||||||
|
JWT_SECRET=change-me-please
|
||||||
|
|
||||||
|
# Erster Admin-Benutzer (wird nur beim allerersten Start angelegt)
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=changeme
|
||||||
|
|
||||||
|
# CORS (bei Betrieb hinter dem mitgelieferten nginx nicht nötig)
|
||||||
|
CORS_ORIGINS=*
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Hinweis auf die Konfigurationsdatei --------------------------------------
|
||||||
|
env_hint() {
|
||||||
|
echo
|
||||||
|
info "Konfiguration liegt in: $ENV_FILE"
|
||||||
|
echo " Vorlage/Beispiel: $EXAMPLE_FILE"
|
||||||
|
warn "Dort kannst du bei Bedarf Werte anpassen: WEB_PORT (Port der Web-UI),"
|
||||||
|
warn "ADMIN_USERNAME/ADMIN_PASSWORD sowie die Datenbank- und JWT-Secrets."
|
||||||
|
warn "Nach Änderungen an der .env neu starten: $COMPOSE --env-file .env up -d"
|
||||||
|
}
|
||||||
|
|
||||||
# --- .env erzeugen ------------------------------------------------------------
|
# --- .env erzeugen ------------------------------------------------------------
|
||||||
create_env() {
|
create_env() {
|
||||||
|
ensure_example
|
||||||
|
|
||||||
if [[ -f "$ENV_FILE" ]]; then
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
info ".env existiert bereits – bestehende Konfiguration wird verwendet."
|
info ".env existiert bereits – bestehende Konfiguration wird verwendet."
|
||||||
|
env_hint
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Erzeuge Konfiguration (.env) …"
|
info "Erzeuge Konfiguration (.env) aus den Beispiel-Vorgaben …"
|
||||||
|
|
||||||
local web_port="8080"
|
local web_port="8080"
|
||||||
local admin_user="admin"
|
local admin_user="admin"
|
||||||
@@ -141,6 +185,25 @@ EOF
|
|||||||
GENERATED_ADMIN_USER="$admin_user"
|
GENERATED_ADMIN_USER="$admin_user"
|
||||||
GENERATED_ADMIN_PASS="$admin_pass"
|
GENERATED_ADMIN_PASS="$admin_pass"
|
||||||
GENERATED_WEB_PORT="$web_port"
|
GENERATED_WEB_PORT="$web_port"
|
||||||
|
|
||||||
|
env_hint
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Warten bis das Backend über die Web-UI erreichbar ist --------------------
|
||||||
|
wait_for_backend() {
|
||||||
|
local port="$1" tries=30 i
|
||||||
|
ensure_curl
|
||||||
|
info "Warte darauf, dass das Backend bereit ist …"
|
||||||
|
for ((i = 1; i <= tries; i++)); do
|
||||||
|
if curl -fsS "http://localhost:${port}/api/health" >/dev/null 2>&1; then
|
||||||
|
info "Backend ist erreichbar. ✅"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
warn "Backend antwortet nach ~$((tries * 2))s noch nicht – das erklärt einen 502 in der Web-UI."
|
||||||
|
warn "Bitte die Logs prüfen: ( cd \"$SCRIPT_DIR\" && $COMPOSE logs backend )"
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Start --------------------------------------------------------------------
|
# --- Start --------------------------------------------------------------------
|
||||||
@@ -161,6 +224,8 @@ main() {
|
|||||||
web_port="$(grep -E '^WEB_PORT=' "$ENV_FILE" | cut -d= -f2-)"
|
web_port="$(grep -E '^WEB_PORT=' "$ENV_FILE" | cut -d= -f2-)"
|
||||||
admin_user="$(grep -E '^ADMIN_USERNAME=' "$ENV_FILE" | cut -d= -f2-)"
|
admin_user="$(grep -E '^ADMIN_USERNAME=' "$ENV_FILE" | cut -d= -f2-)"
|
||||||
|
|
||||||
|
wait_for_backend "$web_port" || true
|
||||||
|
|
||||||
echo
|
echo
|
||||||
info "Fertig! 🎉"
|
info "Fertig! 🎉"
|
||||||
echo " Web-UI: http://localhost:${web_port} (bzw. http://<server-ip>:${web_port})"
|
echo " Web-UI: http://localhost:${web_port} (bzw. http://<server-ip>:${web_port})"
|
||||||
|
|||||||
@@ -49,14 +49,22 @@ async function request(path, { method = "GET", body, form } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
const detail =
|
let detail;
|
||||||
(data && data.detail) ||
|
if (data && data.detail) {
|
||||||
(typeof data === "string" ? data : null) ||
|
detail = Array.isArray(data.detail)
|
||||||
`Fehler ${resp.status}`;
|
? data.detail.map((d) => d.msg).join(", ")
|
||||||
throw new ApiError(
|
: data.detail;
|
||||||
Array.isArray(detail) ? detail.map((d) => d.msg).join(", ") : detail,
|
} else if (resp.status === 502 || resp.status === 503 || resp.status === 504) {
|
||||||
resp.status
|
detail = `Server nicht erreichbar (${resp.status}). Läuft der Backend-Dienst? Prüfe: docker compose logs backend`;
|
||||||
);
|
} else if (typeof data === "string" && data.trim().startsWith("<")) {
|
||||||
|
// HTML-Fehlerseite (z.B. von nginx) nicht roh anzeigen
|
||||||
|
detail = `Server-Fehler (${resp.status}).`;
|
||||||
|
} else if (typeof data === "string" && data) {
|
||||||
|
detail = data;
|
||||||
|
} else {
|
||||||
|
detail = `Fehler ${resp.status}`;
|
||||||
|
}
|
||||||
|
throw new ApiError(detail, resp.status);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user