# Migration Guide: Google AI Builder Apps -> Local Docker Stack **Ziel:** Standardisierter Prozess, um eine von Google AI Studio generierte React-App schnell, robust und fehlerfrei in die lokale Docker/Python-Architektur zu integrieren. **Grundsatz:** "Minimalset & Robustheit". Wir bauen keine aufgeblähten Container, und wir verhindern Timeouts und fehlende Abhängigkeiten proaktiv. --- ## 1. Vorbereitung & Abhängigkeiten (Common Pitfalls) Bevor Code kopiert wird, müssen die Grundlagen stimmen. ### 1.1 Package.json Check Generierte Apps haben oft kein `express`, da sie keinen Server erwarten. * **Aktion:** Öffne `package.json` der App. * **Prüfung:** Steht `express` unter `dependencies`? * **Fix:** ```json "dependencies": { ... "express": "^4.18.2", "cors": "^2.8.5" } ``` ### 1.2 Datenbank-Datei Docker kann keine einzelne Datei mounten, wenn sie auf dem Host nicht existiert. * **Fehler:** "Bind mount failed: ... does not exist" * **Fix:** VOR dem ersten Start die Datei anlegen: ```bash touch mein_neues_projekt.db ``` ### 1.3 Vite Base Path (White Screen Fix) Wenn die App unter einem Unterverzeichnis (z.B. `/gtm/`) läuft, findet sie ihre JS/CSS-Dateien nicht, wenn Vite Standard-Pfade (`/`) nutzt. * **Datei:** `vite.config.ts` * **Fix:** `base` auf `./` setzen. ```typescript export default defineConfig({ base: './', // WICHTIG für Sub-Pfad Deployment // ... }); ``` ### 1.4 Python Dependencies (OpenAI Version) Das Projekt nutzt ein geteiltes `helpers.py`, das auf der alten OpenAI Python Library (v0.28.1) basiert. * **Fehler:** `ModuleNotFoundError: No module named 'openai.error'` * **Ursache:** `pip install openai` installiert standardmäßig v1.x, was inkompatibel ist. * **Fix:** In `requirements.txt` zwingend die Version pinnen: ```text openai==0.28.1 # weitere deps... ### 1.5 Python Syntax & F-Strings Multi-Line f-Strings sind in Docker-Umgebungen fehleranfällig, besonders wenn sie JSON-Strukturen oder komplexe Texte enthalten. * **Wiederkehrender Fehler: `SyntaxError: invalid decimal literal` (oft bei `2. Derive` oder ähnlichen Zeilen in Prompts)** * **Symptom:** Python meldet einen Syntaxfehler innerhalb eines scheinbar korrekten Multi-Line-Strings (`f"""..."""`). * **Ursachen:** 1. **Falsches Escaping von `{}`:** Wenn geschweifte Klammern (`{}`) als Teil eines JSON-Beispiels in einem f-String verwendet werden, müssen sie oft **doppelt** escaped werden (`{{` und `}}`). Wenn es aber kein f-String ist oder die Klammern nicht zur Variableneinfügung dienen, dürfen sie nicht escaped werden (ein `dict()`-Literal ist `{}`). Das ist eine häufige Quelle für Verwirrung. 2. **Fehlende/Falsche String-Delimiter:** Der Multi-Line-String wurde nicht korrekt mit `"""` geöffnet oder geschlossen, oder eine Zeile davor/danach bricht die String-Definition. 3. **Unsichtbare Zeichen:** Manchmal können unsichtbare Unicode-Zeichen (z.B. Zero-Width Space) den Parser stören. 4. **Ternary Operators in f-Strings:** Das ist ein bekannter Anti-Pattern (siehe unten). * **Best Practice zur Fehlervermeidung bei Prompts:** 1. **Nutze explizite `if/else` Blöcke** zur Definition des Prompts, anstatt Ternary Operators (`... if x else ...`) innerhalb von Multi-Line f-Strings. Dies ist die **sicherste Methode**. 2. **Definiere komplexe JSON-Strukturen** oder sehr lange Textblöcke **vorher als separate Variablen** und füge sie dann in den f-String ein (siehe Beispiel unten). 3. **Prüfe akribisch die `"""` Delimiter** und die Einrückung des Multi-Line-Strings. ```python # Beispiel: So sollte es NICHT gemacht werden (fehleranfällig) # prompt = f"""Daten: {json.dumps(data)}""" if lang=='de' else f"""Data: ...""" # Beispiel: So ist es robust (bevorzugte Methode) json_data_for_prompt = json.dumps(data) # JSON vorformatieren if lang == 'de': prompt_template = f""" Dies ist ein deutscher Prompt mit Daten: {json_data_for_prompt} """ else: prompt_template = f""" This is an English prompt with data: {json_data_for_prompt} """ prompt = sys_instr + "\n\n" + prompt_template # System-Instruktion voranstellen ``` * **Signaturen prüfen:** Shared Libraries (`helpers.py`) haben oft ältere Signaturen. Immer die tatsächliche Definition prüfen! * Beispiel: `call_openai_chat` unterstützt oft kein `system_message` Argument. Stattdessen Prompt manuell zusammenbauen (`sys_instr + "\n\n" + prompt`). --- ## 2. Die Backend-Bridge (`server.cjs`) Dies ist der Node.js Server im Container. Er muss **robust** gegen Timeouts sein und Pfade dynamisch erkennen (Dev vs. Prod). **Gold-Standard Template:** ```javascript const express = require('express'); const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const app = express(); const port = 3005; // ANPASSEN! app.use(express.json({ limit: '50mb' })); // 1. Statische Dateien: Robustheit für Docker (Flat) vs. Local (Nested) const distPath = path.join(__dirname, 'dist'); // Docker Standard const isProduction = fs.existsSync(distPath); const staticDir = isProduction ? distPath : __dirname; console.log(`[Init] Serving static files from: ${staticDir}`); app.use(express.static(staticDir)); // 2. Python Pfad: Robustheit für Sideloading let pythonScriptPath = path.join(__dirname, 'mein_orchestrator.py'); // ANPASSEN! if (!fs.existsSync(pythonScriptPath)) { pythonScriptPath = path.join(__dirname, '../mein_orchestrator.py'); } // 3. API Routing app.post('/api/run', (req, res) => { // ... spawn logic ... }); // 4. SPA Fallback app.get('*', (req, res) => { res.sendFile(path.join(staticDir, 'index.html')); }); // 5. Timeout-Härtung (CRITICAL!) const server = app.listen(port, () => { console.log(`Server listening on ${port}`); }); server.setTimeout(600000); // 10 Minuten server.keepAliveTimeout = 610000; server.headersTimeout = 620000; ``` --- ## 3. Docker Optimierung (Multi-Stage) Wir nutzen **Multi-Stage Builds**, um das Image klein zu halten (kein `src`, keine Dev-Tools im Final Image). **Gold-Standard Dockerfile:** ```dockerfile # Stage 1: Frontend Build FROM node:20-slim AS frontend-builder WORKDIR /app COPY mein-app-ordner/package.json ./ RUN npm install COPY mein-app-ordner/ . RUN npm run build # Stage 2: Runtime FROM python:3.11-slim WORKDIR /app # Node.js installieren (für Server Bridge) RUN apt-get update && apt-get install -y --no-install-recommends nodejs npm && rm -rf /var/lib/apt/lists/* # Python Deps COPY mein-app-ordner/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Server & Frontend Artifacts (Flat Structure!) COPY mein-app-ordner/server.cjs . COPY mein-app-ordner/package.json . RUN npm install --omit=dev COPY --from=frontend-builder /app/dist ./dist # Python Logic & Shared Libs COPY mein_orchestrator.py . COPY helpers.py . COPY config.py . COPY market_db_manager.py . EXPOSE 3005 CMD ["node", "server.cjs"] ``` --- ## 4. Docker Compose & Mounts Beim Sideloading (Entwicklung ohne Rebuild) müssen **alle** Abhängigkeiten gemountet werden, nicht nur das Hauptskript. **Wichtig:** Der Pfad zu `server.cjs` ändert sich durch die "Flat Structure" im Dockerfile! ```yaml my-new-app: # ... build context ... volumes: # Logic Sideloading (ALLE Skripte!) - ./mein_orchestrator.py:/app/mein_orchestrator.py - ./helpers.py:/app/helpers.py # WICHTIG: Shared Libs - ./config.py:/app/config.py # WICHTIG: Shared Libs - ./market_db_manager.py:/app/market_db_manager.py # Server Sideloading (Ziel ist Root /app/server.cjs!) - ./mein-app-ordner/server.cjs:/app/server.cjs # Persistence - ./mein_projekt.db:/app/mein_projekt.db environment: - PYTHONUNBUFFERED=1 - DB_PATH=/app/mein_projekt.db ``` --- ## 5. Nginx Proxy Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing Slash (`/`) stimmen. ```nginx location /app/ { proxy_pass http://my-new-app:3005/; # Slash am Ende wichtig! # ... headers ... proxy_read_timeout 1200s; # Timeout passend zum Node Server } ``` --- ## 6. Frontend Anpassungen (React) 1. **API Calls:** Alle direkten Aufrufe an `GoogleGenAI` entfernen. Stattdessen `fetch('/api/run', ...)` nutzen. 2. **Base URL:** In `vite.config.ts` `base: './'` setzen (siehe Punkt 1.3). 3. **Router:** Falls `react-router` genutzt wird, muss der `basename` gesetzt werden (z.B. `/gtm/`). Bei einfachem State-Routing (wie in den aktuellen Apps) reicht der `base` Config Eintrag. --- ## Checkliste vor dem Commit - [ ] `express` in `package.json`? - [ ] `vite.config.ts` hat `base: './'`? - [ ] `requirements.txt` hat `openai==0.28.1`? - [ ] `server.cjs` hat Timeouts (>600s)? - [ ] `docker-compose.yml` mountet auch `helpers.py` und `config.py`? - [ ] Leere `.db` Datei auf dem Host erstellt? - [ ] Dockerfile nutzt Multi-Stage Build?