374 lines
9.9 KiB
Bash
Executable File
374 lines
9.9 KiB
Bash
Executable File
#!/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 ""
|