Release 0.8.0: Cloud-Variante (Supabase, Multi-Studio, Realtime, Web-Deploy)
Rapport ist jetzt dual: lokal (wie bisher) ODER Cloud auf eigenem Supabase-Server. Beide Modi haben dieselben Funktionen, Cloud zusätzlich Multi-User + Live-Sync. Storage-Architektur - src/storage/adapter.js: einheitliche Promise-API, LocalStorage- und SupabaseAdapter - src/storage/migrations.js: applyMigrations als reine Funktion, für beide Backends - Konfig-driven: VITE_SUPABASE_URL im Production-Build → automatisch Cloud-Modus Postgres-Schema (supabase/migrations/0001–0010) - 29 Tabellen, multi-tenant via studio_id + Row-Level-Security - Audit-Spalten (created_by/updated_by/at) + Trigger - Seed-Trigger pro neuem Studio (Rollen, Templates, Absenz-Typen) - Realtime-Publication für Live-Sync - RPCs: ensure_profile, create_studio_with_admin (mit Personen-Sharing), list_studios, load_persons_for_studio, attach_user_to_studio Cloud-Features (App) - BackendChoice.jsx als Erst-Screen «Lokal oder Cloud» - CloudSetup.jsx: 3-Schritt-Wizard für Erst-Einrichtung - Login.jsx: Modus-Switcher + Server-URL + Studio-Dropdown + Passwort-Vergessen - ResetPassword.jsx: empfängt Mail-Link-Klick via PASSWORD_RECOVERY-Event - Realtime: Änderungen zwischen Browsern ohne Reload sichtbar - Settings → System: Cloud-Verbindung, Studio-Switcher, weiteres Studio anlegen - Settings → Team: Mitarbeiter via Email einladen (Admin-Aktion) - Personen-Sharing: bei neuem Studio Personen aus anderen Studios übernehmen - Reload-Resume: studio_id in sessionStorage, kein erneuter Login nötig Web-Deploy - deploy/docker-compose.yml + nginx.conf: dist/ via nginx-Container, Port 8080 - .env.production.example: Build-time Cloud-URL - DEPLOY.md: Anleitung für LAN-only und extern via Nginx Proxy Manager Doku - README.md: Cloud-Variante prominent erklärt - ARCHITECTURE.md: Storage-Adapter, Migrations, neue Views in Risiko-Tabelle - DEPLOY.md: Schritt-für-Schritt für Mac Mini + NPM Version-Bump auf 0.8.0 in package.json, src-tauri/tauri.conf.json, Cargo.toml. Changelog-Entry im App.jsx-Modal (Karim sieht ihn beim ersten Start). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+45
-1
@@ -22,6 +22,10 @@ APP/
|
||||
│ ├── App.jsx Root: State, Navigation, Auth, Migrationen, Sidebar, Modals [823 Z.]
|
||||
│ ├── constants.js STORAGE_KEY, NAV_ITEMS, defaultData, SIA-Phasen, Statusfarben [252 Z.]
|
||||
│ ├── utils.js Business-Logik: Kalkulation, Format, QR-Bill, Hash, CSV, Lohn [678 Z.]
|
||||
│ ├── storage/adapter.js Storage-Adapter (Promise-API): LocalStorageAdapter + SupabaseAdapter, Modus-Auswahl
|
||||
│ ├── storage/supabase-adapter.js Cloud-Implementation: load/save/realtime/signIn/signUp/invite/reset
|
||||
│ ├── storage/supabase-mappers.js fromDB / toDB für ~22 Entities (camelCase ↔ snake_case, JSONB-Spread)
|
||||
│ ├── storage/migrations.js `applyMigrations(parsed, defaultData)` — Schema-Migrations als reine Funktion
|
||||
│ ├── views/ 31 Top-Level-Screens, lazy-geladen
|
||||
│ ├── components/ UI.jsx (StatusBadge, Modal, FormField, …), UpdateNotifier, UpdatesSupport
|
||||
│ ├── print/ PrintComponents.jsx — alle Druckansichten (Rechnung, QR, Brief, …)
|
||||
@@ -30,6 +34,27 @@ APP/
|
||||
│ ├── App.css, index.css Globale CSS-Reset + Variablen
|
||||
│ └── index.jsx
|
||||
│
|
||||
├── supabase/ Cloud-Schema (Postgres + Supabase Auth + Storage + Realtime)
|
||||
│ ├── config.toml Lokale Supabase-Konfiguration (site_url, redirects)
|
||||
│ └── migrations/ 10 SQL-Migrations:
|
||||
│ 0001 initial schema (29 Tabellen, RLS, Audit-Trigger)
|
||||
│ 0002 storage buckets (receipts, logos)
|
||||
│ 0003 seed defaults pro neuem Studio
|
||||
│ 0004 realtime publication
|
||||
│ 0005 signup RPCs (ensure_profile, create_studio_with_admin)
|
||||
│ 0006 list_studios RPC (für Login-Dropdown)
|
||||
│ 0007 persons sharing across studios
|
||||
│ 0008 load_persons RPC (lokale + geteilte)
|
||||
│ 0009 attach_user_to_studio (Mitarbeiter einladen)
|
||||
│ 0010 studio name + setup_completed in settings
|
||||
│
|
||||
├── deploy/ Static-Hosting der Web-App via nginx-Container
|
||||
│ ├── docker-compose.yml nginx:alpine, mountet ../dist
|
||||
│ └── nginx.conf SPA-Routing (try_files $uri /index.html)
|
||||
│
|
||||
├── .env.production.example Template für VITE_SUPABASE_URL / ANON_KEY
|
||||
├── DEPLOY.md Deploy-Anleitung (LAN-only + extern via NPM)
|
||||
│
|
||||
├── src-tauri/ Rust-Backend (Tauri 2.10.3)
|
||||
│ ├── src/main.rs 6 Zeilen — delegiert zu app_lib::run()
|
||||
│ ├── src/lib.rs 103 Zeilen — Tray, Window-Events, Plugins
|
||||
@@ -67,12 +92,22 @@ View ruft eine der zwei Props auf:
|
||||
│
|
||||
▼
|
||||
setData(newData)
|
||||
localStorage.setItem("studio_data_v1", JSON.stringify(newData))
|
||||
storage.save(newData) ← Storage-Adapter
|
||||
│
|
||||
▼
|
||||
React re-rendert die Hierarchie
|
||||
```
|
||||
|
||||
**Storage-Adapter** ([src/storage/adapter.js](src/storage/adapter.js)) ist die einzige Stelle, die `studio_data_v1` schreibt. Zwei Implementationen mit identischem Promise-Interface:
|
||||
- `LocalStorageAdapter` — Browser-localStorage, sync wrapped in Promises
|
||||
- `SupabaseAdapter` ([supabase-adapter.js](src/storage/supabase-adapter.js)) — Cloud via Postgres + REST + Realtime; pro `data.*`-Array eine eigene Tabelle (Mappers in [supabase-mappers.js](src/storage/supabase-mappers.js))
|
||||
|
||||
Auswahl-Logik beim Modul-Load: `localStorage.rapport_backend` entscheidet (`"local"` | `"cloud"`). Bei Production-Build mit gesetztem `VITE_SUPABASE_URL` wird automatisch Cloud (für Web-Deploy). Schnittstelle: `hasExistingData()`, `load()`, `save(data)`, `clear()`. SupabaseAdapter zusätzlich: `signIn/signUp/signOut`, `setStudioId`, `myStudios/listStudios`, `createStudio`, `inviteMember`, `requestPasswordReset`, `subscribeToChanges`. App.jsx zeigt Boot-Spinner ([ViewFallback](src/App.jsx)) bis Initial-Load durch ist.
|
||||
|
||||
**Per-Device-UI-State** (Dark Mode, Zoom, Sidebar-Collapse, Changelog-Seen, `rapport_backend`/`rapport_cloud_url`) bleibt außerhalb des Adapters in direktem `localStorage` — kommt nicht in die Cloud.
|
||||
|
||||
**Schema-Migrations** ([src/storage/migrations.js](src/storage/migrations.js)) sind als reine Funktion `applyMigrations(parsed, defaultData)` extrahiert, damit derselbe Code auf Local- und Cloud-geladenen Daten läuft. Beim Initial-Load in App.jsx wird das Ergebnis vom Adapter durch diese Funktion gepiped, bevor es in `useState` landet.
|
||||
|
||||
**Wichtig:**
|
||||
- `save()` schreibt **synchron** bei jedem Update — kein Debouncing. Bei großen `data`-Objekten kann das spürbar werden.
|
||||
- Es gibt **kein Backup, kein Conflict-Resolution**. Wenn der User zwei App-Fenster hat (passiert kaum, weil Single-Window), überschreibt das letzte gewinnt.
|
||||
@@ -381,6 +416,15 @@ npm run lint # ESLint (manuell — kein Pre-Commit-Hook)
|
||||
| Views (Invoices/Projects/Time) | **Hoch** | Lange Files mit Edge-Cases (Mahnung, Akonto, Drag&Drop) |
|
||||
| `Settings.jsx` Permissions | **Hoch** | Tangiert Rollen/Berechtigungen, Dashboard-Templates |
|
||||
| `Login.jsx` Hash-Logik | **Hoch** | PBKDF2 + Migration, sicherheitsrelevant |
|
||||
| `storage/adapter.js` | **Hoch** | Einzige Schreibstelle für `studio_data_v1`. API ist async (Promise) — Direkt-Zugriffe auf `localStorage[STORAGE_KEY]` in Views sind verboten |
|
||||
| `storage/migrations.js` | **Hoch** | Schema-Migrations. Jede Änderung am `defaultData`-Shape erfordert hier eine Migration-Step + Test mit alten Snapshots |
|
||||
| `storage/supabase-adapter.js` | **Hoch** | Cloud-Read/Write, Auth, Realtime. Bei neuer Tabelle: load() + save() + Mapper anpassen + Migration anlegen + Realtime-Publication ergänzen |
|
||||
| `storage/supabase-mappers.js` | **Hoch** | fromDB ↔ toDB pro Entity. Snake/Camel-Konvention; JSONB-Spread bei settings; isShared-Flag für geteilte Personen |
|
||||
| `supabase/migrations/` | **Hoch** | Cloud-Schema (29 Tabellen + RPCs). Via `supabase db reset` lokal anwendbar. Schema-Änderungen brauchen neue Migration-Datei, kein Bearbeiten bestehender |
|
||||
| `views/CloudSetup.jsx` | **Mittel** | Erst-Einrichtung der Cloud (3 Schritte). Ruft `cloudInit` in App.jsx |
|
||||
| `views/BackendChoice.jsx` | **Niedrig** | Modus-Wahl Lokal/Cloud bei frischer Installation |
|
||||
| `views/ResetPassword.jsx` | **Mittel** | Passwort-Reset nach Mail-Link-Klick. Hängt am `PASSWORD_RECOVERY`-Event |
|
||||
| `deploy/` + `DEPLOY.md` | **Niedrig** | Nginx-Container + Anleitung. Reine Doku/Hosting-Files |
|
||||
| `lib.rs` Tray/Window | **Mittel** | Wenn Nav-IDs geändert werden, müssen Frontend + Rust synchron bleiben |
|
||||
| `tauri.conf.json` Updater | **Sehr hoch** | Public Key ändern bricht alle bestehenden Installationen |
|
||||
| `release.sh` | **Sehr hoch** | Falsche Änderung → defekte Updates beim User |
|
||||
|
||||
Reference in New Issue
Block a user