#!/bin/bash # # Crumbforest Native Installation Script # Installs Crumbforest directly on Linux server without Docker # # Requirements: # - Debian/Ubuntu Linux # - Python 3.11+ # - MariaDB/MySQL # - NGINX # - Qdrant running on localhost:6333 # # Run as root or with sudo # set -e # Exit on error # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration INSTALL_DIR="/opt/crumbforest" APP_USER="crumbforest" APP_GROUP="crumbforest" LOG_DIR="/var/log/crumbforest" SYSTEMD_DIR="/etc/systemd/system" NGINX_SITES="/etc/nginx/sites-available" NGINX_ENABLED="/etc/nginx/sites-enabled" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # Functions print_success() { echo -e "${GREEN}✓${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } print_info() { echo -e "${BLUE}ℹ${NC} $1" } check_root() { if [ "$EUID" -ne 0 ]; then print_error "Bitte als root ausführen oder sudo verwenden" exit 1 fi } check_command() { if command -v $1 &> /dev/null; then print_success "$1 ist installiert" return 0 else print_error "$1 ist NICHT installiert" return 1 fi } # Banner echo "============================================" echo "🦉 Crumbforest Native Installation" echo "============================================" echo "" print_info "Installation nach: $INSTALL_DIR" print_info "App User: $APP_USER" print_info "Logs: $LOG_DIR" echo "" # Step 1: Check prerequisites echo "=== Step 1: Prerequisites Check ===" echo "" check_root all_ok=true if ! check_command python3; then print_error "Python 3 ist erforderlich!" all_ok=false else PYTHON_VERSION=$(python3 --version | awk '{print $2}') print_info "Python Version: $PYTHON_VERSION" fi if ! check_command pip3; then print_error "pip3 ist erforderlich!" all_ok=false fi if ! check_command nginx; then print_error "NGINX ist erforderlich!" all_ok=false fi if ! check_command mysql || ! check_command mariadb; then print_warning "MySQL/MariaDB CLI nicht gefunden (Server könnte trotzdem laufen)" fi # Check if Qdrant is running if curl -s http://localhost:6333/collections > /dev/null 2>&1; then print_success "Qdrant läuft auf localhost:6333" else print_warning "Qdrant nicht erreichbar auf localhost:6333 (wird später benötigt)" fi if [ "$all_ok" = false ]; then print_error "Bitte installiere die fehlenden Dependencies" exit 1 fi echo "" # Step 2: Create system user echo "=== Step 2: System User Setup ===" echo "" if id "$APP_USER" &>/dev/null; then print_warning "User $APP_USER existiert bereits" else useradd --system --no-create-home --shell /bin/false "$APP_USER" print_success "User $APP_USER erstellt" fi echo "" # Step 3: Create directory structure echo "=== Step 3: Directory Structure ===" echo "" print_info "Erstelle Verzeichnisse..." mkdir -p "$INSTALL_DIR"/{app,docs,logs,venv} mkdir -p "$LOG_DIR" print_success "Verzeichnisse erstellt" # Step 4: Copy application files echo "" echo "=== Step 4: Copy Application Files ===" echo "" print_info "Kopiere Application Code..." cp -r "$PROJECT_ROOT/app"/* "$INSTALL_DIR/app/" print_success "App Code kopiert" print_info "Kopiere Dokumentation..." # Check for docs_git (preferred source) or docs DOCS_SOURCE="" if [ -d "$PROJECT_ROOT/docs_git" ]; then DOCS_SOURCE="$PROJECT_ROOT/docs_git" print_info "Nutze docs_git als Quelle" elif [ -d "$PROJECT_ROOT/docs" ]; then DOCS_SOURCE="$PROJECT_ROOT/docs" print_info "Nutze docs als Quelle" fi if [ -n "$DOCS_SOURCE" ]; then cp -r "$DOCS_SOURCE"/* "$INSTALL_DIR/docs/" || true # Fix: Create symlink in app/docs so hardcoded paths work (mimics Docker volume) if [ ! -L "$INSTALL_DIR/app/docs" ]; then # Remove empty dir if it exists if [ -d "$INSTALL_DIR/app/docs" ]; then rmdir "$INSTALL_DIR/app/docs" 2>/dev/null || true; fi ln -sf "$INSTALL_DIR/docs" "$INSTALL_DIR/app/docs" print_success "Docs Symlink erstellt (app/docs -> ../docs)" fi print_success "Docs kopiert von $(basename "$DOCS_SOURCE")" else print_warning "Kein Dokumentations-Verzeichnis (docs_git oder docs) gefunden" fi print_info "Kopiere Root Dokumentation..." cp "$PROJECT_ROOT"/*.md "$INSTALL_DIR/docs/" 2>/dev/null || true print_info "Kopiere Konfiguration..." if [ -f "$PROJECT_ROOT/crumbforest_config.json" ]; then cp "$PROJECT_ROOT/crumbforest_config.json" "$INSTALL_DIR/app/" print_success "Config kopiert" fi echo "" # Step 5: Python virtual environment echo "=== Step 5: Python Virtual Environment ===" echo "" print_info "Erstelle Virtual Environment..." python3 -m venv "$INSTALL_DIR/venv" print_success "venv erstellt" print_info "Installiere Python Dependencies..." "$INSTALL_DIR/venv/bin/pip" install --upgrade pip "$INSTALL_DIR/venv/bin/pip" install -r "$PROJECT_ROOT/app/requirements.txt" print_success "Dependencies installiert" echo "" # Step 6: Environment configuration echo "=== Step 6: Environment Configuration ===" echo "" if [ ! -f "$INSTALL_DIR/.env" ]; then print_info "Erstelle .env aus Template..." cp "$SCRIPT_DIR/env.production.template" "$INSTALL_DIR/.env" # Generate random secrets APP_SECRET=$(openssl rand -hex 32) SECRET_KEY=$(openssl rand -hex 32) DB_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-20) # Replace placeholders sed -i "s/CHANGE_ME_TO_RANDOM_64_CHAR_STRING/$APP_SECRET/g" "$INSTALL_DIR/.env" sed -i "s/CHANGE_ME_TO_SECURE_PASSWORD/$DB_PASSWORD/g" "$INSTALL_DIR/.env" print_success ".env erstellt" print_warning "WICHTIG: Bitte API Keys in $INSTALL_DIR/.env eintragen!" else print_warning ".env existiert bereits - wird nicht überschrieben" fi echo "" # Step 7: Set permissions echo "=== Step 7: Permissions ===" echo "" print_info "Setze Besitzer und Rechte..." chown -R "$APP_USER:$APP_GROUP" "$INSTALL_DIR" chown -R "$APP_USER:$APP_GROUP" "$LOG_DIR" chmod 750 "$INSTALL_DIR" chmod 750 "$INSTALL_DIR/app" chmod 600 "$INSTALL_DIR/.env" # Sensitive data! chmod -R 755 "$INSTALL_DIR/app/static" 2>/dev/null || true # Fix: Symlink .env to app directory for Pydantic Settings if [ ! -L "$INSTALL_DIR/app/.env" ]; then print_info "Erstelle Symlink für .env in App-Verzeichnis..." ln -sf "$INSTALL_DIR/.env" "$INSTALL_DIR/app/.env" fi chown -h $APP_USER:$APP_GROUP "$INSTALL_DIR/app/.env" print_success "Permissions gesetzt" echo "" # Step 8: Install systemd services echo "=== Step 8: systemd Services ===" echo "" print_info "Installiere systemd Service Files..." cp "$SCRIPT_DIR/systemd/crumbforest.service" "$SYSTEMD_DIR/" cp "$SCRIPT_DIR/systemd/crumbforest-indexing.service" "$SYSTEMD_DIR/" print_success "Service Files kopiert" print_info "Aktiviere Services..." systemctl daemon-reload systemctl enable crumbforest.service systemctl enable crumbforest-indexing.service print_success "Services aktiviert" echo "" # Step 9: Install NGINX configuration echo "=== Step 9: NGINX Configuration ===" echo "" print_info "Installiere NGINX Config..." cp "$SCRIPT_DIR/nginx/crumbforest.nginx.conf" "$NGINX_SITES/crumbforest" cp "$SCRIPT_DIR/nginx/crumbforest-locations.conf" "$NGINX_SITES/" print_success "NGINX Config installiert" # Enable site if [ ! -L "$NGINX_ENABLED/crumbforest" ]; then ln -s "$NGINX_SITES/crumbforest" "$NGINX_ENABLED/crumbforest" print_success "NGINX Site aktiviert" else print_warning "NGINX Site bereits aktiviert" fi # Test NGINX config if nginx -t > /dev/null 2>&1; then print_success "NGINX Config Test OK" else print_error "NGINX Config Test FAILED - bitte prüfen!" fi echo "" # Step 10: Install Crumb Doktor (Global Tool) echo "=== Step 10: Install Crumb Doktor ===" echo "" DOKTOR_SRC="$PROJECT_ROOT/crumbpages-doktor.sh" DOKTOR_DEST="/usr/local/bin/crumb-doktor" if [ -f "$DOKTOR_SRC" ]; then print_info "Installiere crumbpages-doktor.sh als globalen Befehl..." cp "$DOKTOR_SRC" "$DOKTOR_DEST" chmod +x "$DOKTOR_DEST" print_success "Installiert als: $DOKTOR_DEST" print_info "Du kannst jetzt überall 'crumb-doktor' verwenden!" else print_warning "crumbpages-doktor.sh nicht gefunden im Repo-Root" fi echo "" # Step 11: Database setup reminder echo "=== Step 11: Database Setup ===" echo "" print_warning "Datenbank muss manuell eingerichtet werden!" echo "" echo "Bitte folgende SQL-Befehle ausführen:" echo "" echo " mysql -u root -p" echo " CREATE DATABASE IF NOT EXISTS crumbforest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" echo " CREATE USER IF NOT EXISTS 'crumb_prod'@'localhost' IDENTIFIED BY 'YOUR_PASSWORD';" echo " GRANT ALL PRIVILEGES ON crumbforest.* TO 'crumb_prod'@'localhost';" echo " FLUSH PRIVILEGES;" echo "" echo "Dann Datenbank-Schema importieren:" echo " mysql -u crumb_prod -p crumbforest < $PROJECT_ROOT/compose/init/01_schema.sql" echo "" read -p "Drücke ENTER wenn Datenbank eingerichtet ist..." echo "" # Summary echo "============================================" echo "✓ Installation abgeschlossen!" echo "============================================" echo "" echo "Nächste Schritte:" echo "" echo "1. API Keys eintragen:" echo " sudo nano $INSTALL_DIR/.env" echo "" echo "2. Services starten:" echo " sudo systemctl start crumbforest-indexing # Einmalig" echo " sudo systemctl start crumbforest" echo "" echo "3. NGINX neu laden:" echo " sudo systemctl reload nginx" echo "" echo "4. Status prüfen:" echo " sudo systemctl status crumbforest" echo " sudo journalctl -u crumbforest -f" echo "" echo "5. Health Check:" echo " curl http://localhost:8000/health" echo "" echo "URLs:" echo " - App: http://crumbforest.194-164-194-191.sslip.io" echo " - IP: http://194.164.194.191" echo "" echo "Installation Location: $INSTALL_DIR" echo "Logs: $LOG_DIR" echo "systemd Logs: journalctl -u crumbforest" echo "" echo "Wuuuuhuuu! 🦉💚" echo ""