Setup-Prompts — Multi-Projekt Sync-Experiment
Ziel: Zwei (oder drei) echte Lovable-Projekte mit eigener DB, die sich per Webhook + Pull-Fallback gegenseitig synchronisieren. Empfohlen: Projekt A einmal frisch bauen, dann remixen → Projekt B/C (identisches Abbild, keine Prompt-Drift).
Ablauf
- Prompt 1 → in einem neuen leeren Lovable-Projekt eingeben. Das wird Projekt A (Master).
- Projekt A remixen → daraus wird Projekt B. Optional nochmal remixen → Projekt C.
- In B (und C) Prompt 2 eingeben, um die Rolle als Peer zu setzen (Name, Peer-URLs).
- In A Prompt 3 eingeben, um die Peer-URLs von B/C einzutragen.
- Shared Secret in allen Projekten als Runtime-Secret hinterlegen (
SYNC_SECRET, gleicher Wert überall).
Beispiel-Sync-Secret
YJhIc4WEADznzEUBqs4GzpqAwxXvyoxswr8X_Ro51IZX93HVno20-H8d7BLvOrgG
Dieser Wert ist nur ein Beispiel. Du kannst ihn verwenden, oder in jedem Projekt einen eigenen, identischen Wert als SYNC_SECRET hinterlegen. Wichtig: derselbe Wert muss in allen Peer-Projekten stehen.
Prompt 1 — Projekt A (Master) aufsetzen
Baue mir eine kleine Notes-App mit Lovable Cloud als Sync-Playground.
Anforderungen:
1. Datenmodell (Migration):
Tabelle public.notes:
- id uuid primary key default gen_random_uuid()
- content text not null
- updated_at timestamptz not null default now()
- origin text not null -- z.B. "A", "B", "C" — welches Projekt hat's zuletzt geschrieben
- deleted boolean not null default false -- Soft-Delete für Sync
Grants + RLS: authenticated darf select/insert/update/delete; service_role all.
Für den Prototyp: RLS-Policies "true" (alle authentifizierten dürfen alles). Anon: kein Zugriff.
2. UI (Startseite):
- Header zeigt groß den Projektnamen (aus VITE_NODE_NAME, default "A").
- Liste aller Notes (nicht deleted), sortiert nach updated_at desc.
- Jede Note zeigt: content, origin-Badge, updated_at, Delete-Button (Soft-Delete).
- Input + "Anlegen"-Button (setzt origin = eigenes NODE_NAME).
- Ganz oben auf der Seite die eigene Preview-URL (und falls vorhanden Publish-URL) dieses Projekts groß und kopierbar anzeigen, mit Label:
**„URL dieses Projekts ({NODE_NAME}) — kopieren und in den *anderen* Projekten unter VITE_PEER_URLS eintragen".**
- Bereich "Peers": Liste der konfigurierten Peer-URLs mit Status (online/offline) und Button "Jetzt syncen".
- Hier werden nur die URLs der *anderen* Projekte eingetragen. Als Platzhaltertext im leeren Zustand anzeigen:
**„Trage hier die URLs der anderen Projekte ein. Deine eigene URL gehört NICHT hier rein, sondern umgekehrt in deren VITE_PEER_URLS."**
- Neben jedem Peer-URL-Eingabefeld ein kleiner Hinweis: **„URL eines *anderen* Projekts (z.B. https://id-preview--... .lovable.app)"**.
- Bereich "Sync-Log": letzte 20 Sync-Events (Richtung, Anzahl, Zeit, Fehler).
3. Konfiguration per Env / Secrets:
- VITE_NODE_NAME (öffentlich, z.B. "A")
- VITE_PEER_URLS (kommagetrennte Basis-URLs, z.B. "https://projekt-b.lovable.app,https://projekt-c.lovable.app")
- SYNC_SECRET (Runtime-Secret, geteilt zwischen allen Projekten — HMAC). Beispiel siehe oben.
- BACKEND_SHARED_SECRET (Runtime-Secret, geteilt mit dem Load-Balancer). Schützt ALLE /api/public/* Routen: nur Requests mit passendem `x-backend-secret`-Header werden bedient. Dadurch können die Backend-Projekte problemlos public published sein, ohne dass Außenstehende die Endpoints aufrufen können.
4. Sync-Endpunkte (TanStack Start Server-Routes unter /api/public/):
Alle /api/public/* Routen (inkl. /health) prüfen ZUERST den Header
`x-backend-secret` gegen `process.env.BACKEND_SHARED_SECRET` per
`crypto.timingSafeEqual`. Fehlt der Header oder passt er nicht → sofort
`new Response("Unauthorized", { status: 401 })`. Kein Body lesen, kein Log
mit dem Secret. Einheitlich als kleine Helper-Funktion `requireBackendSecret(request)` in `src/lib/backend-auth.ts`.
POST /api/public/sync-in
- Header X-Sync-Signature: HMAC-SHA256(body, SYNC_SECRET), timingSafeEqual.
- Body: { changes: Note[], from: string }
- Für jede eingehende Note: upsert per id, nur überschreiben wenn eingehendes updated_at > lokales updated_at (Last-Write-Wins). origin wird übernommen.
- Antwort: { accepted: number, skipped: number }.
GET /api/public/changes?since=<iso-timestamp>
- Header X-Sync-Signature: HMAC über den Query-String.
- Liefert alle Notes (inkl. deleted=true) mit updated_at > since. Limit 500.
POST /api/public/health
- Prüft nur `x-backend-secret` (siehe oben), keine HMAC. Antwortet { ok: true, node: NODE_NAME }.
5. Sync-Mechanismus (Server-Function, vom UI und beim Anlegen/Update/Delete aufgerufen):
- pushToPeers(): schickt eigene Änderungen seit last_pushed_at an jede Peer /sync-in mit HMAC.
- pullFromPeers(): ruft bei jedem Peer /changes?since=last_pulled_at auf, mergt per Last-Write-Wins.
- Nach Insert/Update/Delete lokal automatisch pushToPeers() feuern (fire-and-forget, Fehler loggen, nicht blocken).
- Zusätzlich: Button "Jetzt syncen" → macht push + pull sequentiell.
- last_pushed_at / last_pulled_at pro Peer in einer Tabelle public.sync_state (peer_url primary key, last_pushed_at, last_pulled_at) speichern.
6. Loop-Schutz:
- Beim /sync-in NIEMALS erneut pushToPeers auslösen.
- origin bleibt beim Merge erhalten (nicht auf eigenes NODE_NAME umschreiben).
7. Doku:
- Lege eine SYNC.md an, die kurz erklärt: Datenmodell, Endpunkte, HMAC-Signatur (Beispiel-cURL), Konfliktregel, Setup-Schritte.
- Verlinke SYNC.md im Header ("Sync-Doku").
8. **Chaos-/Fehler-Schalter (läuft auf der Backend-Instanz selbst):**
Ziel: Auf jedem Backend-Projekt lokal simulieren können, dass die App down,
die DB weg, langsam, fehlerhaft oder überlastet ist — ohne den Prozess zu
killen. So lässt sich der Load-Balancer und der Peer-Sync gegen alle
Failure-Modi durchspielen. Die Schalter leben **ausschließlich in dieser
Instanz** (in-memory pro Worker) und wirken auf **alle `/api/public/*`
Routen** dieser Instanz. Kein Persist, kein Sync zu Peers — Reload/Redeploy
setzt alles auf „normal".
a) State-Modul `src/lib/chaos.ts` (server-only, module-scope):
```ts
export type ChaosMode = {
appDown: boolean; // liefert 503 „app down" auf allen /api/public/*
dbDown: boolean; // Endpunkte, die die DB brauchen, liefern 503 „db down"
readOnly: boolean; // Writes (POST/PATCH/DELETE inkl. /sync-in) liefern 503
latencyMs: number; // künstliche Verzögerung vor jeder Antwort (0 = aus)
errorRate: number; // 0..1, Anteil zufälliger 500er vor Handler-Ausführung
rejectSync: boolean; // /sync-in + /changes liefern 503 (Sync-Ausfall isoliert testen)
dropHealth: boolean; // /health liefert 503 statt 200 (Health-Check „rot")
badSecret: boolean; // simuliert Secret-Mismatch → 401 auf allen /api/public/*
seededAt: number; // Zeitpunkt der letzten Änderung (für UI)
};
// Getter/Setter + resetChaos() + snapshot() exportieren.
```
b) Middleware/Helper `withChaos(request, handler)` — vor **jedem**
`/api/public/*` Handler aufrufen, direkt nach `requireBackendSecret`:
- Wenn `badSecret` an → 401 zurück (unabhängig vom echten Secret).
- Wenn `appDown` an → `503 {"error":"app down","node":NODE_NAME}`.
- Wenn `latencyMs > 0` → `await new Promise(r => setTimeout(r, latencyMs))`.
- Wenn `errorRate > 0` und `Math.random() < errorRate` → `500 {"error":"chaos"}`.
- Für `/health`: wenn `dropHealth` an → 503.
- Für `/sync-in` und `/changes`: wenn `rejectSync` an → 503 „sync disabled".
- Bei Write-Methoden auf DB-Routen: wenn `readOnly` an → 503 „read-only".
- Bei allen DB-berührenden Routen: wenn `dbDown` an → 503
`{"error":"db down","node":NODE_NAME}` **ohne** DB-Query.
Jede simulierte Antwort setzt `X-Chaos: <grund>` Response-Header für Log/Debug.
c) Steuer-Endpunkte unter `/api/public/chaos/*` (ebenfalls durch
`requireBackendSecret` geschützt, aber **nicht** durch `withChaos` — sonst
sperrst du dich aus):
- `GET /api/public/chaos` → aktueller Zustand + `node: NODE_NAME`.
- `POST /api/public/chaos` → Body `Partial<ChaosMode>`, setzt Felder,
antwortet mit neuem Zustand.
- `POST /api/public/chaos/reset`→ setzt alles auf Default (alles aus,
latencyMs=0, errorRate=0).
- `POST /api/public/chaos/panic`→ Kill-Switch: setzt `appDown=true`.
- `POST /api/public/chaos/heal` → Alias für `reset`, für „schnell wieder
online" im Demo.
d) UI-Bereich „Chaos-Simulation" unten auf der Startseite dieser Instanz,
deutlich abgesetzt (roter/oranger Rahmen, Überschrift „⚠️ Chaos-Modus
— nur diese Instanz"):
- Toggle-Schalter für jedes Boolean-Feld (appDown, dbDown, readOnly,
rejectSync, dropHealth, badSecret).
- Slider/Number-Input für `latencyMs` (0–5000 ms) und `errorRate` (0–1
in 0.05-Schritten, als Prozent anzeigen).
- Button „Alles zurücksetzen" (→ `/chaos/reset`).
- Button „Panic (App down)" (→ `/chaos/panic`).
- Live-Anzeige des aktuellen Zustands (aus `GET /chaos`, alle 2s pollen).
- Prominenter Banner ganz oben auf der Seite, wenn **irgendein** Chaos-
Feld aktiv ist: „⚠️ Diese Instanz simuliert Fehler: <Liste aktiver
Modi>". So sieht man beim Vorführen sofort, warum eine Anfrage failed.
- Beim `dbDown`-Toggle in der lokalen Notes-Liste einen sichtbaren Hinweis
„DB simuliert offline — Liste ist Snapshot".
e) Sicherheit / Nebenwirkungen:
- Chaos-Zustand **niemals** zu Peers syncen. `pushToPeers` und
`pullFromPeers` respektieren `rejectSync` lokal (kein Aufruf senden,
wenn eigener `rejectSync=true`) — spart Fehler-Logs.
- Chaos-Endpunkte tauchen im normalen Sync-Log **nicht** auf.
- Wenn `appDown` gesetzt ist, darf die eigene UI trotzdem laden (die
Chaos-Panels sind Client-seitig; Steuerung geht über den geschützten
Chaos-Endpunkt, der `withChaos` bewusst umgeht).
f) Test-Szenarien (dokumentiere sie in SYNC.md unter „Chaos-Modi"):
- „App komplett aus" → `appDown=true` in B. Load-Balancer failovert auf A/C,
Health zeigt B rot. `appDown=false` → B wieder in Rotation.
- „Nur DB weg" → `dbDown=true` in B. `/health` bleibt grün (nur App), aber
Notes-Endpunkte antworten 503. Peer-Sync markiert B als „unhealthy DB".
- „Langsame Instanz" → `latencyMs=2000` in C. LB-Timeout / Retry-Verhalten
sichtbar, Health-Latency-Anzeige klettert.
- „Flaky" → `errorRate=0.3` in A. Sync-Log zeigt Retries, LB fällt
temporär auf gesundes Backend zurück.
- „Split-Brain" → `rejectSync=true` in B, weiter lokal schreiben. Nach
Heilung (`heal`) mergen die divergenten Notes per Last-Write-Wins.
- „Falsches Secret" → `badSecret=true` in C. Alle Peer-Calls und LB-Health
gegen C liefern 401, C isoliert sich sichtbar.
Nutze Lovable Cloud (kein externes Supabase). Halte den Code klein und lesbar.
Prompt 2 — Projekt B (bzw. C), nach Remix von A
Konfiguriere dieses geremixte Projekt als Peer "B":
WICHTIG: In VITE_PEER_URLS gehören NUR die URLs der ANDEREN Projekte, nicht die eigene.
1. Setze VITE_NODE_NAME = "B" (in .env, ohne Anführungszeichen zu belassen — Vite-Env-Format).
2. Hole dir die Preview-URL von Projekt A (oben im Lovable-Editor unter "Preview" oder im Browser, z.B. https://id-preview--... .lovable.app) und trage sie ein:
VITE_PEER_URLS = "<URL von Projekt A>"
Falls es Projekt C schon gibt, ebenfalls dessen Preview-URL hinten anhängen, kommagetrennt.
3. Lege das Runtime-Secret SYNC_SECRET mit demselben Wert wie in Projekt A an. Beispielwert (oder dein eigener, identischer Wert): `YJhIc4WEADznzEUBqs4GzpqAwxXvyoxswr8X_Ro51IZX93HVno20-H8d7BLvOrgG`
Lege zusätzlich BACKEND_SHARED_SECRET mit demselben Wert wie in Projekt A und im Load-Balancer-Projekt an. Ohne diesen Header lehnt B/C alle /api/public/*-Requests mit 401 ab — so darf B/C ruhig public published sein.
4. Ändere nichts an der DB-Struktur oder Sync-Logik — die soll identisch zu A bleiben.
5. Der Chaos-Modus (Prompt 1, Punkt 8) muss identisch vorhanden sein — bei jedem Remix übernommen, in-memory pro Instanz. Prüfe, dass `/api/public/chaos` erreichbar ist und die UI-Panel unten auf der Seite sichtbar ist.
6. Zeige mir am Ende deutlich:
- die eigene Preview-URL dieses Projekts (B), die ich in Projekt A als Peer eintragen muss.
- die URL(s), die ich gerade unter VITE_PEER_URLS eingetragen habe.
7. Stelle sicher, dass die UI-Hinweise aus Prompt 1 (Punkt 2) vorhanden sind: eigene URL oben groß + kopierbar, und beim Peers-Bereich der Hinweis „Hier gehören die URLs der *anderen* Projekte rein, nicht die eigene". Falls sie fehlen, ergänze sie.
(Für Projekt C analog: VITE_NODE_NAME = "C", VITE_PEER_URLS = "<A>,<B>".)
Prompt 3 — Projekt A: Peers eintragen
Trage in Projekt A unter .env die Preview-URLs der anderen Projekte ein (NICHT die eigene URL von A):
VITE_PEER_URLS = "<URL Projekt B>,<URL Projekt C>"
Diese URLs bekommst du, indem du in Projekt B (und C) oben auf der Seite die eigene URL kopierst (siehe Prompt 2, Schritt 5).
Verifiziere, dass alle Peers unter /api/public/health erreichbar sind, und triggere einmal manuell einen Full-Sync (push + pull) gegen jeden Peer. Zeige mir das Ergebnis im Sync-Log.
Test-Szenarien
- Happy Path: In A eine Note anlegen → nach ≤ 2s in B und C sichtbar (Origin-Badge "A").
- Offline-Peer: C stoppen (Preview zu). In A + B je eine Note anlegen. C wieder starten, "Jetzt syncen" drücken → beide Notes tauchen mit korrektem Origin auf.
- Konflikt: Dieselbe id in A und B parallel updaten. Nach Sync gewinnt das jüngere
updated_atüberall. - Delete-Propagation: In A eine Note löschen (Soft-Delete). Nach Sync ist sie in B und C verschwunden.
Wichtige Fallstricke
VITE_*-Env-Variablen sind clientseitig sichtbar. Für das Shared Secret nurSYNC_SECRET(Runtime-Secret) verwenden — niemals alsVITE_*.- Preview-URLs sind stabil unter
project--<id>.lovable.app— für Peer-Konfig immer diese verwenden, nicht die vollen Alias-URLs. - HMAC-Signatur über den exakten Body-String bilden (nicht über das re-serialisierte JSON), sonst schlägt die Verifizierung fehl.
- Beim Remix wird die DB nicht kopiert — die neue Instanz bekommt eine leere Postgres. Das ist gewollt.
Rollout-Strategie: Änderungen von A → B → C über GitHub
Ziel: Änderungen (Code, UI, neue Module, DB-Migrations) sauber vom Master in die Peers ausrollen, ohne Prompt-Drift und ohne dass A/B/C auseinanderlaufen.
Grundprinzip
┌──────────────┐ ┌──────────────┐
│ Lovable A │ ◄──sync──► │ Repo A │ ← "Master", hier entwickelst du
└──────────────┘ └──────┬───────┘
│ cherry-pick / PR
┌────────────┼────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Lovable B │ ◄──sync──► │ Repo B │ │ Repo C │ ◄──sync──► Lovable C
└──────────────┘ └──────────────┘ └──────────────┘
Jedes Lovable-Projekt ist bidirektional mit seinem eigenen GitHub-Repo verbunden. Änderungen wandern per Git zwischen den Repos, Lovable zieht sie automatisch nach.
Einmaliges Setup
- Repos verbinden (in jedem Projekt einzeln):
Plus-Menü (+) → GitHub → Connect project → Repo anlegen als
sync-demo-a,sync-demo-b,sync-demo-c. - Lokal alle drei Repos klonen in einen gemeinsamen Ordner:
~/sync-demo/ ├─ a/ (git clone …/sync-demo-a) ├─ b/ (git clone …/sync-demo-b) └─ c/ (git clone …/sync-demo-c) - Remotes verlinken, damit du zwischen den Repos cherry-picken kannst:
(Alternativ die GitHub-HTTPS-URL von Repo A als Remote hinzufügen.)cd ~/sync-demo/b git remote add master ../a git remote update cd ~/sync-demo/c git remote add master ../a git remote update - Migrations-Ordner in allen Repos identisch halten. Empfehlung:
supabase/migrations/mit strikt aufsteigend nummerierten Dateien (Timestamp-Prefix).
Standard-Workflow für eine Änderung
Phase 1 — im Master (Projekt A) entwickeln
- Änderung in Lovable A per Prompt umsetzen (UI, neues Modul, DB-Schema).
- Wenn DB-Änderung: Migration idempotent formulieren (
CREATE TABLE IF NOT EXISTS,ADD COLUMN IF NOT EXISTS,DROP … IF EXISTS). - In A testen. Erst wenn's läuft, weitermachen.
- Lovable pusht automatisch nach Repo A. Prüfe: Commit taucht auf GitHub auf.
Phase 2 — Änderung in Repo B (und C) landen
Bevorzugt: eine PR pro logischer Änderung. Zwei Wege, je nach Größe:
Kleiner Change (1–5 Commits): Cherry-Pick.
cd ~/sync-demo/b
git fetch master
git checkout -b sync/<kurzbeschreibung>
git cherry-pick <sha-aus-repo-a> # ggf. mehrere
# Konflikte lösen, testen (siehe unten), pushen:
git push -u origin sync/<kurzbeschreibung>
# PR auf GitHub öffnen, mergen.
Größerer Change / mehrere zusammenhängende Commits: Range-Merge.
cd ~/sync-demo/b
git fetch master
git checkout -b sync/<kurzbeschreibung>
git merge master/main --no-ff
# Konflikte lösen, testen, pushen, PR, merge.
Phase 3 — Rollout in Lovable B/C
- DB-Migration ZUERST: Nach dem Merge in Repo B zieht Lovable B den neuen Code inklusive Migrations-Datei. Prüfe in Lovable B, dass die Migration ausgeführt wurde (Cloud → Database → Migrations / Tables). Falls Lovable sie nicht automatisch erkennt, in Lovable B prompten:
Führe die neue Migration aus supabase/migrations/<datei>.sql aus. - Danach Code veröffentlichen: In Lovable B im Publish-Dialog auf Update klicken (Frontend-Änderungen sind sonst nur im Preview).
- Dasselbe für Lovable C.
Phase 4 — Sync verifizieren
- In A eine Test-Note anlegen, die das neue Feld / Feature nutzt.
- Auf B und C nachschauen: Note ist da, Feld korrekt gefüllt, kein 500er im Sync-Log.
- Umgekehrt: in B eine Note anlegen → in A und C prüfen.
Reihenfolge-Regel (kritisch)
Bei jeder Änderung, die DB und Code betrifft:
- Migration in allen drei Projekten ausrollen (A, B, C).
- Dann Code (Publish/Update) in allen drei Projekten.
Sonst schreibt der neue Code in A eine Spalte, die B/C noch nicht kennen → /api/public/sync-in wirft 500, Sync steht.
Bei rückwärtskompatiblen Änderungen (neue Spalte nullable, neue Tabelle) ist die Reihenfolge weniger kritisch, aber halte dich trotzdem daran — spart Debug-Zeit.
Umgang mit Konflikten beim Cherry-Pick
.env/VITE_NODE_NAME/VITE_PEER_URLS: NIE aus A übernehmen — B hat "B" drin, C hat "C". Beim Konflikt immer die Ziel-Seite behalten:git checkout --ours .env git add .env git cherry-pick --continuebun.lock/package-lock.json: bei Konflikten neu generieren (bun install), committen.src/routeTree.gen.ts: nie manuell mergen — löschen, Lovable regeneriert:rm src/routeTree.gen.ts git checkout master/main -- src/routes/ bun run dev # regeneriert die Datei git add src/routeTree.gen.ts
Was NICHT über Git syncbar ist
Diese Dinge musst du pro Projekt separat setzen — Git überträgt sie nicht:
| Ding | Wo setzen |
|---|---|
Runtime-Secrets (SYNC_SECRET) |
Lovable → Cloud → Secrets, in jedem Projekt |
Runtime-Secret BACKEND_SHARED_SECRET |
Lovable → Cloud → Secrets, in Load-Balancer UND in jedem Backend (identischer Wert) |
.env mit VITE_NODE_NAME / VITE_PEER_URLS |
pro Projekt manuell (unterschiedliche Werte!) |
| DB-Daten (Notes-Inhalte) | läuft über den Sync-Mechanismus zur Laufzeit |
| Cloud-Instanzgröße, Auth-Einstellungen | pro Projekt in Lovable UI |
Change-Prompt-Vorlage (für Änderungen direkt in A)
Damit Master-Änderungen möglichst kompakt und cherry-pickbar bleiben:
Ändere Folgendes im Projekt (bitte in einem einzigen zusammenhängenden Commit):
1. [ ] DB-Migration (falls nötig): <konkretes SQL, idempotent formuliert>
2. [ ] Code-Änderung: <welche Datei(en), welches Verhalten>
3. [ ] UI-Änderung: <was soll der User sehen>
4. [ ] Sync-Auswirkung: <ändert sich das Datenformat in /api/public/sync-in?>
→ falls ja: rückwärtskompatibel machen (neue Felder nullable, alte Felder nicht entfernen).
Halte den Diff klein. Keine Umformatierungen an nicht betroffenen Dateien.
Rollback
Wenn ein Rollout in B/C etwas kaputt macht:
- Code: In Lovable B/C → Version History → auf den letzten guten Stand zurück. (Löst automatisch einen Commit im Repo aus.)
- DB: Migrations sind selten trivial rückgängig zu machen. Deswegen: immer eine Down-Migration mitschreiben oder zumindest im PR-Text notieren, wie man das Schema von Hand zurücksetzt.
- Sync-State: Bei Bedarf
public.sync_statein B/C leeren, damit der nächste Sync-Zyklus einen Full-Pull macht.
Checkliste pro Rollout
- Änderung in A lokal getestet
- Idempotente Migration im Repo, wenn DB betroffen
- PR in Repo B gemergt, Migration in Lovable B ausgeführt, Publish→Update geklickt
- Dasselbe für Repo C / Lovable C
- End-to-End-Test: Note in A anlegen → in B + C sichtbar
- End-to-End-Test: Note in B anlegen → in A + C sichtbar
- Sync-Log in allen drei Projekten ohne Fehler
Prompt 5 — Demo- vs Live-Test-Bereich im selben Projekt trennen
Ziel: In diesem Projekt (Gateway Pilot / Master-Erklärstück) zwei klar getrennte Modi haben:
- Demo-Modus — läuft komplett in-memory im Browser (kein DB-Write). Dient nur dazu, das Konzept live vorzuführen. Reload = alles weg. Kein Cloud-Traffic.
- Live-Test-Modus — schreibt in eine echte Tabelle in Lovable Cloud (
public.live_notes), die sich später gegen echte Peers B/C syncen lässt. Reload = Daten bleiben.
Beide Modi teilen sich UI-Komponenten, aber unterschiedliche Datenquellen/Hooks. Ein Umschalter oben rechts entscheidet, was aktiv ist. Der aktive Modus wird als Badge groß sichtbar angezeigt (Farbe: Demo = neutral/grau, Live = akzent/warn), damit man beim Vorführen nie durcheinanderkommt.
Prompt (in dieses Projekt einfügen)
Erweitere dieses Projekt um eine saubere Trennung zwischen zwei Bereichen: "Demo" und "Live-Test".
1. Routing:
- /demo → in-memory Playground (keine DB), so wie bisher
- /live → echte Notes gegen Lovable Cloud (public.live_notes)
- / (Startseite) → Erklärseite mit zwei großen Karten "Demo öffnen" / "Live-Test öffnen"
und einem Warnhinweis, dass /live echte Daten schreibt.
2. Gemeinsame UI:
- Header zeigt einen Mode-Badge ("DEMO" grau / "LIVE" farbig-warn) je nach Route.
- Notes-Liste, Input, Delete-Button sind dieselbe Komponente <NotesPanel dataSource={...} />.
- dataSource ist ein Hook-Interface: { notes, addNote, deleteNote, isLoading, error }.
3. Demo-Datasource (useDemoNotes):
- Reiner React-State (useState/useReducer), kein Fetch, kein Cloud-Zugriff.
- Reload leert alles. Optional: initial 2–3 Beispiel-Notes.
4. Live-Datasource (useLiveNotes):
- Migration: Tabelle public.live_notes (id uuid pk default gen_random_uuid(),
content text not null, updated_at timestamptz not null default now(),
origin text not null default 'local', deleted boolean not null default false).
Grants: authenticated select/insert/update/delete; service_role all. RLS an, Policies "true" für authenticated.
- CRUD über den Standard-Supabase-Client (browser). Realtime-Subscription auf live_notes für live Updates.
- Zeigt zusätzlich einen kleinen "Cloud"-Indikator (verbunden/fehler).
5. Umschalter & Safety:
- In /live oben eine dismissible Warn-Leiste: "Dieser Bereich schreibt in die echte Datenbank."
- Delete-Button in /live erfordert Bestätigung (confirm-Dialog).
- Keine gemeinsamen Storage-Keys zwischen Demo und Live.
6. Vorbereitung für späteren Peer-Sync:
- live_notes hat bereits die Felder (origin, deleted, updated_at), die Prompt 1 für Sync erwartet.
- Später kann /live 1:1 zum "Projekt A"-Verhalten aufgerüstet werden, ohne Schema-Bruch.
Wichtig: Demo und Live dürfen sich niemals Daten teilen. Kein Fallback von Live auf Demo bei Fehler — im Fehlerfall in /live einen sichtbaren Error-State zeigen.
Warum diese Trennung
- Vorführen ohne Risiko: Demo ist reines Anschauungsobjekt, kein Traffic, kein Datenmüll in der DB.
- Ehrlicher Live-Test: /live verhält sich wie ein echtes Peer (A) — dieselben Feldnamen, dieselbe Semantik. Wenn später B/C angebunden werden, muss an /live nichts umgebaut werden, nur Sync-Endpunkte drankleben (Prompt 1, Punkt 4).
- Kein "geht bei mir aber in Demo": durch getrennte Datasources fällt sofort auf, wenn eine Änderung nur im In-Memory-Pfad funktioniert.
Checkliste
-
/demofunktioniert offline / ohne Cloud-Verbindung -
/livezeigt Warn-Badge und schreibt inpublic.live_notes - Reload in
/demoleert Daten, Reload in/livebehält Daten - Realtime: zweiter Browser-Tab in
/livesieht neue Notes ohne Refresh - Migration
live_notesist idempotent (create table if not exists ...) — bereit für spätere Übernahme in B/C