#!/bin/bash # # n8n Workflow Auto-Reload Script # Wird beim LXC-Start ausgeführt, um den Workflow neu zu laden # set -euo pipefail # Konfiguration SCRIPT_DIR="/opt/customer-stack" LOG_DIR="${SCRIPT_DIR}/logs" LOG_FILE="${LOG_DIR}/workflow-reload.log" ENV_FILE="${SCRIPT_DIR}/.env" WORKFLOW_TEMPLATE="${SCRIPT_DIR}/workflow-template.json" WORKFLOW_NAME="RAG KI-Bot (PGVector)" # API-Konfiguration API_URL="http://127.0.0.1:5678" COOKIE_FILE="/tmp/n8n_reload_cookies.txt" MAX_WAIT=60 # Maximale Wartezeit in Sekunden # Logging-Funktion log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}" } log_error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "${LOG_FILE}" >&2 } # Funktion: Warten bis n8n bereit ist wait_for_n8n() { log "Warte auf n8n API..." local count=0 while [ $count -lt $MAX_WAIT ]; do if curl -sS -o /dev/null -w "%{http_code}" "${API_URL}/rest/settings" 2>/dev/null | grep -q "200"; then log "n8n API ist bereit" return 0 fi sleep 1 count=$((count + 1)) done log_error "n8n API nicht erreichbar nach ${MAX_WAIT} Sekunden" return 1 } # Funktion: .env-Datei laden load_env() { if [ ! -f "${ENV_FILE}" ]; then log_error ".env-Datei nicht gefunden: ${ENV_FILE}" return 1 fi # Exportiere alle Variablen aus .env set -a source "${ENV_FILE}" set +a log "Konfiguration geladen aus ${ENV_FILE}" return 0 } # Funktion: Login bei n8n n8n_login() { log "Login bei n8n als ${N8N_OWNER_EMAIL}..." # Escape special characters in password for JSON local escaped_password escaped_password=$(echo "${N8N_OWNER_PASS}" | sed 's/\\/\\\\/g; s/"/\\"/g') local response response=$(curl -sS -X POST "${API_URL}/rest/login" \ -H "Content-Type: application/json" \ -c "${COOKIE_FILE}" \ -d "{\"emailOrLdapLoginId\":\"${N8N_OWNER_EMAIL}\",\"password\":\"${escaped_password}\"}" 2>&1) if echo "$response" | grep -q '"code":\|"status":"error"'; then log_error "Login fehlgeschlagen: ${response}" return 1 fi log "Login erfolgreich" return 0 } # Funktion: Workflow nach Name suchen find_workflow() { local workflow_name="$1" log "Suche nach Workflow '${workflow_name}'..." local response response=$(curl -sS -X GET "${API_URL}/rest/workflows" \ -H "Content-Type: application/json" \ -b "${COOKIE_FILE}" 2>&1) # Extract workflow ID by name local workflow_id workflow_id=$(echo "$response" | grep -oP "\"name\":\s*\"${workflow_name}\".*?\"id\":\s*\"\K[^\"]+|\"id\":\s*\"\K[^\"]+(?=.*?\"name\":\s*\"${workflow_name}\")" | head -1 || echo "") if [ -n "$workflow_id" ]; then log "Workflow gefunden: ID=${workflow_id}" echo "$workflow_id" return 0 else log "Workflow '${workflow_name}' nicht gefunden" echo "" return 1 fi } # Funktion: Workflow löschen delete_workflow() { local workflow_id="$1" log "Lösche Workflow ${workflow_id}..." local response response=$(curl -sS -X DELETE "${API_URL}/rest/workflows/${workflow_id}" \ -H "Content-Type: application/json" \ -b "${COOKIE_FILE}" 2>&1) log "Workflow ${workflow_id} gelöscht" return 0 } # Funktion: Credential nach Name und Typ suchen find_credential() { local cred_name="$1" local cred_type="$2" log "Suche nach Credential '${cred_name}' (Typ: ${cred_type})..." local response response=$(curl -sS -X GET "${API_URL}/rest/credentials" \ -H "Content-Type: application/json" \ -b "${COOKIE_FILE}" 2>&1) # Extract credential ID by name and type local cred_id cred_id=$(echo "$response" | grep -oP "\"name\":\s*\"${cred_name}\".*?\"type\":\s*\"${cred_type}\".*?\"id\":\s*\"\K[^\"]+|\"id\":\s*\"\K[^\"]+(?=.*?\"name\":\s*\"${cred_name}\".*?\"type\":\s*\"${cred_type}\")" | head -1 || echo "") if [ -n "$cred_id" ]; then log "Credential gefunden: ID=${cred_id}" echo "$cred_id" return 0 else log_error "Credential '${cred_name}' nicht gefunden" echo "" return 1 fi } # Funktion: Workflow-Template verarbeiten process_workflow_template() { local pg_cred_id="$1" local ollama_cred_id="$2" local output_file="/tmp/workflow_processed.json" log "Verarbeite Workflow-Template..." # Python-Script zum Verarbeiten des Workflows python3 - "$pg_cred_id" "$ollama_cred_id" <<'PYTHON_SCRIPT' import json import sys # Read the workflow template with open('/opt/customer-stack/workflow-template.json', 'r') as f: workflow = json.load(f) # Get credential IDs from arguments pg_cred_id = sys.argv[1] ollama_cred_id = sys.argv[2] # Remove fields that should not be in the import fields_to_remove = ['id', 'versionId', 'meta', 'tags', 'active', 'pinData'] for field in fields_to_remove: workflow.pop(field, None) # Process all nodes and replace credential IDs for node in workflow.get('nodes', []): credentials = node.get('credentials', {}) # Replace PostgreSQL credential if 'postgres' in credentials: credentials['postgres'] = { 'id': pg_cred_id, 'name': 'PostgreSQL (local)' } # Replace Ollama credential if 'ollamaApi' in credentials: credentials['ollamaApi'] = { 'id': ollama_cred_id, 'name': 'Ollama (local)' } # Write the processed workflow with open('/tmp/workflow_processed.json', 'w') as f: json.dump(workflow, f) print("Workflow processed successfully") PYTHON_SCRIPT if [ $? -eq 0 ]; then log "Workflow-Template erfolgreich verarbeitet" echo "$output_file" return 0 else log_error "Fehler beim Verarbeiten des Workflow-Templates" return 1 fi } # Funktion: Workflow importieren import_workflow() { local workflow_file="$1" log "Importiere Workflow aus ${workflow_file}..." local response response=$(curl -sS -X POST "${API_URL}/rest/workflows" \ -H "Content-Type: application/json" \ -b "${COOKIE_FILE}" \ -d @"${workflow_file}" 2>&1) # Extract workflow ID and version ID local workflow_id local version_id workflow_id=$(echo "$response" | grep -oP '"id"\s*:\s*"\K[^"]+' | head -1) version_id=$(echo "$response" | grep -oP '"versionId"\s*:\s*"\K[^"]+' | head -1) if [ -z "$workflow_id" ]; then log_error "Workflow-Import fehlgeschlagen: ${response}" return 1 fi log "Workflow importiert: ID=${workflow_id}, Version=${version_id}" echo "${workflow_id}:${version_id}" return 0 } # Funktion: Workflow aktivieren activate_workflow() { local workflow_id="$1" local version_id="$2" log "Aktiviere Workflow ${workflow_id}..." local response response=$(curl -sS -X POST "${API_URL}/rest/workflows/${workflow_id}/activate" \ -H "Content-Type: application/json" \ -b "${COOKIE_FILE}" \ -d "{\"versionId\":\"${version_id}\"}" 2>&1) if echo "$response" | grep -q '"active":true\|"active": true'; then log "Workflow ${workflow_id} erfolgreich aktiviert" return 0 else log_error "Workflow-Aktivierung fehlgeschlagen: ${response}" return 1 fi } # Funktion: Cleanup cleanup() { rm -f "${COOKIE_FILE}" /tmp/workflow_processed.json 2>/dev/null || true } # Hauptfunktion main() { log "=========================================" log "n8n Workflow Auto-Reload gestartet" log "=========================================" # Erstelle Log-Verzeichnis falls nicht vorhanden mkdir -p "${LOG_DIR}" # Lade Konfiguration if ! load_env; then log_error "Fehler beim Laden der Konfiguration" exit 1 fi # Prüfe ob Workflow-Template existiert if [ ! -f "${WORKFLOW_TEMPLATE}" ]; then log_error "Workflow-Template nicht gefunden: ${WORKFLOW_TEMPLATE}" exit 1 fi # Warte auf n8n if ! wait_for_n8n; then log_error "n8n nicht erreichbar" exit 1 fi # Login if ! n8n_login; then log_error "Login fehlgeschlagen" cleanup exit 1 fi # Suche nach bestehendem Workflow local existing_workflow_id existing_workflow_id=$(find_workflow "${WORKFLOW_NAME}" || echo "") if [ -n "$existing_workflow_id" ]; then log "Bestehender Workflow gefunden, wird gelöscht..." delete_workflow "$existing_workflow_id" fi # Suche nach Credentials log "Suche nach bestehenden Credentials..." local pg_cred_id local ollama_cred_id pg_cred_id=$(find_credential "PostgreSQL (local)" "postgres" || echo "") ollama_cred_id=$(find_credential "Ollama (local)" "ollamaApi" || echo "") if [ -z "$pg_cred_id" ] || [ -z "$ollama_cred_id" ]; then log_error "Credentials nicht gefunden (PostgreSQL: ${pg_cred_id}, Ollama: ${ollama_cred_id})" cleanup exit 1 fi # Verarbeite Workflow-Template local processed_workflow processed_workflow=$(process_workflow_template "$pg_cred_id" "$ollama_cred_id") if [ -z "$processed_workflow" ]; then log_error "Fehler beim Verarbeiten des Workflow-Templates" cleanup exit 1 fi # Importiere Workflow local import_result import_result=$(import_workflow "$processed_workflow") if [ -z "$import_result" ]; then log_error "Workflow-Import fehlgeschlagen" cleanup exit 1 fi # Extrahiere IDs local new_workflow_id local new_version_id new_workflow_id=$(echo "$import_result" | cut -d: -f1) new_version_id=$(echo "$import_result" | cut -d: -f2) # Aktiviere Workflow if ! activate_workflow "$new_workflow_id" "$new_version_id"; then log_error "Workflow-Aktivierung fehlgeschlagen" cleanup exit 1 fi # Cleanup cleanup log "=========================================" log "Workflow-Reload erfolgreich abgeschlossen" log "Workflow-ID: ${new_workflow_id}" log "=========================================" exit 0 } # Trap für Cleanup bei Fehler trap cleanup EXIT # Hauptfunktion ausführen main "$@"