Files
Brancheneinstufung2/BUILDER_APPS_MIGRATION.md

7.0 KiB

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:
    "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:
    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.
    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:
    openai==0.28.1
    # weitere deps...
    

1.5 Python Syntax & F-Strings

Multi-Line f-Strings mit JSON-Beispielen ({...}) sind extrem fehleranfällig (Escaping, Einrückung).

  • Best Practice: Komplexe JSON-Daten oder Strings vorher in Variablen speichern und dann einfügen.
    # Schlecht:
    prompt = f"""Daten: {json.dumps(data.get('nested'))}"""
    
    # Gut:
    nested_json = json.dumps(data.get('nested'))
    prompt = f"""Daten: {nested_json}"""
    
  • Signaturen prüfen: Shared Libraries (helpers.py) haben oft ältere Signaturen.
    • 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:

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:

# 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!

  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.

        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?