Files
Brancheneinstufung2/RELOCATION.md

379 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### **Anforderungsdokument (Version 3): Docker-Migration nach Ubuntu VM (`docker1`)**
**Zweck:** Verbindliche Netzwerk-Anforderungen und schrittweiser Migrationsplan für den Umzug des GTM-Engine Stacks (ca. 20 Container). Basierend auf den Stabilisierungs-Erkenntnissen vom 07.03.2026.
#### **Teil 1: Externe Port-Freigaben (Firewall)**
Diese Ports müssen auf der Firewall für den eingehenden Verkehr zur VM `10.10.81.2` geöffnet werden.
| Host-Port | Ziel-Dienst | Zugriff | Zweck |
| :--- | :--- | :--- | :--- |
| **8090** | `gateway_proxy` | Intranet | **Zentraler Zugang.** Alle Web-Apps (Dashboard, CE, Lead). |
| **3000** | `gitea` | Intranet | Gitea Web UI. |
| **2222** | `gitea` | Intranet | Gitea Git via SSH. |
| **8003** | `connector-so` | **Public** | SuperOffice Webhook-Empfänger (SSL erforderlich!). |
| **5678** | `n8n` | **Public** | Automation Workhooks. |
| **8094** | `gtm-architect`| Intranet | GTM Architect Direct. |
| **8092** | `b2b-marketing`| Intranet | B2B Marketing Assistant Direct. |
| **8001** | `transcription`| Intranet | Transcription Tool Direct (via 8090). |
---
### **Port-Mapping (docker-compose.yml)**
Die folgende Tabelle listet alle Host-Ports auf, die durch die `docker-compose.yml` gebunden werden. Diese sind die Ports, die auf `docker1` lauschen werden.
| Host-Port | Ziel-Dienst | Zweck / Notiz |
| :--- | :--- | :--- |
| **8090** | `nginx` (Gateway) | Zentraler Zugang (Intranet) |
| **8000** | `company-explorer` | API (intern) |
| **8003** | `connector-superoffice`| Webhook (Public) |
| **8501** | `lead-engine` | UI (intern) |
| **8004** | `lead-engine` | API (intern) |
| **8092** | `b2b-marketing-assistant`| UI/API (Intranet) |
| **8093** | `content-engine` | UI/API (Intranet) |
| **8094** | `gtm-architect` | UI/API (Intranet) |
| **8096** | `heatmap-frontend` | UI (Intranet) |
| **8002** | `heatmap-backend` | API (intern) |
| **8097** | `competitor-analysis` | UI/API (Intranet) |
| **8098** | `market-intelligence` | UI/API (Intranet) |
| **8001** | `transcription-tool` | UI/API (Intranet) |
---
#### **Teil 2: Netzwerk-Architektur (Intern)**
* **DNS Resolver:** In Nginx konfiguriert (`resolver 127.0.0.11`).
* **WebSockets:** Das Gateway unterstützt `Upgrade`-Header (kritisch für Streamlit/Lead-Engine).
* **Echo-Prävention:** Der Connector (`worker.py`) identifiziert sich dynamisch. Keine manuellen ID-Einträge in `.env` nötig, solange `SO_CLIENT_ID` passt.
* **Routing:**
* `/ce/` -> `company-explorer:8000`
* `/lead/` -> `lead-engine:8501` (UI)
* `/feedback/` -> `lead-engine:8004` (API)
* `/gtm/` -> `gtm-architect:3005` (API/Frontend)
* `/b2b/` -> `b2b-marketing-assistant:3002` (API/Frontend)
* `/content/` -> `content-engine:3000` (API/Frontend)
* `/market/` -> `market-intelligence:3001` (API/Frontend)
* `/competitor/` -> `competitor-analysis:3000` (API/Frontend)
* `/heatmap/` -> `heatmap-frontend:80` (Static/Proxy)
* `/tr/` -> `transcription-tool:8001` (API/Frontend) -> **Achtung:** Benötigt expliziten `rewrite` in Nginx!
---
# ⚠️ Kritische Lehren (Update 08.03.2026)
Der Umzug muss zwingend diese Punkte beachten, um den "Totalausfall" zu vermeiden:
### 1. Datenbank-Schema & Volumes
**Regel:** Datenbanken werden **NIEMALS** mehr direkt auf einen Host-Pfad gemountet.
**Grund:** Permission-Errors und SQLite-Locks auf Netzwerk-Dateisystemen.
**Vorgehen:** Nutzung von benannten Volumes (`explorer_db_data`, `connector_db_data`, `lead_engine_data`, `gtm_architect_data`, `b2b_marketing_data`, `content_engine_data`, `market_intel_data`, `competitor_analysis_data`, `transcription_uploads`).
### 2. Lead Engine: Kalender-Logik (v1.4)
* **Raster:** Das System bietet Termine nur im **15-Minuten-Takt** (:00, :15, :30, :45) an.
* **Abstand:** Zwischen zwei Terminvorschlägen liegen mind. **3 Stunden** Pause.
* **AppOnly Workaround:** Termin wird im Kalender von `info@robo-planet.de` erstellt und der Mitarbeiter (`e.melcer@`) als Teilnehmer hinzugefügt.
### 3. GTM Architect & B2B Assistant: Standalone-Betrieb
* **Architektur:** Beide Apps nutzen das "Self-Contained Image" Muster. Code, Frontend-Builds (`dist/`) und `node_modules` sind fest im Image verbaut.
* **GTM Port:** 3005 intern.
* **B2B Port:** 3002 intern.
* **DB-Abhängigkeit:** Der B2B Assistant benötigt zwingend die Datei `market_db_manager.py` (wird beim Build aus dem Root kopiert).
### 4. Transcription Tool: FFmpeg & Routing
* **FFmpeg:** Muss im Image vorhanden sein (Build dauert ca. 15 Min auf Synology).
* **Pfade:** Das Tool benötigt eine `tsconfig.json` im `frontend/` Ordner für den TypeScript-Build.
* **Nginx:** Der Pfad `/tr/` muss explizit umgeschrieben werden: `rewrite ^/tr/(.*) /$1 break;`.
---
### 📂 Docker Volume Migration (Der "Plug & Play" Weg)
Um die Daten (Companies, Leads, Projekte, Audio-Files) ohne Verluste umzuziehen, müssen die benannten Volumes gesichert werden.
**Auf der Synology (Quelle):**
```bash
# Backup aller kritischen Volumes in ein Archiv
docker run --rm -v explorer_db_data:/data -v $(pwd):/backup alpine tar czf /backup/explorer_data.tar.gz -C /data .
docker run --rm -v lead_engine_data:/data -v $(pwd):/backup alpine tar czf /backup/lead_data.tar.gz -C /data .
docker run --rm -v gtm_architect_data:/data -v $(pwd):/backup alpine tar czf /backup/gtm_data.tar.gz -C /data .
docker run --rm -v b2b_marketing_data:/data -v $(pwd):/backup alpine tar czf /backup/b2b_data.tar.gz -C /data .
docker run --rm -v content_engine_data:/data -v $(pwd):/backup alpine tar czf /backup/content_data.tar.gz -C /data .
docker run --rm -v market_intel_data:/data -v $(pwd):/backup alpine tar czf /backup/market_data.tar.gz -C /data .
docker run --rm -v competitor_analysis_data:/data -v $(pwd):/backup alpine tar czf /backup/competitor_data.tar.gz -C /data .
docker run --rm -v transcription_uploads:/data -v $(pwd):/backup alpine tar czf /backup/tr_uploads.tar.gz -C /data .
```
**Auf der Ubuntu VM (Ziel):**
1. Volumes anlegen: `docker volume create explorer_db_data` (etc.)
2. Daten wiederherstellen:
```bash
docker run --rm -v explorer_db_data:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/explorer_data.tar.gz"
```
---
### **Verbindlicher Migrationsplan**
**Phase 1: Vorbereitung**
1. [ ] **`git push`** auf Synology (aktuellster Stand inkl. GTM-Integration).
2. [ ] **`.env` Datei sichern** (Vollständigkeit prüfen!).
3. [ ] **Volumes sichern** (siehe oben: `tar.gz` Erstellung).
**Phase 2: Deployment auf `docker1`**
1. [ ] Repo klonen: `git clone ... /opt/gtm-engine`
2. [ ] `.env` kopieren.
3. [ ] **Volumes restoren** (BEVOR `docker compose up` ausgeführt wird).
4. [ ] Starten: `docker compose up -d --build`
5. [ ] **Schema-Check:** `docker exec -it company-explorer python /app/fix_missing_columns.py`
**Phase 3: Verifizierung**
1. [ ] Check Kalender-Lesen: `docker exec lead-engine python /app/trading_twins/test_calendar_logic.py`
2. [ ] Check GTM Architect: `https://10.10.81.2:8090/gtm/`
---
### **Anhang A: Post-Deployment Port Check**
Führen Sie dieses Skript auf einem Client-Rechner aus (nicht auf `docker1` selbst), um die Erreichbarkeit der freigegebenen Ports zu testen. Ersetzen Sie `TARGET_IP` durch `10.10.81.2`.
```bash
#!/bin/bash
TARGET_IP="10.10.81.2"
PORTS_INTRANET=(8090 3000 2222 8092 8093 8094 8096 8097 8098 8001)
PORTS_PUBLIC=(8003 5678)
echo "--- Testing Intranet Ports on $TARGET_IP ---"
for port in "${PORTS_INTRANET[@]}"; do
if nc -z -w 2 $TARGET_IP $port; then
echo "✅ Port $port is OPEN"
else
echo "❌ Port $port is CLOSED"
fi
done
echo -e "\n--- Testing Public Ports on $TARGET_IP (von extern prüfen!) ---"
for port in "${PORTS_PUBLIC[@]}"; do
echo " - Bitte prüfen Sie Port $port manuell von außerhalb des Netzwerks."
done
```
---
### **Teil 4: Entwicklungs- vs. Produktions-Workflow**
Dieses Kapitel definiert die verbindlichen Regeln für die Weiterentwicklung der GTM-Engine nach der Migration, um maximale Stabilität und Sicherheit des Produktivsystems zu gewährleisten.
#### **1. Grundprinzipien: Getrennte Welten**
* **Regel 1: Keine Entwicklung in der Produktion.** Auf der Wackler-VM (`docker1`) wird niemals Code direkt bearbeitet, experimentiert oder getestet. Sie ist ausschließlich für den Betrieb der stabilen Softwareversion vorgesehen.
* **Regel 2: Getrennte Konfiguration.** Jede Umgebung hat ihre eigene Konfigurationsdatei.
* **Produktion (Wackler):** `.env` Enthält alle echten API-Schlüssel und Endpunkte.
* **Entwicklung (Synology):** `.env.dev` (oder eine Kopie der `.env`) Verwendet Test-Schlüssel, Sandbox-Endpunkte und Dummy-Werte.
* **Regel 3: Die Produktionsdatenbank ist heilig.** Die produktiven Docker-Volumes (`explorer_db_data` etc.) werden niemals für Entwicklungszwecke angetastet. Lokale Entwicklung findet gegen eine separate, lokale Test-Datenbank statt.
#### **2. Umgang mit Live-Daten & Aktionen**
**a) SuperOffice Webhook**
* **Problem:** Es darf nur einen aktiven Webhook geben, um Dateninkonsistenzen zu vermeiden.
* **Lösung:**
1. **Produktion:** Der Webhook (`https://<neue-wackler-domain>/connector/webhook`) wird bei SuperOffice registriert. In der `.env` ist das `WEBHOOK_SECRET_TOKEN` gesetzt.
2. **Entwicklung:** In der `.env.dev` wird das `WEBHOOK_SECRET_TOKEN` auskommentiert (`#WEBHOOK_SECRET_TOKEN=...`). Der Connector ist somit "taub" für externe Anrufe.
3. **Testen in der Entwicklung:** Um realistische Tests durchzuführen, wird eine Umgebungsvariable `LOG_WEBHOOKS=true` im **produktiv-Connector** gesetzt. Dieser schreibt dann den Body jedes eingehenden Webhooks in die Docker-Logs. Diese JSON-Payloads können kopiert werden, um mit einem Skript (`curl`) gezielte, manuelle Tests gegen die lokale Entwicklungsumgebung zu fahren.
**b) E-Mail-Versand (Lead Engine & Co.)**
* **Problem:** Das Entwicklungssystem darf unter keinen Umständen E-Mails an echte Kunden oder Kontakte senden.
* **Lösung: Der "Safety Override"**
1. **Produktion:** Die echten MS Graph API Credentials (`INFO_...`) sind in der `.env` gesetzt.
2. **Entwicklung:** Die MS Graph Credentials in der `.env.dev` sind auskommentiert oder mit Dummy-Werten belegt.
3. **Sicherer Test-Modus:** Eine neue Umgebungsvariable `DEV_MODE_EMAIL_RECIPIENT` wird implementiert.
* Wenn diese Variable in der `.env.dev` gesetzt ist (z.B. `DEV_MODE_EMAIL_RECIPIENT=ihre.test@adresse.de`), leitet der Code **alle** ausgehenden E-Mails an diese eine Adresse um.
* **Test-Prozess:** Für einen E-Mail-Test werden in der `.env.dev` temporär die echten MS Graph Credentials eingetragen, `DEV_MODE_EMAIL_RECIPIENT` gesetzt, der Test durchgeführt und danach werden die Credentials **sofort wieder entfernt/auskommentiert**.
#### **3. Deployment-Prozess: Von der Entwicklung zur Produktion**
Der Prozess, um neuen, getesteten Code sicher auf das Produktivsystem zu bringen, ist wie folgt:
1. **Entwicklung (Synology):**
* Ein neues Feature wird entwickelt und lokal gegen die Test-Datenbank getestet.
* Änderungen werden committet und in das **lokale Gitea auf der Synology** gepusht (`git push`).
2. **Deployment (Wackler VM):**
* Verbindung zur Wackler-VM via SSH.
* Ins Projektverzeichnis navigieren (`/opt/gtm-engine`).
* Den neuesten stabilen Code vom Entwicklungs-Gitea holen: `git pull synology main`.
* Die Docker-Container mit dem neuen Code neu bauen und starten: `docker compose up -d --build`.
3. **Netzwerkanforderung für Deployment:**
* Für den `git pull`-Befehl muss die Wackler-VM (`10.10.81.2`) auf den **Gitea-Port (TCP 3000) der Synology-Diskstation** zugreifen können. Dies muss einmalig im Netzwerk konfiguriert werden.
---
### **Teil 5: Alternative - Single-Host-Setup (Dev & Prod auf einer VM)**
Dieses Szenario beschreibt den wahrscheinlicheren Fall, dass die gesamte Arbeit (Entwicklung und Produktion) auf der von der IT bereitgestellten VM (`docker1`) stattfinden muss. Der Ansatz wandelt sich von "physischer Trennung" zu **"logischer Trennung mit starker Isolation"**.
#### **1. Grundkonzept: Schalldichte Räume**
Auf der VM werden zwei komplett isolierte Umgebungen parallel betrieben. Docker ist für dieses Szenario optimiert und gewährleistet die Trennung von Code, Konfiguration, Ports und vor allem Daten.
* **Produktionsumgebung (`prod`):** Läuft stabil mit dem geprüften Code und greift auf die produktiven Daten zu.
* **Entwicklungsumgebung (`dev`):** Dient zum Entwickeln und Testen. Hat eine eigene Konfiguration und eine komplett separate Test-Datenbank.
#### **2. Implementierung**
**a) Verzeichnisstruktur**
Eine saubere Ordnerstruktur ist die Basis für die Trennung.
```
/opt/gtm-engine/
├── dev/
│ ├── docker-compose.yml
│ ├── .env
│ └── (kompletter Code des Projekts...)
└── prod/
├── docker-compose.yml
├── .env
└── (kompletter Code des Projekts...)
```
**b) Auflösung von Port-Konflikten**
Zwei Stacks können nicht dieselben Ports nutzen. Die `dev`-Umgebung nutzt daher verschobene Ports.
| Dienst | Produktions-Port (in `prod/docker-compose.yml`) | Entwicklungs-Port (in `dev/docker-compose.yml`) |
| :--- | :---: | :---: |
| Gateway | `8090:80` | `9090:80` |
| Webhook | `8003:8003` | `9003:8003` |
| GTM | `8094:3005` | `9094:3005` |
So ist das **Produktivsystem** unter `http://10.10.81.2:8090` und das **Entwicklungssystem** unter `http://10.10.81.2:9090` erreichbar.
**c) Garantierte Datenbank-Isolation**
**Dies ist der wichtigste Punkt:** Docker stellt sicher, dass die Datenbanken niemals vermischt werden können. Docker Compose erstellt benannte Volumes mit dem Verzeichnisnamen als Präfix.
* `docker compose up` im Ordner `prod/` erzeugt das Volume `prod_explorer_db_data`.
* `docker compose up` im Ordner `dev/` erzeugt das Volume `dev_explorer_db_data`.
Diese beiden Volumes sind **vollständig voneinander isolierte Container** auf der Festplatte. Ein Zugriff von `dev` auf `prod`-Daten ist technisch unmöglich. **Ihre Anforderung, dass die Produktionsdatenbank sicher ist, wird hiermit zu 100% erfüllt.**
#### **3. Vereinfachter Workflow auf einer Maschine**
Das neue Gitea (auf Port 3000) wird zur zentralen "Source of Truth".
1. **Entwicklung:**
* Sie arbeiten im Verzeichnis `/opt/gtm-engine/dev/`.
* Sie ändern den Code und testen ihn, indem Sie den `dev`-Stack starten:
`cd /opt/gtm-engine/dev/ && docker compose up -d --build`
* Sie verifizieren die Änderungen im Browser unter `http://10.10.81.2:9090`.
* Wenn alles passt, committen Sie und pushen zum Gitea auf derselben Maschine: `git push`.
2. **Deployment (Release in die Produktion):**
* Wechseln Sie in das Produktionsverzeichnis: `cd /opt/gtm-engine/prod/`.
* Holen Sie den neuen, im `dev`-Zweig getesteten Code aus Gitea: `git pull`.
* Aktualisieren Sie den Produktions-Stack: `docker compose up -d --build`.
Dieser Prozess ist sicher, schnell und wiederholbar, da er nur aus Standard-Git- und Docker-Befehlen besteht.
---
### **UMZUG Live - Lessons Learned (März 2026)**
Der reale Umzug von der Synology auf die Wackler-VM (`docker1`) hat gezeigt, dass die Theorie gut war, die Praxis aber spezifische Hürden bereithielt. Diese Lektionen sind kritisch für alle zukünftigen Deployments:
#### **1. Die Git-Historien-Falle (Packfile / Timeout Error)**
* **Problem:** Versuche, das Repository per `git clone` oder über die Gitea-Migrations-Funktion umzuziehen, brachen reproduzierbar mit `RPC failed; HTTP 504` oder `fatal: expected 'packfile'` ab.
* **Ursache:** Das lokale `.git` Verzeichnis war auf **1,4 Gigabyte** angewachsen, da in der Vergangenheit versehentlich große MP3-Dateien committet wurden. Auch nach dem Löschen verbleiben diese in der Historie und blockieren den Transfer über Proxies.
* **Lösung:** Radikale Bereinigung der Historie via `git filter-branch` (oder `git filter-repo`) für den Ordner `uploads_audio/`, gefolgt von einer aggressiven Garbage Collection (`git gc --prune=now --aggressive`).
* **Ergebnis:** Schrumpfung des Repos von 1,4 GB auf **130 MB**.
* **Lesson:** Bei Git-Transfer-Fehlern zuerst die Größe des `.git` Ordners prüfen.
#### **2. Der "Filter-Branch" Nebeneffekt (.env Schutz)**
* **Problem:** Nach der Git-Bereinigung schienen ungetrackte Dateien (wie `.env` und `volume_backups/`) plötzlich verschwunden.
* **Ursache:** `git filter-branch` erzwingt einen harten Reset auf den bereinigten Stand. Dateien, die in der `.gitignore` stehen, werden dabei oft aus dem Arbeitsverzeichnis entfernt.
* **Lösung:** Wiederherstellung aus einem zuvor erstellten `.tar.gz`-Backup des gesamten Projektverzeichnisses.
* **Lesson:** Führe niemals tiefe Git-Eingriffe ohne ein physisches Dateibackup der ungetrackten Dateien durch.
#### **3. Das "Transit-Repo" Muster für Secrets**
* **Problem:** Sicherer Transfer der `.env` und Datenbanken ohne Einchecken in das Haupt-Repo und ohne Zugriff auf externe Transfer-Dienste (Firewall-Blockade).
* **Lösung:** Erstellung eines temporären, privaten Gitea-Repositorys namens `transit` auf dem Ziel-System. Hochladen des verschlüsselten Backups über die Gitea-Weboberfläche und anschließendes Klonen/Entpacken auf der VM. Danach sofortige Löschung des `transit` Repos.
* **Lesson:** Gitea ist das beste Transit-Medium für große Files hinter restriktiven Firewalls.
#### **4. Docker Build Context & Berechtigungen**
* **Problem:** `docker compose up --build` brach ab mit: `failed to solve: error from sender: open /volume_backups: permission denied`.
* **Ursache:** Docker versucht beim Bauen das gesamte Verzeichnis einzulesen. Ordner, die dem `root` User gehören (z.B. durch Gemini-Aktionen erstellt), blockieren den Prozess.
* **Lösung:**
1. Berechtigungen korrigieren: `sudo chown -R $USER:$USER volume_backups/`
2. Ordner in der `.dockerignore` ausschließen, damit er gar nicht erst eingelesen wird.
* **Lesson:** Alles, was nicht in ein Container-Image gehört, **muss** in die `.dockerignore`.
#### **5. Vite & TypeScript Build-Tücken**
* **Problem:** Der Frontend-Build schlug fehl, weil die `tsconfig.json` im Container nicht gefunden wurde.
* **Ursache:** `COPY . .` verhält sich in komplexen Verzeichnisstrukturen manchmal unvorhersehbar.
* **Lösung:** Explizites Kopieren aller Konfigurationsdateien im Dockerfile: `COPY tsconfig.json tsconfig.node.json tsconfig.app.json vite.config.ts ./` direkt vor dem Build-Schritt.
* **Lesson:** Verlasse dich beim Build nicht auf Wildcards; kopiere essenzielle Config-Files explizit.