From 37da10da59f027f545f2ee34b8fe2224c5c65724 Mon Sep 17 00:00:00 2001 From: Wolfgang Date: Fri, 9 Jan 2026 21:28:21 +0100 Subject: [PATCH] Schritt7-3 --- install.sh | 124 +++++++++++++++++++++-------------- sql/init_pgvector.sql | 35 +--------- templates/docker-compose.yml | 84 ++++++++++-------------- 3 files changed, 110 insertions(+), 133 deletions(-) diff --git a/install.sh b/install.sh index 55ed72b..956b204 100755 --- a/install.sh +++ b/install.sh @@ -183,60 +183,88 @@ fi info "Step 6 OK: Docker + Compose Plugin installiert, Locales gesetzt, Basis-Verzeichnisse erstellt" info "Next: Schritt 7 (finales docker-compose + Secrets + n8n/supabase up + Healthchecks)" -# ===== Step 7: Compose + Secrets + Start ===== -step "7" "Deploy docker-compose + generate secrets + start stack" -# Annahmen: -# - CTID ist in $CTID -# - Hostname ist in $CT_HOSTNAME (z.B. supabase$(date +%s)) -# - Domain: zq0.de -> ergibt FQDN -N8N_FQDN="${CT_HOSTNAME}.zq0.de" -POSTGRES_PASSWORD="$(rand_alnum 32)" -DASHBOARD_USERNAME="$(rand_alnum 12)" -DASHBOARD_PASSWORD="$(rand_alnum 24)" -N8N_ENCRYPTION_KEY="$(rand_hex 64)" +# --------------------------- +# Step 7: Finalize stack + secrets + up + checks +# --------------------------- +info "Step 7: Stack finalisieren + Secrets + Up + Checks" -# Dateien in den CT kopieren -pct push "$CTID" "${SCRIPT_DIR}/templates/docker-compose.yml" "/opt/customer-stack/docker-compose.yml" --perms 0644 -pct push "$CTID" "${SCRIPT_DIR}/sql/init_pgvector.sql" "/opt/customer-stack/sql/init_pgvector.sql" --perms 0644 +# ---- Host/IP für URL bauen +# Wenn du später Reverse Proxy nutzt, werden diese Werte angepasst. +STACK_DIR="/opt/customer-stack" -# .env im CT erstellen -pct exec "$CTID" -- bash -lc "cat > /opt/customer-stack/.env <<'EOF' -TZ=Europe/Berlin -N8N_HOST=${N8N_FQDN} -N8N_EDITOR_BASE_URL=https://${N8N_FQDN}/ -WEBHOOK_URL=https://${N8N_FQDN}/ +N8N_PORT="5678" +N8N_PROTOCOL="http" +N8N_HOST="${CT_IP}" +N8N_EDITOR_BASE_URL="${N8N_PROTOCOL}://${N8N_HOST}:${N8N_PORT}/" +WEBHOOK_URL="${N8N_EDITOR_BASE_URL}" -DASHBOARD_USERNAME=${DASHBOARD_USERNAME} -DASHBOARD_PASSWORD=${DASHBOARD_PASSWORD} - -N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} - -POSTGRES_USER=postgres -POSTGRES_PASSWORD=${POSTGRES_PASSWORD} -POSTGRES_DB=postgres +# ---- Secrets generieren (einmalig pro CT) +# Wichtig: nicht jedes Mal neu erzeugen, sonst ist n8n "kaputt" (encryption key ändert sich) +pct_exec "${CTID}" "test -f ${STACK_DIR}/.env || ( \ + umask 077; \ + PG_DB='n8n'; \ + PG_USER='n8n'; \ + PG_PASSWORD=\"\$(tr -dc 'A-Za-z0-9' ${STACK_DIR}/.env < ${STACK_DIR}/docker-compose.yml <<'YML' +${COMPOSE_CONTENT} +YML" +else + die "Missing template: ${SCRIPT_DIR}/templates/docker-compose.yml" +fi -# Warten bis n8n antwortet -pct exec "$CTID" -- bash -lc ' -set -e -for i in $(seq 1 60); do - if curl -fsS http://127.0.0.1:5678/ >/dev/null; then - echo "[INFO] n8n is up" - exit 0 - fi - sleep 2 -done -echo "[ERROR] n8n did not become ready in time" >&2 -exit 1 -' +# sql init +pct_exec "${CTID}" "mkdir -p ${STACK_DIR}/sql" +if [[ -f "${SCRIPT_DIR}/sql/init_pgvector.sql" ]]; then + SQL_CONTENT="$(cat "${SCRIPT_DIR}/sql/init_pgvector.sql")" + pct exec "${CTID}" -- bash -lc "cat > ${STACK_DIR}/sql/init_pgvector.sql <<'SQL' +${SQL_CONTENT} +SQL" +else + die "Missing sql file: ${SCRIPT_DIR}/sql/init_pgvector.sql" +fi -log_info "Step 7 OK: Stack running" -log_info "URL: https://${N8N_FQDN}" -log_info "BasicAuth user: ${DASHBOARD_USERNAME}" -log_info "BasicAuth pass: ${DASHBOARD_PASSWORD}" +# ---- Volumes + Rechte (wichtig!) +pct_exec "${CTID}" "mkdir -p ${STACK_DIR}/volumes/n8n-data ${STACK_DIR}/volumes/postgres/data" +# n8n läuft als node (uid 1000), postgres i.d.R. uid 999 +pct_exec "${CTID}" "chown -R 1000:1000 ${STACK_DIR}/volumes/n8n-data" +pct_exec "${CTID}" "chown -R 999:999 ${STACK_DIR}/volumes/postgres/data" + +# ---- Start stack +pct_exec "${CTID}" "cd ${STACK_DIR} && docker compose pull" +pct_exec "${CTID}" "cd ${STACK_DIR} && docker compose up -d" + +# ---- Minimal Checks +pct_exec "${CTID}" "cd ${STACK_DIR} && docker compose ps" +pct_exec "${CTID}" "cd ${STACK_DIR} && docker logs --tail=30 customer-postgres || true" +pct_exec "${CTID}" "cd ${STACK_DIR} && docker logs --tail=30 n8n || true" + +info "Step 7 OK: Stack deployed" +info "n8n: ${N8N_EDITOR_BASE_URL}" +info "Hinweis: Ohne Reverse-Proxy/TLS ist N8N_SECURE_COOKIE=false gesetzt. Später bei HTTPS wieder true." diff --git a/sql/init_pgvector.sql b/sql/init_pgvector.sql index 06469d1..bbb3ce1 100644 --- a/sql/init_pgvector.sql +++ b/sql/init_pgvector.sql @@ -1,35 +1,2 @@ --- Wird beim ersten Start des Containers automatisch ausgeführt --- (liegt in /docker-entrypoint-initdb.d/) --- --- Zweck: --- - DBs anlegen: n8n, vectors --- - pgvector Extension in beiden DBs aktivieren --- - optional Schema vec in vectors vorbereiten - -\connect postgres - --- Datenbanken anlegen (idempotent) -SELECT 'CREATE DATABASE n8n' -WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'n8n') \gexec; - -SELECT 'CREATE DATABASE vectors' -WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'vectors') \gexec; - --- pgvector in n8n aktivieren (schadet nicht, hilft evtl. später) -\connect n8n CREATE EXTENSION IF NOT EXISTS vector; - --- pgvector in vectors aktivieren -\connect vectors -CREATE EXTENSION IF NOT EXISTS vector; - --- Optional: Schema vorbereiten -CREATE SCHEMA IF NOT EXISTS vec; - --- Optional: Beispiel-Tabelle (Dimension an Embeddings anpassen, z.B. 768 / 1024 / 1536) --- CREATE TABLE IF NOT EXISTS vec.documents ( --- id uuid PRIMARY KEY DEFAULT gen_random_uuid(), --- content text, --- metadata jsonb, --- embedding vector(1536) --- ); +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; diff --git a/templates/docker-compose.yml b/templates/docker-compose.yml index dd0e227..b99d596 100644 --- a/templates/docker-compose.yml +++ b/templates/docker-compose.yml @@ -1,81 +1,63 @@ services: - db: + postgres: image: pgvector/pgvector:pg16 - container_name: customer-db + container_name: customer-postgres restart: unless-stopped environment: - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_DB: ${POSTGRES_DB} - TZ: ${TZ:-Europe/Berlin} + POSTGRES_DB: ${PG_DB} + POSTGRES_USER: ${PG_USER} + POSTGRES_PASSWORD: ${PG_PASSWORD} volumes: - - db_data:/var/lib/postgresql/data - - ./sql/init_pgvector.sql:/docker-entrypoint-initdb.d/10-init_pgvector.sql:ro - networks: - - internal + - ./volumes/postgres/data:/var/lib/postgresql/data + - ./sql:/docker-entrypoint-initdb.d:ro healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB} -h 127.0.0.1"] + test: ["CMD-SHELL", "pg_isready -U ${PG_USER} -d ${PG_DB} || exit 1"] interval: 10s timeout: 5s - retries: 12 + retries: 20 + networks: + - customer-net n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped depends_on: - db: + postgres: condition: service_healthy ports: - - "5678:5678" + - "${N8N_PORT}:5678" environment: - TZ: ${TZ:-Europe/Berlin} - - # --- n8n core --- - N8N_HOST: ${N8N_HOST} + # --- Web / Cookies / URL --- N8N_PORT: 5678 - N8N_PROTOCOL: https - N8N_PATH: / + N8N_PROTOCOL: ${N8N_PROTOCOL} + N8N_HOST: ${N8N_HOST} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} WEBHOOK_URL: ${WEBHOOK_URL} - # Reverse-Proxy Betrieb (OPNsense davor) - N8N_PROXY_HOPS: 1 - N8N_SECURE_COOKIE: true + # Ohne TLS/Reverse Proxy: sonst Secure-Cookie Warning / Login-Probleme + N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} - # Optionaler Basisschutz via BasicAuth - N8N_BASIC_AUTH_ACTIVE: "true" - N8N_BASIC_AUTH_USER: ${DASHBOARD_USERNAME} - N8N_BASIC_AUTH_PASSWORD: ${DASHBOARD_PASSWORD} + # --- 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} - # Verschlüsselung für Credentials + # --- Basics --- + GENERIC_TIMEZONE: Europe/Berlin + TZ: Europe/Berlin + + # optional (später hart machen) N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} - # --- DB für n8n --- - DB_TYPE: postgresdb - DB_POSTGRESDB_HOST: db - DB_POSTGRESDB_PORT: 5432 - DB_POSTGRESDB_DATABASE: n8n - DB_POSTGRESDB_USER: ${POSTGRES_USER} - DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} - - # Qualität-of-life - GENERIC_TIMEZONE: ${TZ:-Europe/Berlin} - volumes: - - n8n_data:/home/node/.n8n + - ./volumes/n8n-data:/home/node/.n8n networks: - - internal - healthcheck: - test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/ >/dev/null 2>&1 || exit 1"] - interval: 10s - timeout: 5s - retries: 20 + - customer-net networks: - internal: + customer-net: driver: bridge - -volumes: - db_data: - n8n_data: