# BrandLoop (videogen) KI-Video-Generierung mit lernender Brand-Knowledge-Base (Elo-Scoring über Attribute). - **Prototyp:** [prototyp-app.html](prototyp-app.html) – statische Preview unter `videogen.project.webklar.com` - **Desktop-Ansicht:** Design-Handoff für die Desktop-Adaption in [design/desktop-ansicht/](design/desktop-ansicht/) (Spezifikation: [README.md](design/desktop-ansicht/README.md)) - **Backend:** selbst gehostetes Appwrite (`https://appwrite.webklar.com/v1`), Projekt **BrandLoop** `6a5cee34002bb8360c34`, Datenbank `brandloop` ## App (`client/`) Expo / React Native, eine Codebase für Web und nativ. Stand: Gerüst aus E1 – drei Tabs, sonst leer. ```bash npm --prefix client run web ``` ## Bildgenerierung Die Warteschlange aus `jobs` arbeitet der Dispatcher ab – **serverseitig**, weil der Anbieter-Schlüssel nicht ins Client-Bundle darf: ```bash BILD_ANBIETER=stub node scripts/job-dispatcher.mjs ``` `stub` erzeugt Platzhalterbilder und macht die ganze Kette prüfbar, ohne Kosten. `ark` und `openrouter` sprechen die echten Modelle an – beide setzen voraus, dass das jeweilige Konto freigeschaltet bzw. aufgeladen ist: - **BytePlus Ark:** Modell in der Konsole unter Model Service aktivieren - **OpenRouter:** Guthaben aufladen Danach reicht `BILD_ANBIETER=ark node scripts/job-dispatcher.mjs` – am Code ändert sich nichts. ## Demo-Daten Demo-Daten zum Anschauen (Marke, zwei Ordner, vier Modelle mit echten Bildern aus `uploads/`) – legt alles als **Client** an, belegt damit nebenbei die Rechte, und ist idempotent: ```bash node scripts/seed-demo.mjs ``` Die Mandantentrennung lässt sich jederzeit nachprüfen – legt zwei echte Konten an, prüft über die API und räumt sich selbst auf: ```bash node scripts/test-mandanten.mjs ``` Die Appwrite-SDKs sind **exakt** gepinnt (`appwrite@23.0.0`, `react-native-appwrite@0.25.0`): der Server läuft 1.8.1, neuere SDKs sprechen Response-Format 1.9.x. Beim Server-Upgrade beide zusammen anheben. Konfiguration (Endpoint, Projekt-ID, DB-ID) steht in `client/app.json` unter `expo.extra.appwrite`. Der Appwrite-**Server-Key** gehört dort **nicht** hinein – er landet sonst im Client-Bundle; er steht in der `.env` im Repo-Root und wird nur von `scripts/` und später von Appwrite-Functions benutzt. ## Datenbank-Setup Das komplette Schema (22 Tabellen, Indizes, 5 Storage-Buckets, internes Dev-Team) legt [scripts/setup-appwrite.mjs](scripts/setup-appwrite.mjs) an – idempotent, kann nach Plan-Erweiterungen jederzeit erneut laufen: ```bash # API-Key liegt als BRANDLOOP_APPWRITE_API_KEY in /home/webklar/apps/.env, dann einfach: node scripts/setup-appwrite.mjs # oder explizit: APPWRITE_API_KEY=... node scripts/setup-appwrite.mjs ``` Der API-Key braucht die Scopes **Databases/Tables (read+write), Storage/Buckets (read+write), Teams (read+write)**. ## Prompt-Templates (P1–P18) Alle 18 Prompts des Prompt-Inventars liegen versioniert in [prompts/](prompts/) (Konventionen und Slot-Registry: [prompts/README.md](prompts/README.md)) und werden mit [scripts/seed-prompts.mjs](scripts/seed-prompts.mjs) in die Tabelle `prompt_templates` geseedet — idempotent: Inhaltsänderung ⇒ neue Zeile `version+1`, alte Zeilen `aktiv=false`. P8 ist eine Familie (1× Regie-Kern + 4 Modell-Adapter seedance/veo/sora/wan) = 22 Zeilen. Das Skript seedet außerdem die 4 globalen Brand-Regeln in `rules`. ```bash node scripts/seed-prompts.mjs ``` ### Bewusste Abweichungen vom DB-Plan - **Referenzen als indizierte String-Spalten (size 64) statt Relationship-Spalten.** Appwrite-Relationships sind nicht filter-/indizierbar – der Top-N-Index auf `attribute_scores` (`brand_id, folder_id, category_id, score DESC`) und alle Listen-Queries brauchen aber genau das. Many-to-many (`videos.attribute_ids`) ist ein String-Array (`Query.contains`). IDs mit 64 Zeichen, weil uuid4 = 36 Zeichen. - **`created_at`-Spalten entfallen** – Appwrite pflegt `$createdAt` automatisch (Indizes nutzen `$createdAt` direkt, z. B. `score_events`, `jobs`). - **Enum-Werte ohne Umlaute** (`laeuft` statt `läuft`). - **`attributes.tags`: Key-Index statt Volltext-Index** – Appwrite verbietet Fulltext auf Array-Spalten; Tag-Suche läuft über `Query.contains`. - **S3/Hetzner-Backend für Storage** ist eine *instanzweite* Appwrite-Einstellung (`_APP_STORAGE_DEVICE`) und betrifft alle Projekte auf dem Server – wird daher nicht vom Skript gesetzt, sondern muss bewusst am Server konfiguriert werden. Aktuell: lokales Storage. ### Rechte-Modell - `rules` + `prompt_templates`: Tabellen-Rechte nur für Team `internal` (Dev-Modus) 🔒 - Alle anderen Tabellen: `rowSecurity` an, keine Tabellen-Rechte – Zeilen bekommen beim Anlegen die Team-Permission der jeweiligen Brand (Multi-Tenant). - `consents`-Bucket: zusätzlich Lese-Recht für Team `internal`. - Append-only per Konvention (Server-Key schreibt): `score_events`, `video_metrics`, `jobs`. ### Noch offen (laut Plan) - Appwrite **Functions**: `elo-update`, `favorit-berechnen`, `ads-metriken-holen`, `kategorie-durchschnitt`, `archivierung`, `job-dispatcher` - Stripe-Anbindung (metered billing über `usage_records`)