Data upload
This commit is contained in:
63
templates/docker-compose.yml
Normal file
63
templates/docker-compose.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: customer-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${PG_DB}
|
||||
POSTGRES_USER: ${PG_USER}
|
||||
POSTGRES_PASSWORD: ${PG_PASSWORD}
|
||||
volumes:
|
||||
- ./volumes/postgres/data:/var/lib/postgresql/data
|
||||
- ./sql:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${PG_USER} -d ${PG_DB} || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
networks:
|
||||
- customer-net
|
||||
|
||||
n8n:
|
||||
image: n8nio/n8n:latest
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "${N8N_PORT}:5678"
|
||||
environment:
|
||||
# --- Web / Cookies / URL ---
|
||||
N8N_PORT: 5678
|
||||
N8N_PROTOCOL: ${N8N_PROTOCOL}
|
||||
N8N_HOST: ${N8N_HOST}
|
||||
N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL}
|
||||
WEBHOOK_URL: ${WEBHOOK_URL}
|
||||
|
||||
# Ohne TLS/Reverse Proxy: sonst Secure-Cookie Warning / Login-Probleme
|
||||
N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE}
|
||||
|
||||
# --- DB (Postgres) ---
|
||||
DB_TYPE: postgresdb
|
||||
DB_POSTGRESDB_HOST: postgres
|
||||
DB_POSTGRESDB_PORT: 5432
|
||||
DB_POSTGRESDB_DATABASE: ${PG_DB}
|
||||
DB_POSTGRESDB_USER: ${PG_USER}
|
||||
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD}
|
||||
|
||||
# --- Basics ---
|
||||
GENERIC_TIMEZONE: Europe/Berlin
|
||||
TZ: Europe/Berlin
|
||||
|
||||
# optional (später hart machen)
|
||||
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
|
||||
|
||||
volumes:
|
||||
- ./volumes/n8n-data:/home/node/.n8n
|
||||
networks:
|
||||
- customer-net
|
||||
|
||||
networks:
|
||||
customer-net:
|
||||
driver: bridge
|
||||
20
templates/env.template
Normal file
20
templates/env.template
Normal file
@@ -0,0 +1,20 @@
|
||||
# Basics
|
||||
TZ=Europe/Berlin
|
||||
|
||||
# n8n URL-Setup (wird pro Kunde gefüllt)
|
||||
N8N_HOST={{N8N_HOST}}
|
||||
N8N_EDITOR_BASE_URL=https://{{N8N_HOST}}/
|
||||
WEBHOOK_URL=https://{{N8N_HOST}}/
|
||||
|
||||
# Dashboard BasicAuth (wird random generiert)
|
||||
DASHBOARD_USERNAME={{DASHBOARD_USERNAME}}
|
||||
DASHBOARD_PASSWORD={{DASHBOARD_PASSWORD}}
|
||||
|
||||
# n8n Credential Encryption Key (wird random generiert, 64 hex chars ok)
|
||||
N8N_ENCRYPTION_KEY={{N8N_ENCRYPTION_KEY}}
|
||||
|
||||
# Postgres
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD={{POSTGRES_PASSWORD}}
|
||||
POSTGRES_DB=postgres
|
||||
|
||||
32
templates/n8n-workflow-reload.service
Normal file
32
templates/n8n-workflow-reload.service
Normal file
@@ -0,0 +1,32 @@
|
||||
[Unit]
|
||||
Description=n8n Workflow Auto-Reload Service
|
||||
Documentation=https://docs.n8n.io/
|
||||
After=docker.service
|
||||
Wants=docker.service
|
||||
# Warte bis n8n-Container läuft
|
||||
After=docker-n8n.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
User=root
|
||||
WorkingDirectory=/opt/customer-stack
|
||||
|
||||
# Warte kurz, damit Docker-Container vollständig gestartet sind
|
||||
ExecStartPre=/bin/sleep 10
|
||||
|
||||
# Führe Reload-Script aus
|
||||
ExecStart=/bin/bash /opt/customer-stack/reload-workflow.sh
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=n8n-workflow-reload
|
||||
|
||||
# Restart-Policy bei Fehler
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
379
templates/reload-workflow.sh
Normal file
379
templates/reload-workflow.sh
Normal file
@@ -0,0 +1,379 @@
|
||||
#!/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
|
||||
# Erstelle Log-Verzeichnis sofort (vor den Logging-Funktionen)
|
||||
mkdir -p "${LOG_DIR}"
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
# 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 "$@"
|
||||
Reference in New Issue
Block a user