#!/bin/bash # Callio Self-Installer v1.0 # Usage: # Mit Token (config-server): sudo bash install.sh --token KUNDE_TOKEN # Direkt (ohne config-server): sudo bash install.sh --subdomain kanzlei-mueller --name "Kanzlei Müller" --branche kanzlei set -euo pipefail CALLIO_VERSION="1.0.0" INSTALL_DIR="/opt/callio" CONFIG_API="https://install.getcallio.de/api/config" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ── Argumente ──────────────────────────────────────────────────────────────── TOKEN="" SUBDOMAIN="" KUNDE_NAME="" BRANCHE="" while [[ $# -gt 0 ]]; do case $1 in --token) TOKEN="$2"; shift 2 ;; --subdomain) SUBDOMAIN="$2"; shift 2 ;; --name) KUNDE_NAME="$2";shift 2 ;; --branche) BRANCHE="$2"; shift 2 ;; *) echo "Unbekannter Parameter: $1"; exit 1 ;; esac done echo "" echo "╔══════════════════════════════════════╗" echo "║ Callio Installer v${CALLIO_VERSION} ║" echo "╚══════════════════════════════════════╝" echo "" # ── Root prüfen ────────────────────────────────────────────────────────────── if [ "$EUID" -ne 0 ]; then echo "❌ Bitte als root ausführen: sudo bash install.sh" exit 1 fi # ── System prüfen ──────────────────────────────────────────────────────────── echo "📋 System wird geprüft..." RAM=$(free -m | awk '/^Mem:/{print $2}') if [[ "$RAM" -lt 900 ]]; then echo "❌ Mindestens 1GB RAM benötigt. Gefunden: ${RAM}MB" exit 1 fi echo "✅ System OK — RAM: ${RAM}MB" # ── Docker installieren ─────────────────────────────────────────────────────── if ! command -v docker &> /dev/null; then echo "📦 Docker wird installiert..." curl -fsSL https://get.docker.com | sh systemctl enable docker systemctl start docker echo "✅ Docker installiert" else echo "✅ Docker: $(docker --version | cut -d' ' -f3 | tr -d ',')" fi # ── Verzeichnisse anlegen ───────────────────────────────────────────────────── echo "📁 Verzeichnisse anlegen..." mkdir -p "$INSTALL_DIR" chmod 700 "$INSTALL_DIR" # ── Dateien in korrekte Struktur bringen ───────────────────────────────────── # Erwartete Struktur auf dem VPS: # $INSTALL_DIR/ # callio-installer/ ← docker-compose.yml + services/ # webhook-server/ ← Dockerfile + routes/ + services/ # callio-installer/ ins INSTALL_DIR bringen CALLIO_INSTALLER_DEST="$INSTALL_DIR/callio-installer" if [ ! -d "$CALLIO_INSTALLER_DEST" ]; then echo "📋 callio-installer/ nach $CALLIO_INSTALLER_DEST kopieren..." cp -r "$SCRIPT_DIR" "$CALLIO_INSTALLER_DEST" fi # webhook-server/ ins INSTALL_DIR bringen (Bruder-Verzeichnis im Repo) WEBHOOK_SRC="$(dirname "$SCRIPT_DIR")/webhook-server" WEBHOOK_DEST="$INSTALL_DIR/webhook-server" if [ -d "$WEBHOOK_SRC" ] && [ ! -d "$WEBHOOK_DEST" ]; then echo "📋 webhook-server/ nach $WEBHOOK_DEST kopieren..." cp -r "$WEBHOOK_SRC" "$WEBHOOK_DEST" fi # ── Config laden (Token-Modus) ───────────────────────────────────────────── if [ -n "$TOKEN" ]; then echo "🔑 Lade Konfiguration für Token..." CONFIG_RESPONSE=$(curl -fsSL \ -H "Authorization: Bearer $TOKEN" \ "${CONFIG_API}/${TOKEN}" 2>/dev/null || echo "") if [ -z "$CONFIG_RESPONSE" ] || echo "$CONFIG_RESPONSE" | grep -q '"error"'; then echo "❌ Ungültiger Token oder Server nicht erreichbar" exit 1 fi SUBDOMAIN=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('subdomain',''))") KUNDE_NAME=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('kunde_name',''))") BRANCHE=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('branche','kanzlei'))") BOT_TOKEN=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('telegram_bot_token',''))") OPENROUTER=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('openrouter_api_key',''))") CUSTOMER_EMAIL=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('customer_email',''))") CUSTOMER_FIRMA=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('customer_firma',''))") CUSTOMER_BRANCHE=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('customer_branche',''))") CF_TOKEN=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('cloudflare_tunnel_token',''))") DASHBOARD_SECRET=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('dashboard_secret',''))") BUSINESS_TYPE=$(echo "$CONFIG_RESPONSE" | python3 -c "import sys,json;print(json.load(sys.stdin).get('business_type','Büro'))") # Minimal-.env schreiben (Setup-Wizard ergänzt den Rest) cat > "$INSTALL_DIR/.env" << EOF SUBDOMAIN=$SUBDOMAIN KUNDE_NAME=$KUNDE_NAME BUSINESS_TYPE=$BUSINESS_TYPE TELEGRAM_BOT_TOKEN=$BOT_TOKEN TELEGRAM_BEN_CHAT_ID=7692268130 OPENROUTER_API_KEY=$OPENROUTER INSTALL_DIR=$INSTALL_DIR CALLIO_VERSION=$CALLIO_VERSION # Kunden-Metadaten (vom Config-Server) SETUP_TOKEN=$TOKEN CUSTOMER_EMAIL=$CUSTOMER_EMAIL CUSTOMER_FIRMA=$CUSTOMER_FIRMA CUSTOMER_BRANCHE=$CUSTOMER_BRANCHE CLOUDFLARE_TUNNEL_TOKEN=$CF_TOKEN DASHBOARD_SECRET=$DASHBOARD_SECRET EOF # Token invalidieren curl -s -X DELETE "${CONFIG_API}/${TOKEN}" > /dev/null 2>&1 || true echo "🔒 Token invalidiert" fi # ── .env vorbereiten (Direkt-Modus) ────────────────────────────────────────── if [ ! -f "$INSTALL_DIR/.env" ]; then cp "$INSTALL_DIR/.env.template" "$INSTALL_DIR/.env" 2>/dev/null || cat > "$INSTALL_DIR/.env" << EOF SUBDOMAIN=${SUBDOMAIN:-} KUNDE_NAME=${KUNDE_NAME:-} BUSINESS_TYPE=Büro TELEGRAM_BOT_TOKEN=8725120891:AAHoWeZf6mxMUHprFgnEoct-bix8BznpMNw TELEGRAM_BEN_CHAT_ID=7692268130 OPENROUTER_API_KEY=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 INSTALL_DIR=$INSTALL_DIR CALLIO_VERSION=$CALLIO_VERSION EOF fi chmod 600 "$INSTALL_DIR/.env" # ── UFW Firewall ────────────────────────────────────────────────────────────── if command -v ufw &> /dev/null; then echo "🔒 Firewall konfigurieren..." ufw --force reset > /dev/null 2>&1 ufw default deny incoming > /dev/null ufw default allow outgoing > /dev/null ufw allow 22/tcp > /dev/null # SSH ufw allow 8080/tcp > /dev/null # Setup-Wizard (temporär, wird nach Setup geschlossen) ufw --force enable > /dev/null echo "✅ Firewall: Port 22 (SSH) + 8080 (Setup) offen" fi # ── VPS-IP ermitteln ────────────────────────────────────────────────────────── SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || curl -s icanhazip.com 2>/dev/null || echo "DEINE-VPS-IP") # ── Setup-Container starten ─────────────────────────────────────────────────── echo "" echo "🐳 Setup-Interface wird gestartet..." cd "$INSTALL_DIR/callio-installer" # .env für docker compose initial befüllen (Setup-Wizard überschreibt .env später) export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs) 2>/dev/null || true docker compose --profile setup up -d --build callio-setup 2>&1 | tail -5 echo "" echo "╔══════════════════════════════════════════════╗" echo "║ 🚀 Setup-Interface bereit! ║" echo "║ ║" echo "║ Öffne im Browser: ║" echo "║ http://${SERVER_IP}:8080/setup ║" echo "║ ║" echo "║ (Nur während Setup aktiv — danach gesperrt) ║" echo "╚══════════════════════════════════════════════╝" echo "" echo "Warte auf Setup-Abschluss..." # Warte bis .env-complete Flag gesetzt wird (vom Setup-Wizard) while [ ! -f "$INSTALL_DIR/.setup-complete" ]; do sleep 5 done echo "" echo "✅ Setup abgeschlossen! Callio startet..." # Port 8080 in UFW schließen if command -v ufw &> /dev/null; then ufw delete allow 8080/tcp > /dev/null 2>&1 || true echo "🔒 Port 8080 geschlossen" fi echo "" echo "╔═════════════════════════════════════════════╗" echo "║ ✅ Callio erfolgreich installiert! ║" echo "║ ║" echo "║ Logs: docker compose logs -f ║" echo "║ Stop: docker compose down ║" echo "║ Start: docker compose up -d ║" echo "╚═════════════════════════════════════════════╝"