#!/usr/bin/env bash # # Project-Good – Installer für selfhostbare Lebensmittel-Lagerverwaltung. # Installiert bei Bedarf Docker und startet den kompletten Stack via Docker Compose. # # Nutzung: # ./install.sh interaktiv (fragt Admin-Passwort etc.) # ./install.sh --yes nicht-interaktiv (erzeugt Zufalls-Admin-Passwort) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="$SCRIPT_DIR/.env" EXAMPLE_FILE="$SCRIPT_DIR/.env.example" ASSUME_YES=0 [[ "${1:-}" == "--yes" || "${1:-}" == "-y" ]] && ASSUME_YES=1 info() { printf '\033[0;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[0;33m[!]\033[0m %s\n' "$*"; } error() { printf '\033[0;31m[x]\033[0m %s\n' "$*" >&2; } rand() { LC_ALL=C tr -dc 'A-Za-z0-9' /dev/null 2>&1; then SUDO="sudo" else SUDO="" fi # --- Paketinstallation (paketmanager-unabhängig) ------------------------------ pkg_install() { local pkg="$1" if command -v apt-get >/dev/null 2>&1; then $SUDO apt-get update -y && $SUDO apt-get install -y "$pkg" elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y "$pkg" elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y "$pkg" elif command -v apk >/dev/null 2>&1; then $SUDO apk add --no-cache "$pkg" elif command -v pacman >/dev/null 2>&1; then $SUDO pacman -Sy --noconfirm "$pkg" elif command -v zypper >/dev/null 2>&1; then $SUDO zypper install -y "$pkg" else return 1 fi } # --- curl sicherstellen (wird für die Docker-Installation gebraucht) ---------- ensure_curl() { if command -v curl >/dev/null 2>&1; then return 0 fi warn "curl ist nicht installiert – wird jetzt automatisch installiert …" if ! pkg_install curl; then error "curl konnte nicht installiert werden (kein bekannter Paketmanager gefunden). Bitte curl manuell installieren und erneut ausführen." exit 1 fi command -v curl >/dev/null 2>&1 || { error "curl-Installation fehlgeschlagen."; exit 1; } info "curl installiert." } # --- Docker prüfen / installieren --------------------------------------------- ensure_docker() { if command -v docker >/dev/null 2>&1; then info "Docker gefunden: $(docker --version)" else warn "Docker ist nicht installiert." if [[ "$ASSUME_YES" -eq 1 ]]; then reply="j" else read -r -p "Soll Docker jetzt automatisch installiert werden? [J/n] " reply reply="${reply:-j}" fi case "$reply" in [JjYy]*) ensure_curl info "Installiere Docker über get.docker.com …" curl -fsSL https://get.docker.com | sh ;; *) error "Docker wird benötigt. Bitte manuell installieren und erneut ausführen." exit 1 ;; esac fi # docker compose (v2) oder docker-compose (v1) ermitteln if docker compose version >/dev/null 2>&1; then COMPOSE="docker compose" elif command -v docker-compose >/dev/null 2>&1; then COMPOSE="docker-compose" else error "Docker Compose wurde nicht gefunden. Bitte Docker Compose v2 installieren." exit 1 fi info "Verwende: $COMPOSE" if ! docker info >/dev/null 2>&1; then error "Docker-Daemon nicht erreichbar. Läuft Docker? Ggf. 'sudo' nötig oder Nutzer zur 'docker'-Gruppe hinzufügen." exit 1 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 ------------------------------------------------------------ create_env() { ensure_example if [[ -f "$ENV_FILE" ]]; then info ".env existiert bereits – bestehende Konfiguration wird verwendet." env_hint return fi info "Erzeuge Konfiguration (.env) aus den Beispiel-Vorgaben …" local web_port="8080" local admin_user="admin" local admin_pass admin_pass="$(rand 16)" if [[ "$ASSUME_YES" -eq 0 ]]; then read -r -p "Port für die Web-UI [8080]: " input; web_port="${input:-8080}" read -r -p "Admin-Benutzername [admin]: " input; admin_user="${input:-admin}" read -r -s -p "Admin-Passwort [leer = zufällig generieren]: " input; echo [[ -n "$input" ]] && admin_pass="$input" fi cat > "$ENV_FILE" </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 -------------------------------------------------------------------- main() { info "Project-Good-Installer startet …" ensure_docker create_env info "Baue Container (das kann beim ersten Mal einige Minuten dauern) …" ( cd "$SCRIPT_DIR" && $COMPOSE --env-file "$ENV_FILE" build ) info "Starte Dienste …" ( cd "$SCRIPT_DIR" && $COMPOSE --env-file "$ENV_FILE" up -d ) # Werte aus .env lesen (auch bei bestehender .env) # shellcheck disable=SC1090 local web_port admin_user web_port="$(grep -E '^WEB_PORT=' "$ENV_FILE" | cut -d= -f2-)" admin_user="$(grep -E '^ADMIN_USERNAME=' "$ENV_FILE" | cut -d= -f2-)" wait_for_backend "$web_port" || true echo info "Fertig! 🎉" echo " Web-UI: http://localhost:${web_port} (bzw. http://:${web_port})" echo " Admin-Login: ${admin_user}" if [[ -n "${GENERATED_ADMIN_PASS:-}" ]]; then echo " Admin-Passwort: ${GENERATED_ADMIN_PASS}" warn "Bitte dieses Passwort notieren – es wird nicht erneut angezeigt." else echo " Admin-Passwort: (wie in .env gesetzt)" fi echo echo " Logs: ( cd $SCRIPT_DIR && $COMPOSE logs -f )" echo " Stoppen: ( cd $SCRIPT_DIR && $COMPOSE down )" } main "$@"