#!/bin/bash ####################################################################### # MYP AIO-Installer - Python & Node.js Module # # Dieses Modul behandelt die Installation von: # - Python und pip mit --break-system-packages # - Node.js und npm # - Python-Abhängigkeiten aus requirements.txt # - Node.js-Abhängigkeiten aus package.json # - Build-Prozess für Frontend-Assets ####################################################################### # Funktionsdeklarationen für Python & Node.js Setup install_python_dependencies() { log "INFO" "=== PYTHON-ABHÄNGIGKEITEN INSTALLIEREN ===" # Python-Version überprüfen verify_python_installation # pip konfigurieren configure_pip # Virtuelle Umgebung erstellen (optional) # create_python_venv # Requirements installieren install_python_requirements # Python-Installation verifizieren verify_python_dependencies log "INFO" "Python-Abhängigkeiten Installation abgeschlossen" } install_node_dependencies() { log "INFO" "=== NODE.JS-ABHÄNGIGKEITEN INSTALLIEREN ===" # Node.js installieren install_nodejs # npm konfigurieren configure_npm # Package-Dependencies installieren install_npm_packages # Build-Prozess durchführen build_frontend_assets # Node.js-Installation verifizieren verify_node_dependencies log "INFO" "Node.js-Abhängigkeiten Installation abgeschlossen" } verify_python_installation() { log "INFO" "Überprüfe Python-Installation..." # Python3 Version prüfen if ! command -v python3 >/dev/null 2>&1; then log "ERROR" "Python3 ist nicht installiert" return 1 fi local python_version=$(python3 --version | cut -d' ' -f2) log "INFO" "Python-Version: $python_version" # Mindestversion prüfen (Python 3.8+) if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"; then log "ERROR" "Python-Version zu alt (mindestens 3.8 erforderlich)" return 1 fi # pip überprüfen if ! command -v pip3 >/dev/null 2>&1; then log "INFO" "pip3 nicht gefunden, installiere pip..." apt-get install -y python3-pip fi local pip_version=$(pip3 --version | cut -d' ' -f2) log "INFO" "pip-Version: $pip_version" log "INFO" "Python-Installation verifiziert" } configure_pip() { log "INFO" "Konfiguriere pip..." # pip Konfigurationsverzeichnis erstellen mkdir -p "/home/$PROJECT_USER/.config/pip" mkdir -p "/root/.config/pip" # pip.conf für bessere Performance und Sicherheit cat > "/home/$PROJECT_USER/.config/pip/pip.conf" << 'EOF' [global] timeout = 60 retries = 5 trusted-host = pypi.org pypi.python.org files.pythonhosted.org [install] upgrade-strategy = only-if-needed break-system-packages = true EOF # Kopiere für root cp "/home/$PROJECT_USER/.config/pip/pip.conf" "/root/.config/pip/pip.conf" # Berechtigungen setzen chown -R "$PROJECT_USER:$PROJECT_GROUP" "/home/$PROJECT_USER/.config" # pip auf neueste Version aktualisieren log "INFO" "Aktualisiere pip auf neueste Version..." pip3 install --upgrade pip --break-system-packages || { log "WARN" "pip-Update hatte Probleme" } log "INFO" "pip konfiguriert" } create_python_venv() { log "INFO" "Erstelle Python Virtual Environment..." local venv_path="$INSTALL_PATH/venv" # Virtual Environment erstellen python3 -m venv "$venv_path" || { log "ERROR" "Virtual Environment konnte nicht erstellt werden" return 1 } # Aktivierungsscript für systemd erstellen cat > "$INSTALL_PATH/activate_venv.sh" << EOF #!/bin/bash source "$venv_path/bin/activate" exec "\$@" EOF chmod +x "$INSTALL_PATH/activate_venv.sh" # Virtual Environment in systemd-Service nutzen export PYTHON_VENV_PATH="$venv_path" log "INFO" "Python Virtual Environment erstellt: $venv_path" } install_python_requirements() { log "INFO" "Installiere Python-Requirements..." # Requirements-Datei prüfen local requirements_file="$INSTALL_PATH/requirements.txt" if [[ ! -f "$requirements_file" ]]; then log "ERROR" "Requirements-Datei nicht gefunden: $requirements_file" return 1 fi log "INFO" "Gefundene Requirements-Datei: $requirements_file" # Anzahl der Requirements anzeigen local req_count=$(grep -v '^#' "$requirements_file" | grep -v '^$' | wc -l) log "INFO" "Installiere $req_count Python-Packages..." # Installation mit --break-system-packages log "INFO" "Führe pip install mit --break-system-packages aus..." # Erstelle temporäres Install-Script für bessere Kontrolle cat > "/tmp/pip_install.sh" << EOF #!/bin/bash set -e cd "$INSTALL_PATH" # Upgrade pip falls nötig pip3 install --upgrade pip --break-system-packages # Installiere Requirements pip3 install -r requirements.txt --break-system-packages --no-cache-dir # Erstelle Freeze-Liste für Debugging pip3 freeze --break-system-packages > installed_packages.txt EOF chmod +x "/tmp/pip_install.sh" # Installation ausführen if /tmp/pip_install.sh; then log "INFO" "Python-Requirements erfolgreich installiert" else log "ERROR" "Python-Requirements Installation fehlgeschlagen" # Versuche einzelne Installation bei Fehlern log "INFO" "Versuche einzelne Package-Installation..." install_requirements_individually "$requirements_file" fi # Cleanup rm -f "/tmp/pip_install.sh" log "INFO" "Python-Requirements Installation abgeschlossen" } install_requirements_individually() { local requirements_file="$1" log "INFO" "Installiere Requirements einzeln..." # Lese Requirements und installiere einzeln while IFS= read -r line; do # Überspringe Kommentare und leere Zeilen [[ "$line" =~ ^#.*$ ]] && continue [[ -z "$line" ]] && continue # Package-Name extrahieren local package=$(echo "$line" | cut -d'=' -f1 | cut -d'>' -f1 | cut -d'<' -f1 | tr -d ' ') if [[ -n "$package" ]]; then log "INFO" "Installiere: $package" if pip3 install "$line" --break-system-packages --no-cache-dir; then log "INFO" "✓ $package installiert" else log "WARN" "✗ $package Installation fehlgeschlagen" fi fi done < "$requirements_file" } install_nodejs() { log "INFO" "Installiere Node.js..." # Prüfe ob Node.js bereits installiert ist if command -v node >/dev/null 2>&1; then local current_version=$(node --version) log "INFO" "Node.js bereits installiert: $current_version" # Prüfe Version (mindestens v16) if node -e "process.exit(parseInt(process.version.slice(1)) >= 16 ? 0 : 1)"; then log "INFO" "Node.js Version ist ausreichend" return 0 else log "WARN" "Node.js Version zu alt, aktualisiere..." fi fi # Versuche Installation über APT (NodeSource Repository) if install_nodejs_apt; then log "INFO" "Node.js über APT installiert" return 0 fi # Fallback: Installation über NodeSource Script if install_nodejs_nodesource; then log "INFO" "Node.js über NodeSource installiert" return 0 fi # Letzter Fallback: Snap if install_nodejs_snap; then log "INFO" "Node.js über Snap installiert" return 0 fi log "ERROR" "Node.js Installation fehlgeschlagen" return 1 } install_nodejs_apt() { log "INFO" "Versuche Node.js Installation über APT..." # APT-Cache aktualisieren apt-get update -y # Node.js installieren if DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs npm; then # Version prüfen local node_version=$(node --version 2>/dev/null || echo "v0.0.0") local npm_version=$(npm --version 2>/dev/null || echo "0.0.0") log "INFO" "Node.js installiert: $node_version" log "INFO" "npm installiert: $npm_version" return 0 else log "WARN" "APT Node.js Installation fehlgeschlagen" return 1 fi } install_nodejs_nodesource() { log "INFO" "Versuche Node.js Installation über NodeSource..." # NodeSource Setup-Script herunterladen und ausführen if curl -fsSL https://deb.nodesource.com/setup_18.x | bash -; then # Node.js installieren if DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs; then log "INFO" "Node.js über NodeSource erfolgreich installiert" return 0 fi fi log "WARN" "NodeSource Installation fehlgeschlagen" return 1 } install_nodejs_snap() { log "INFO" "Versuche Node.js Installation über Snap..." # Snap installieren falls nicht vorhanden if ! command -v snap >/dev/null 2>&1; then DEBIAN_FRONTEND=noninteractive apt-get install -y snapd systemctl enable --now snapd.socket sleep 5 fi # Node.js über Snap installieren if snap install node --classic; then # Symlinks erstellen ln -sf /snap/bin/node /usr/local/bin/node ln -sf /snap/bin/npm /usr/local/bin/npm log "INFO" "Node.js über Snap erfolgreich installiert" return 0 fi log "WARN" "Snap Node.js Installation fehlgeschlagen" return 1 } configure_npm() { log "INFO" "Konfiguriere npm..." # npm Konfiguration für bessere Performance npm config set audit-level moderate npm config set fund false npm config set update-notifier false npm config set prefer-offline true # Cache-Verzeichnis setzen npm config set cache "/home/$PROJECT_USER/.npm" # Registry auf https setzen npm config set registry https://registry.npmjs.org/ # Timeout erhöhen npm config set timeout 300000 # npm auf neueste Version aktualisieren log "INFO" "Aktualisiere npm auf neueste Version..." npm install -g npm@latest || { log "WARN" "npm-Update hatte Probleme" } # Berechtigungen für npm-Cache chown -R "$PROJECT_USER:$PROJECT_GROUP" "/home/$PROJECT_USER/.npm" 2>/dev/null || true log "INFO" "npm konfiguriert" } install_npm_packages() { log "INFO" "Installiere npm-Packages..." # Package.json prüfen local package_json="$INSTALL_PATH/package.json" if [[ ! -f "$package_json" ]]; then log "WARN" "package.json nicht gefunden: $package_json" log "INFO" "Erstelle minimale package.json..." create_minimal_package_json fi # Wechsle ins Installationsverzeichnis cd "$INSTALL_PATH" # npm install ausführen log "INFO" "Führe npm install aus..." if npm install --production; then log "INFO" "npm-Packages erfolgreich installiert" else log "WARN" "npm install hatte Probleme, versuche --force..." if npm install --production --force; then log "INFO" "npm-Packages mit --force installiert" else log "ERROR" "npm install fehlgeschlagen" return 1 fi fi # Package-Liste für Debugging npm list --depth=0 > npm_packages.txt 2>/dev/null || true log "INFO" "npm-Packages Installation abgeschlossen" } create_minimal_package_json() { log "INFO" "Erstelle minimale package.json..." cat > "$INSTALL_PATH/package.json" << 'EOF' { "name": "myp-system", "version": "1.0.0", "description": "Mercedes-Benz 3D Printer Management System", "main": "app.py", "scripts": { "build:css": "tailwindcss -i static/css/input.css -o static/css/output.css --minify", "watch:css": "tailwindcss -i static/css/input.css -o static/css/output.css --watch", "dev": "npm run watch:css", "build": "npm run build:css" }, "dependencies": { "tailwindcss": "^3.3.0", "@tailwindcss/forms": "^0.5.0", "@tailwindcss/typography": "^0.5.0", "autoprefixer": "^10.4.0", "postcss": "^8.4.0" }, "keywords": ["3d-printing", "management", "mercedes-benz"], "author": "Till Tomczak", "license": "Proprietary" } EOF log "INFO" "Minimale package.json erstellt" } build_frontend_assets() { log "INFO" "Baue Frontend-Assets..." cd "$INSTALL_PATH" # Prüfe ob build-Script verfügbar ist if npm run build --silent 2>/dev/null; then log "INFO" "Frontend-Assets mit npm run build erstellt" elif command -v tailwindcss >/dev/null 2>&1; then log "INFO" "Verwende direkte TailwindCSS-Kommandos..." build_tailwind_directly else log "WARN" "Kein Build-System verfügbar, überspringe Asset-Build" return 0 fi # Komprimiere Assets für Produktionsumgebung compress_assets log "INFO" "Frontend-Assets Build abgeschlossen" } build_tailwind_directly() { log "INFO" "Baue TailwindCSS direkt..." # Input-CSS erstellen falls nicht vorhanden if [[ ! -f "static/css/input.css" ]]; then mkdir -p "static/css" cat > "static/css/input.css" << 'EOF' @tailwind base; @tailwind components; @tailwind utilities; /* Custom MYP Styles */ .btn-primary { @apply bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition-colors; } .card { @apply bg-white rounded-lg shadow-md p-6; } .form-input { @apply border border-gray-300 rounded px-3 py-2 focus:outline-none focus:border-blue-500; } EOF fi # TailwindCSS Build npx tailwindcss -i static/css/input.css -o static/css/output.css --minify || { log "WARN" "TailwindCSS Build fehlgeschlagen" } } compress_assets() { log "INFO" "Komprimiere Assets..." # CSS-Dateien komprimieren find "$INSTALL_PATH/static/css" -name "*.css" -type f | while read -r css_file; do if command -v gzip >/dev/null 2>&1; then gzip -c "$css_file" > "${css_file}.gz" log "INFO" "Komprimiert: $(basename "$css_file")" fi done # JavaScript-Dateien komprimieren find "$INSTALL_PATH/static/js" -name "*.js" -type f | while read -r js_file; do if command -v gzip >/dev/null 2>&1; then gzip -c "$js_file" > "${js_file}.gz" log "INFO" "Komprimiert: $(basename "$js_file")" fi done log "INFO" "Asset-Komprimierung abgeschlossen" } verify_python_dependencies() { log "INFO" "Überprüfe Python-Dependencies..." local errors=0 # Wichtige Packages prüfen local critical_packages=( "flask" "flask-sqlalchemy" "flask-login" "flask-wtf" "werkzeug" "gunicorn" ) for package in "${critical_packages[@]}"; do if ! python3 -c "import $package" 2>/dev/null; then log "ERROR" "Kritisches Python-Package fehlt: $package" errors=$((errors + 1)) fi done # Erstelle Package-Report python3 -c "import pkg_resources; print('\n'.join([str(d) for d in pkg_resources.working_set]))" > "$INSTALL_PATH/python_packages_installed.txt" if [[ $errors -eq 0 ]]; then log "INFO" "Python-Dependencies Verifikation erfolgreich" return 0 else log "ERROR" "Python-Dependencies Verifikation fehlgeschlagen ($errors Fehler)" return 1 fi } verify_node_dependencies() { log "INFO" "Überprüfe Node.js-Dependencies..." local errors=0 # Node.js Version prüfen if ! command -v node >/dev/null 2>&1; then log "ERROR" "Node.js nicht verfügbar" errors=$((errors + 1)) else local node_version=$(node --version) log "INFO" "Node.js Version: $node_version" fi # npm Version prüfen if ! command -v npm >/dev/null 2>&1; then log "ERROR" "npm nicht verfügbar" errors=$((errors + 1)) else local npm_version=$(npm --version) log "INFO" "npm Version: $npm_version" fi # TailwindCSS prüfen if ! npx tailwindcss --help >/dev/null 2>&1; then log "WARN" "TailwindCSS nicht verfügbar" fi if [[ $errors -eq 0 ]]; then log "INFO" "Node.js-Dependencies Verifikation erfolgreich" return 0 else log "ERROR" "Node.js-Dependencies Verifikation fehlgeschlagen ($errors Fehler)" return 1 fi }