diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 00000000..07aeacc5 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,89 @@ +version: '3.8' + +services: + # --- GATEKEEPER (NGINX) --- + nginx: + image: nginx:alpine + container_name: gateway_proxy + restart: unless-stopped + ports: + - "8090:80" + volumes: + - ./nginx-proxy-test.conf:/etc/nginx/nginx.conf:ro + - ./.htpasswd:/etc/nginx/.htpasswd:ro + depends_on: + dashboard: + condition: service_started + company-explorer: + condition: service_healthy + lead-engine: + condition: service_started + + # --- DASHBOARD (Required by Nginx) --- + dashboard: + image: nginx:alpine + container_name: dashboard + restart: unless-stopped + volumes: + - ./dashboard:/usr/share/nginx/html:ro + + # --- COMPANY-EXPLORER (Required by Lead-Engine) --- + company-explorer: + build: + context: ./company-explorer + dockerfile: Dockerfile + container_name: company-explorer + restart: unless-stopped + ports: + - "8000:8000" + environment: + API_USER: "admin" + API_PASSWORD: "gemini" + PYTHONUNBUFFERED: "1" + DATABASE_URL: "sqlite:////data/companies_v3_fixed_2.db" + GEMINI_API_KEY: "${GEMINI_API_KEY}" + SERP_API_KEY: "${SERP_API}" + NOTION_TOKEN: "${NOTION_API_KEY}" + volumes: + - ./company-explorer:/app + - explorer_db_data:/data + - ./Log_from_docker:/app/logs_debug + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/docs"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + # --- LEAD-ENGINE (Our Webhook Service) --- + lead-engine: + build: + context: ./lead-engine + dockerfile: Dockerfile + container_name: lead-engine + restart: unless-stopped + ports: + - "8501:8501" # UI (Streamlit) + - "8004:8004" # API / Monitor + - "8099:8004" # Direct Test Port + environment: + PYTHONUNBUFFERED: "1" + GEMINI_API_KEY: "${GEMINI_API_KEY}" + SERP_API: "${SERP_API}" + INFO_Application_ID: "${INFO_Application_ID}" + INFO_Tenant_ID: "${INFO_Tenant_ID}" + INFO_Secret: "${INFO_Secret}" + CAL_APPID: "${CAL_APPID}" + CAL_SECRET: "${CAL_SECRET}" + CAL_TENNANT_ID: "${CAL_TENNANT_ID}" + TEAMS_WEBHOOK_URL: "${TEAMS_WEBHOOK_URL}" + FEEDBACK_SERVER_BASE_URL: "${FEEDBACK_SERVER_BASE_URL}" + WORDPRESS_BOOKING_URL: "${WORDPRESS_BOOKING_URL}" + MS_BOOKINGS_URL: "${MS_BOOKINGS_URL}" + volumes: + - ./lead-engine:/app + - lead_engine_data:/app/data + +volumes: + explorer_db_data: {} + lead_engine_data: {} diff --git a/lead-engine/README.md b/lead-engine/README.md index 0fd0799c..df39dc86 100644 --- a/lead-engine/README.md +++ b/lead-engine/README.md @@ -34,25 +34,264 @@ Der vollautomatische "Zero Touch" Workflow für Trading Twins Anfragen. * **Abstand:** Bietet zwei Termine an, mit ca. **3 Stunden Pause** dazwischen. * **Buchung:** Klick auf Link -> Server erstellt Outlook-Termin von `info@` mit `e.melcer` als Teilnehmer. -### 6. Smartlead Webhook Integration (NEU) -* **Zweck:** Empfang von Echtzeit-Lead-Benachrichtigungen direkt aus der Smartlead-Plattform. -* **Event-basiert:** Das System empfängt nur Daten für *neue* Ereignisse (z.B. "Lead als heiß markiert"), keine historischen Daten. +### 6. Smartlead Webhook Test & Integration [31f88f42] + +#### 🚀 Übersicht +Dieses Modul ist für den Empfang von Echtzeit-Lead-Benachrichtigungen direkt aus der Smartlead-Plattform konzipiert. Es verarbeitet eingehende Daten von Hot Leads und Follow-up Leads, um automatisierte Prozesse in unserer Infrastruktur anzustoßen. #### Endpunkte -Die folgenden Endpunkte werden für Smartlead bereitgestellt. Die Basis-URL ist flexibel und kann bei einem Serverumzug einfach angepasst werden. Der Platzhalter `{IHRE_AKTUELLE_DOMAIN}` sollte durch die aktuell aktive URL (z.B. `floke-ai.duckdns.org` oder die zukünftige neue Domain) ersetzt werden. +Die folgenden Endpunkte werden für Smartlead bereitgestellt. Der Platzhalter `{IHRE_AKTUELLE_DOMAIN}` sollte durch die aktuell aktive URL Ihrer Diskstation (z.B. `floke-ai.duckdns.org:8090`) oder später durch die produktive Domain ersetzt werden. Beachten Sie, dass für den Produktivbetrieb bei Wackler **HTTPS** obligatorisch sein wird. -* **Hot Leads:** `https://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/hot-lead` -* **Follow-up Leads:** `https://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/follow-up-lead` +* **Hot Leads:** `http(s)://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/hot-lead` +* **Follow-up Leads:** `http(s)://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/follow-up-lead` -#### Inbetriebnahme & Test-Prozess -Die Integration wird in einem kontrollierten Prozess ausgerollt, um die Datenstruktur sicher zu analysieren: +#### Lokales Test-Setup (Diskstation) +Um die Smartlead-Webhooks auf Ihrer Diskstation zu testen, stellen Sie sicher, dass nur die notwendigen Dienste aktiv sind. Hierfür verwenden wir eine schlanke `docker-compose.test.yml` und eine angepasste Nginx-Konfiguration. -1. **URLs an Smartlead übergeben:** Die oben genannten URLs werden an den Smartlead-Support oder in deren Admin-Oberfläche eingetragen. -2. **Test-Auslösung anfordern:** Smartlead wird gebeten, für jeden der beiden Endpunkte einen einzelnen Test-Lead manuell auszulösen. -3. **Datenanalyse:** Alle eingehenden Anfragen werden ungefiltert in die Log-Datei `lead-engine/Log/smartlead_webhooks.log` geschrieben. Dies ermöglicht eine genaue Analyse der von Smartlead gesendeten JSON-Struktur. -4. **Implementierung der Logik:** Basierend auf den analysierten Test-Daten wird die eigentliche Geschäftslogik (z.B. Eintrag in die Datenbank, Teams-Benachrichtigung) implementiert. +1. **Vorbereiten der `docker-compose.test.yml`:** + Stellen Sie sicher, dass die Datei `docker-compose.test.yml` im Projekt-Root-Verzeichnis existiert und folgenden Inhalt hat (wurde bereits von Gemini erstellt): + ```yaml + version: '3.8' + + services: + nginx: + image: nginx:alpine + container_name: gateway_proxy + restart: unless-stopped + ports: + - "8090:80" + volumes: + - ./nginx-proxy-test.conf:/etc/nginx/nginx.conf:ro + - ./.htpasswd:/etc/nginx/.htpasswd:ro + depends_on: + dashboard: + condition: service_started + company-explorer: + condition: service_healthy + lead-engine: + condition: service_started + + dashboard: + image: nginx:alpine + container_name: dashboard + restart: unless-stopped + volumes: + - ./dashboard:/usr/share/nginx/html:ro + + company-explorer: + build: + context: ./company-explorer + dockerfile: Dockerfile + container_name: company-explorer + restart: unless-stopped + ports: + - "8000:8000" + environment: + API_USER: "admin" + API_PASSWORD: "gemini" + PYTHONUNBUFFERED: "1" + DATABASE_URL: "sqlite:////data/companies_v3_fixed_2.db" + GEMINI_API_KEY: "${GEMINI_API_KEY}" + SERP_API_KEY: "${SERP_API}" + NOTION_TOKEN: "${NOTION_API_KEY}" + volumes: + - ./company-explorer:/app + - explorer_db_data:/data + - ./Log_from_docker:/app/logs_debug + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/docs"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + lead-engine: + build: + context: ./lead-engine + dockerfile: Dockerfile + container_name: lead-engine + restart: unless-stopped + ports: + - "8501:8501" # UI (Streamlit) + - "8004:8004" # API / Monitor + - "8099:8004" # Direct Test Port + environment: + PYTHONUNBUFFERED: "1" + GEMINI_API_KEY: "${GEMINI_API_KEY}" + SERP_API: "${SERP_API}" + INFO_Application_ID: "${INFO_Application_ID}" + INFO_Tenant_ID: "${INFO_Tenant_ID}" + INFO_Secret: "${INFO_Secret}" + CAL_APPID: "${CAL_APPID}" + CAL_SECRET: "${CAL_SECRET}" + CAL_TENNANT_ID: "${CAL_TENNANT_ID}" + TEAMS_WEBHOOK_URL: "${TEAMS_WEBHOOK_URL}" + FEEDBACK_SERVER_BASE_URL: "${FEEDBACK_SERVER_BASE_URL}" + WORDPRESS_BOOKING_URL: "${WORDPRESS_BOOKING_URL}" + MS_BOOKINGS_URL: "${MS_BOOKINGS_URL}" + volumes: + - ./lead-engine:/app + - lead_engine_data:/app/data + + volumes: + explorer_db_data: {} + lead_engine_data: {} + ``` + +2. **Vorbereiten der `nginx-proxy-test.conf`:** + Stellen Sie sicher, dass die Datei `nginx-proxy-test.conf` im Projekt-Root-Verzeichnis existiert und folgenden Inhalt hat (wurde bereits von Gemini erstellt): + ```nginx + events { + worker_connections 1024; + } + + http { + include mime.types; + default_type application/octet-stream; + + access_log /dev/stdout; + error_log /dev/stderr; + + client_max_body_size 50M; + proxy_read_timeout 1200s; + proxy_connect_timeout 1200s; + proxy_send_timeout 1200s; + send_timeout 1200s; + + resolver 127.0.0.11 valid=30s ipv6=off; + + server { + listen 80; + + location / { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://dashboard:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location /ce/ { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://company-explorer:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location /lead/ { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://lead-engine:8501/;\ + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + proxy_read_timeout 86400; + } + + location /feedback/ { + auth_basic off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + rewrite ^/feedback/(.*)$ /$1 break; + proxy_pass http://lead-engine:8004; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /public/smartlead/ { + auth_basic off; + rewrite ^/public/smartlead/(.*)$ /webhook/$1 break; + + proxy_pass http://lead-engine:8004; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } + } + ``` + +3. **Log-Datei vorbereiten:** + Erstellen Sie das Log-Verzeichnis und die Datei im `lead-engine` Kontext (falls noch nicht geschehen): + ```bash + mkdir -p lead-engine/Log && touch lead-engine/Log/smartlead_webhooks.log && chmod 777 lead-engine/Log/smartlead_webhooks.log + ``` + +4. **Test-Setup starten:** + Um das lokale Test-Setup zu starten, führen Sie diese Befehle aus: + ```bash + # Bestehende (potenziell fehlerhafte) Container stoppen und entfernen + docker-compose -f docker-compose.test.yml down + + # Neues, schlankes Setup starten + docker-compose -f docker-compose.test.yml up -d + ``` + +#### Test-Prozess & Datenanalyse (Diskstation) + +1. **URLs an Smartlead übergeben:** + Geben Sie die folgenden URLs an Smartlead weiter, um Test-Events auszulösen. Ersetzen Sie `` durch die tatsächliche IP/Domain Ihrer Diskstation: + * **Hot Lead Webhook:** `http://:8090/public/smartlead/hot-lead` + * **Follow-up Lead Webhook:** `http://:8090/public/smartlead/follow-up-lead` + Wenn Sie HTTPS nutzen, ändern Sie `http` zu `https` und verwenden Sie Ihren Domainnamen. + +2. **Test-Auslösung bestätigen:** + Informieren Sie Gemini, sobald Smartlead die Test-Auslösung bestätigt hat. + +3. **Log-Daten analysieren:** + Nach Erhalt der Test-Events lesen wir die Datei `/app/lead-engine/Log/smartlead_webhooks.log` aus, um die exakte JSON-Struktur der Payloads und die Quell-IP-Adressen von Smartlead zu ermitteln. + +#### Anforderungen für Wackler IT (Produktivumgebung) + +Basierend auf den Analysen aus dem lokalen Test-Setup werden wir die Anforderungen an die IT-Abteilung von Wackler formulieren, um die Smartlead-Webhooks in der Produktivumgebung sicher freizuschalten. + +1. **Externe Erreichbarkeit:** + * **Domain:** `gtm.robo-planet.de` + * **Protokoll:** **HTTPS** (Port 443) + * **Öffentliche Pfade:** `/smartlead/hot-lead` und `/smartlead/follow-up-lead` + +2. **Firewall-Regel (Quell-IPs):** + * Smartlead sendet Webhooks von spezifischen IP-Adressen (diese werden aus den Test-Logs ermittelt). Die Firewall muss so konfiguriert werden, dass nur Anfragen von diesen **erlaubten Smartlead-IPs** den externen Nginx-Server erreichen dürfen. Alle anderen IPs sind zu blockieren. + +3. **Nginx-Konfiguration (Externer Server `gtm.robo-planet.de`):** + Der externe Nginx-Server muss um einen `location`-Block erweitert werden, der dem folgenden Beispiel ähnelt (die genaue `proxy_pass`-IP (`10.10.81.2`) sollte von der Wackler IT bestätigt werden, da dies die interne IP Ihres GTM-Gateways im Wackler-Netzwerk ist): + + ```nginx + location /smartlead/ { + auth_basic off; # Keine Authentifizierung für öffentliche Webhooks + + # Erlaubte Smartlead-IPs (Platzhalter - aus Test-Logs zu befüllen) + # Beispiel: + # allow 192.0.2.0/24; + # allow 203.0.113.0/24; + deny all; # Alle anderen IPs blockieren + + rewrite ^/smartlead/(.*)$ /public/smartlead/$1 break; + proxy_pass http://10.10.81.2:8090; # Weiterleitung zum internen GTM-Gateway (Port 8090) + + # Standard Proxy Header + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + ``` + +4. **Wichtiger Sicherheitshinweis:** + Es muss explizit sichergestellt werden, dass der **SuperOffice Connector** in der Produktivumgebung nicht gestartet oder so konfiguriert ist, dass er keine Verbindung zu Wacklers SuperOffice-System herstellt, um Datenkonflikte zu vermeiden. + +--- -Dieser Prozess stellt sicher, dass wir nicht "blind" entwickeln, sondern auf Basis der realen Datenstruktur von Smartlead aufbauen. ## 🏗 Architektur diff --git a/nginx-proxy-test.conf b/nginx-proxy-test.conf new file mode 100644 index 00000000..8f6cfcb3 --- /dev/null +++ b/nginx-proxy-test.conf @@ -0,0 +1,82 @@ +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + access_log /dev/stdout; + error_log /dev/stderr; + + client_max_body_size 50M; + proxy_read_timeout 1200s; + proxy_connect_timeout 1200s; + proxy_send_timeout 1200s; + send_timeout 1200s; + + resolver 127.0.0.11 valid=30s ipv6=off; + + server { + listen 80; + + # Route for the main Dashboard + location / { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://dashboard:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # Route for Company Explorer + location /ce/ { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://company-explorer:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Route for Lead Engine UI + location /lead/ { + auth_basic "Restricted Access - Local AI Suite"; + auth_basic_user_file /etc/nginx/.htpasswd; + proxy_pass http://lead-engine:8501/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + proxy_read_timeout 86400; + } + + # Route for Lead Engine Feedback (public) + location /feedback/ { + auth_basic off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + rewrite ^/feedback/(.*)$ /$1 break; + proxy_pass http://lead-engine:8004; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Smartlead Webhooks (public) + location /public/smartlead/ { + auth_basic off; + rewrite ^/public/smartlead/(.*)$ /webhook/$1 break; + + proxy_pass http://lead-engine:8004; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +}