diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27b4758..cc5cf3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [master] + branches: [master, develop] pull_request: concurrency: diff --git a/README.md b/README.md index c517318..c27cd5b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ npm run dev:mock Der Mock-Modus verwendet ausschließlich anonyme Repository-Daten. Für Google OAuth, Picker, Sheets, PostgreSQL und Vercel Functions gilt die [Produktions-Setup-Anleitung](docs/anleitungen/produktions-setup.md). +Der Integrationsstand auf `develop` ist unter [accura-preview.kiumu.app](https://accura-preview.kiumu.app/) mit derselben anonymen, bereits angemeldeten Mock-Sitzung verfügbar. Pull Requests zielen standardmäßig auf `develop`; `master` bleibt der bewusst freizugebende Produktionsstand. + ## Prüfen ```bash diff --git a/agents.md b/agents.md index a3b9a1a..c45269a 100644 --- a/agents.md +++ b/agents.md @@ -144,6 +144,10 @@ Ich bevorzuge einfache, gezielte Lösungen. Unnötige Komplexität muss unbeding ## Git, Linear und externe Aktionen * Sei extrem vorsichtig mit destruktiven Commands, die nicht ausdrücklich angewiesen wurden. +* `master` ist der Produktionsbranch. `develop` ist der dauerhafte Integrationsbranch und läuft mit anonymen Mock-Daten unter `https://accura-preview.kiumu.app/`. +* Erstelle Arbeitsbranches grundsätzlich von `develop` und richte Pull Requests gegen `develop`, sofern ich nicht ausdrücklich einen Produktionsrelease oder ein anderes Ziel beauftrage. +* Ein Release nach `master` ist ein eigener bewusster Schritt. Merge oder pushe weder `develop` noch `master` ohne ausdrücklichen Auftrag. +* Die Mock-Preview beweist UI-, PWA- und Clientverhalten mit anonymen Daten, aber keine realen Google-, PostgreSQL- oder Vercel-Function-Abläufe. * Lösche keine Dateien, Daten, Branches oder Konfigurationen, wenn der Auftrag dies nicht eindeutig verlangt. * Erstelle keine Commits, pushe keine Branches, merge keine Pull Requests und führe keine Deployments durch, sofern ich das nicht ausdrücklich beauftragt habe. * Bearbeite keine Tasks in Linear und ändere keine anderen externen Systeme, wenn ich nur um Analyse, Prüfung oder einen Vorschlag gebeten habe. diff --git a/build/financeRuntimeMode.test.ts b/build/financeRuntimeMode.test.ts new file mode 100644 index 0000000..498b1e2 --- /dev/null +++ b/build/financeRuntimeMode.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; +import { resolveMockApiEnabled } from './financeRuntimeMode'; + +describe('resolveMockApiEnabled', () => { + it.each(['VERCEL_ENV', 'VITE_VERCEL_ENV'] as const)('uses anonymous mock data when %s identifies a preview build', (variable) => { + expect(resolveMockApiEnabled({ + command: 'build', + environment: { [variable]: 'preview' }, + })).toBe(true); + }); + + it('keeps the explicit local development mock', () => { + expect(resolveMockApiEnabled({ + command: 'serve', + environment: { VITE_USE_MOCK_API: 'true' }, + })).toBe(true); + }); + + it('uses the real API for ordinary local development', () => { + expect(resolveMockApiEnabled({ command: 'serve', environment: {} })).toBe(false); + }); + + it.each(['production', undefined])('cannot enable mocks in a non-preview build through VITE_USE_MOCK_API (%s)', (vercelEnvironment) => { + expect(resolveMockApiEnabled({ + command: 'build', + environment: { + VERCEL_ENV: vercelEnvironment, + VITE_USE_MOCK_API: 'true', + }, + })).toBe(false); + }); +}); diff --git a/build/financeRuntimeMode.ts b/build/financeRuntimeMode.ts new file mode 100644 index 0000000..25bbb03 --- /dev/null +++ b/build/financeRuntimeMode.ts @@ -0,0 +1,20 @@ +type BuildEnvironment = Record; + +type ResolveMockApiOptions = { + command: 'build' | 'serve'; + environment?: BuildEnvironment; +}; + +/** + * The decision is compiled into the bundle so production cannot switch data + * sources at runtime and does not ship the anonymous fixture chunk. + */ +export function resolveMockApiEnabled({ + command, + environment = process.env, +}: ResolveMockApiOptions): boolean { + if (command === 'build') { + return (environment.VERCEL_ENV ?? environment.VITE_VERCEL_ENV) === 'preview'; + } + return environment.VITE_USE_MOCK_API === 'true'; +} diff --git a/docs/README.md b/docs/README.md index 241317c..8791dd0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -88,7 +88,7 @@ Diese Seite ist der zentrale Index und damit die Single Source of Truth (SSOT) f ### Entscheidungen, Vorlagen und historische Pfade -- [ADR-Index](entscheidungen/README.md), [0001](entscheidungen/0001-google-sheets-als-datenquelle.md), [0002](entscheidungen/0002-versionierte-domaenengrenze-und-integer-cents.md), [0003](entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md), [0004](entscheidungen/0004-single-user-sicherheitsmodell.md), [0005](entscheidungen/0005-last-known-good-und-offline.md), [0006](entscheidungen/0006-provider-selektoren-und-view-model.md), [0007](entscheidungen/0007-vite-pwa-und-vercel-functions.md), [0008](entscheidungen/0008-material-design-und-dynamische-farben.md), [0009](entscheidungen/0009-ereignisgesteuerte-aktualisierung.md), [0010](entscheidungen/0010-gehaltsbezogene-faelligkeitsprojektion.md), [0011](entscheidungen/0011-lokaler-privacy-modus.md), [0012](entscheidungen/0012-app-vorschau-und-lokaler-pin-lock.md) +- [ADR-Index](entscheidungen/README.md), [0001](entscheidungen/0001-google-sheets-als-datenquelle.md), [0002](entscheidungen/0002-versionierte-domaenengrenze-und-integer-cents.md), [0003](entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md), [0004](entscheidungen/0004-single-user-sicherheitsmodell.md), [0005](entscheidungen/0005-last-known-good-und-offline.md), [0006](entscheidungen/0006-provider-selektoren-und-view-model.md), [0007](entscheidungen/0007-vite-pwa-und-vercel-functions.md), [0008](entscheidungen/0008-material-design-und-dynamische-farben.md), [0009](entscheidungen/0009-ereignisgesteuerte-aktualisierung.md), [0010](entscheidungen/0010-gehaltsbezogene-faelligkeitsprojektion.md), [0011](entscheidungen/0011-lokaler-privacy-modus.md), [0012](entscheidungen/0012-app-vorschau-und-lokaler-pin-lock.md), [0013](entscheidungen/0013-postgresql-als-finanzquelle.md), [0014](entscheidungen/0014-google-oauth-nur-als-identitaet.md) - [Dokumentationsseite](vorlagen/dokumentationsseite.md), [ADR](vorlagen/adr.md), [Fonts](fonts/README.md) - Historische Einstiegspunkte: [Designsystem](design-system.md), [Sicherheit und Datenfluss](security-and-data-flow.md), [Schema](finance-data-schema-v1.md), [Google-Setup](google-oauth-vercel-setup.md) diff --git a/docs/anleitungen/lokale-entwicklung.md b/docs/anleitungen/lokale-entwicklung.md index dd3bc9b..66eaebb 100644 --- a/docs/anleitungen/lokale-entwicklung.md +++ b/docs/anleitungen/lokale-entwicklung.md @@ -15,6 +15,12 @@ npm run dev:mock Vite nennt die lokale URL. Dieser Modus lädt ausschließlich anonyme Daten aus `src/mocks`, simuliert Sitzung/API und Picker und benötigt weder Google noch PostgreSQL. Die Umschaltung ist nur aktiv, wenn Vite im Entwicklungsmodus läuft und `VITE_USE_MOCK_API=true` gesetzt ist. Ein Produktionsbuild verwendet den Mock nicht. +## Develop- und Vercel-Preview + +Der dauerhafte Integrationsbranch `develop` wird unter `https://accura-preview.kiumu.app/` als Vercel Preview bereitgestellt. Alle Vercel-Preview-Deployments verwenden automatisch dieselbe anonyme, bereits angemeldete Mock-Sitzung wie `npm run dev:mock`. Damit benötigen weder `develop` noch Pull-Request-Previews Google- oder PostgreSQL-Secrets. + +Die Trennung ist fail-closed: Nur Vercels exakte Umgebung `preview` aktiviert den Build-Mock automatisch. `production` verwendet unabhängig von `VITE_USE_MOCK_API` immer die reale API. Production und Preview besitzen außerdem verschiedene Origins und dadurch getrennte Cookies, IndexedDB- und Service-Worker-Speicher. + ## Realer lokaler Serverfluss Plain `npm run dev` startet nur Vite und stellt `api/` nicht bereit. Für echte Vercel Functions: @@ -48,4 +54,5 @@ npx vercel dev --listen 3000 - npm-Skripte: [package.json](../../package.json) - Vite/PWA: [vite.config.ts](../../vite.config.ts) - Mock-API: [src/mocks/mockFinanceApi.ts](../../src/mocks/mockFinanceApi.ts) +- Modusauflösung: [build/financeRuntimeMode.ts](../../build/financeRuntimeMode.ts) - Mock-Workbook: [src/mocks/anonymousWorkbook.ts](../../src/mocks/anonymousWorkbook.ts) diff --git a/docs/anleitungen/testen-und-release.md b/docs/anleitungen/testen-und-release.md index fcf9b6e..b09fbca 100644 --- a/docs/anleitungen/testen-und-release.md +++ b/docs/anleitungen/testen-und-release.md @@ -61,7 +61,9 @@ Chromes nativer Installationsdialog, Android-Launcher, Task-Switcher und OS-Spla ## Release-Entscheidung -Ein Release erfolgt nur, wenn automatische Prüfungen grün, Änderungen und Migrationen verstanden, Secrets/Finanzwerte ausgeschlossen und relevante manuelle Szenarien abgenommen sind. GitHub-CI prüft Pushes auf `master` und Pull Requests mit Lint, Unit, Build und Smoke. Reale Google-/Datenbankabläufe bleiben außerhalb CI. +Arbeitsbranches entstehen von `develop` und werden per Pull Request dorthin integriert. Vercel stellt sie und den dauerhaften `develop`-Stand mit anonymer, bereits angemeldeter Mock-Sitzung bereit. Dieser Pfad darf keine realen Google-/Datenbankabläufe vortäuschen. + +Ein Produktionsrelease von `develop` nach `master` erfolgt nur als eigener bewusster Schritt, wenn automatische Prüfungen grün, Änderungen und Migrationen verstanden, Secrets/Finanzwerte ausgeschlossen und relevante reale Szenarien abgenommen sind. GitHub-CI prüft Pushes auf `develop` und `master` sowie Pull Requests mit Lint, Unit, Build und Smoke. Reale Google-/Datenbankabläufe bleiben außerhalb CI. ## Nachweis diff --git a/docs/architektur/backend-und-sicherheit.md b/docs/architektur/backend-und-sicherheit.md index 41f70ed..b051980 100644 --- a/docs/architektur/backend-und-sicherheit.md +++ b/docs/architektur/backend-und-sicherheit.md @@ -10,6 +10,8 @@ Die Vercel Functions sind Backend-for-Frontend und Sicherheitsgrenze. Sie verifizieren die einzige erlaubte Identität, verwalten Google-Token und Datenbankverbindung, lesen Sheets, validieren das Finance-Schema und liefern eine kleine same-origin JSON-API. Der Browser spricht Google nur beim bewusst geöffneten Picker direkt an. +Dies beschreibt den aktuell implementierten Übergangsstand. Im verbindlichen Zielbild aus [ADR 0013](../entscheidungen/0013-postgresql-als-finanzquelle.md) und [ADR 0014](../entscheidungen/0014-google-oauth-nur-als-identitaet.md) liest der Server `FinanceDataV1` ownergebunden aus PostgreSQL. Google bleibt nur Identitätsanbieter; Picker, `drive.file`, Sheets-Laufzeitzugriff und persistierte Refresh-Tokens entfallen beim Cutover. Bis dahin bleibt der alte Pfad betriebsfähig, wird aber nicht weiter ausgebaut. + ## OAuth-Sequenz ```mermaid @@ -71,7 +73,7 @@ Widerrufene oder abgelaufene Google-Grants werden als `reconnect_required` abgeb ## Begründung und Nachweis -Siehe [ADR 0003](../entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md) und [ADR 0004](../entscheidungen/0004-single-user-sicherheitsmodell.md). +Für den aktuellen Übergangsstand siehe die ersetzte [ADR 0003](../entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md). Das Zielbild steht in [ADR 0013](../entscheidungen/0013-postgresql-als-finanzquelle.md) und [ADR 0014](../entscheidungen/0014-google-oauth-nur-als-identitaet.md); [ADR 0004](../entscheidungen/0004-single-user-sicherheitsmodell.md) bleibt gültig. - Konfiguration: [api/_lib/config.ts](../../api/_lib/config.ts) - HTTP-Grenze: [api/_lib/http.ts](../../api/_lib/http.ts) diff --git a/docs/architektur/finanz-domaene.md b/docs/architektur/finanz-domaene.md index 312201f..57d4523 100644 --- a/docs/architektur/finanz-domaene.md +++ b/docs/architektur/finanz-domaene.md @@ -10,7 +10,9 @@ Die Tabelle enthält Quellen und zeitbezogene Snapshots, keine UI-Gesamtsummen. Der Parser validiert Beziehungen und normalisiert Geld in Integer-Cents. Reine Selektoren wählen den fachlich gültigen Stand und berechnen Summen. Das View-Model ergänzt lokalisierte Texte und Screen-Strukturen. React rendert diese Ausgabe. -## Sheets- und Finance-Datenpipeline +Im beschlossenen Zielbild ist `FinanceDataV1` die quellenunabhängige Domänengrenze: Der einmalige Sheet-Import erzeugt denselben Vertrag wie das PostgreSQL-Repository. Die folgenden Sheets-Schritte beschreiben den aktuell implementierten Übergangsstand. Quelle, Owner-Zuordnung und SQL-Grenze legt [ADR 0013](../entscheidungen/0013-postgresql-als-finanzquelle.md) fest. + +## Aktuelle Sheets- und Finance-Datenpipeline ```mermaid flowchart LR @@ -73,7 +75,7 @@ Das View-Model erzeugt die vier Screenmodelle gemeinsam; dadurch teilen UI und T Die Demnächst-Logik nimmt monatliche Wiederholung an und berücksichtigt weder einmalige Termine noch Feiertags-/Bankarbeitstagverschiebungen. Bis ein Nutzerprofil existiert, liefert der Browser nach Möglichkeit eine IANA-Zeitzone wie `Europe/Berlin`; fehlt sie, wird der lokale Gerätekalender verwendet. Das View-Model wird beim Sichtbarwerden der App und nach dem nächsten lokalen Tageswechsel mit einem neuen Projektionstag berechnet. Tests übergeben feste ISO-Daten und bleiben dadurch deterministisch. Negative `safeToSpendCents` sind möglich und werden nicht künstlich auf null begrenzt. Dasselbe gilt für reale negative Konto- und Pocketstände sowie den daraus abgeleiteten aktuellen Gesamtbestand. -Bei einer späteren Migration zu PostgreSQL gelten dieselben fachlichen Grenzen: reine Kalendertage wie Snapshot-, Fälligkeits- und Meilensteindaten werden als `date` gespeichert; tatsächliche Ereigniszeitpunkte wie Synchronisationen oder Änderungen als `timestamptz`. Ob Client, Anwendungsserver oder Datenbank den Projektionstag ableitet, wird mit der Datenbankarchitektur entschieden. Für die Finanz-Domäne bleibt er ein expliziter Eingabewert und darf weder stillschweigend aus dem Snapshot-Stichtag noch aus der Server- oder Datenbank-Session-Zeitzone entstehen. +Beim beschlossenen Wechsel zu PostgreSQL gelten dieselben fachlichen Grenzen: reine Kalendertage wie Snapshot- und Fälligkeitsdaten werden als `date` gespeichert; tatsächliche Ereigniszeitpunkte wie Synchronisationen oder Änderungen als `timestamptz`. Die Monats- oder Tagespräzision eines Meilensteins muss separat erhalten bleiben. Für die Finanz-Domäne bleibt der Projektionstag ein expliziter Eingabewert und darf weder stillschweigend aus dem Snapshot-Stichtag noch aus der Server- oder Datenbank-Session-Zeitzone entstehen. Mit dem Onboarding wird die dort bestätigte IANA-Zeitzone als Heimatzeitzone im Nutzerprofil gespeichert. Die Gerätezeitzone darf den initialen Wert vorschlagen, ändert die Heimatzeitzone bei Reisen aber nicht automatisch. Nutzer können sie bewusst korrigieren. IANA-Zonen werden validiert; feste UTC-Offsets reichen wegen Sommerzeit und Regeländerungen nicht aus. diff --git a/docs/architektur/ueberblick.md b/docs/architektur/ueberblick.md index 0c0b256..4ca0bba 100644 --- a/docs/architektur/ueberblick.md +++ b/docs/architektur/ueberblick.md @@ -6,7 +6,13 @@ > **Kanonisch für:** Systemkontext, Vertrauensgrenzen und Architektur-Gesamtbild. > **Verwandte Dokumente:** [Frontend](frontend.md), [Backend und Sicherheit](backend-und-sicherheit.md), [Quellcode-Karte](../referenz/quellcode-karte.md) -## Mentales Modell +## Verbindliche Richtung + +Der beschlossene Quellenwechsel trennt Finanzquelle und Identität: PostgreSQL wird die einzige produktive Finanzquelle, Google Sheets bleibt ein einmaliges Importformat und Google OAuth dient danach nur noch der Anmeldung. Der vollständige Browservertrag bleibt `FinanceDataV1`; `owner_id` existiert ausschließlich in der Persistenz. Finanzberechnungen und Snapshot-Auswahl bleiben außerhalb von SQL. Verbindlich sind [ADR 0013](../entscheidungen/0013-postgresql-als-finanzquelle.md) und [ADR 0014](../entscheidungen/0014-google-oauth-nur-als-identitaet.md). + +Dieses Zielbild ist noch nicht vollständig implementiert. Bis Schema, Import und Cutover abgeschlossen sind, gilt der folgende Abschnitt als Beschreibung des laufenden Systems; neue Arbeit darf daraus keine fortbestehende Bindung an Sheets ableiten. + +## Aktuell implementierter Übergangsstand `accura` besteht aus einer React-PWA im Browser, same-origin Vercel Functions, PostgreSQL und Google-Diensten. Die Tabelle ist die Finanzquelle; PostgreSQL speichert nur die Verbindung. Der Server bildet die Sicherheits- und Validierungsgrenze. Der Browser erhält ausschließlich normalisierte Finanzdaten und zeigt daraus abgeleitete View-Models. @@ -50,7 +56,7 @@ Google Sheets → Vercel Function → Tabellenparser → `FinanceDataV1` → Pro ## Architekturentscheidungen -Die Gründe sind in [ADRs](../entscheidungen/README.md) festgehalten. Besonders zentral sind Google Sheets als Quelle, versionierte Integer-Cent-Domäne, serverseitiger Google-Zugriff, Single-User-Sicherheit und Last-known-good-Offline. +Die Gründe sind in [ADRs](../entscheidungen/README.md) festgehalten. Für die nächste Arbeit sind PostgreSQL als Finanzquelle, Google OAuth nur als Identität, die versionierte Integer-Cent-Domäne, Single-User-Sicherheit und Last-known-good-Offline zentral. ADR 0001 und ADR 0003 erklären nur noch den implementierten historischen Ausgangspunkt. ## Grenzen und Sicherheitsannahmen diff --git a/docs/entscheidungen/0001-google-sheets-als-datenquelle.md b/docs/entscheidungen/0001-google-sheets-als-datenquelle.md index 2fae95b..1848a4a 100644 --- a/docs/entscheidungen/0001-google-sheets-als-datenquelle.md +++ b/docs/entscheidungen/0001-google-sheets-als-datenquelle.md @@ -6,7 +6,7 @@ > **Kanonisch für:** Begründung von Google Sheets als Quelldatenspeicher. > **Verwandte Dokumente:** [Schema v1](../referenz/finance-data-schema-v1.md), [ADR-Index](README.md) -- **Status:** Angenommen +- **Status:** Ersetzt durch [ADR 0013](0013-postgresql-als-finanzquelle.md) ## Kontext diff --git a/docs/entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md b/docs/entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md index 5c94640..4a62d89 100644 --- a/docs/entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md +++ b/docs/entscheidungen/0003-serverseitiger-google-zugriff-und-drive-file.md @@ -6,7 +6,7 @@ > **Kanonisch für:** Begründung des serverseitigen Tokenflusses und `drive.file`-Scopes. > **Verwandte Dokumente:** [Web-Sicherheit und OAuth](../grundlagen/web-sicherheit-und-oauth.md), [ADR-Index](README.md) -- **Status:** Angenommen +- **Status:** Ersetzt durch [ADR 0014](0014-google-oauth-nur-als-identitaet.md) ## Kontext diff --git a/docs/entscheidungen/0013-postgresql-als-finanzquelle.md b/docs/entscheidungen/0013-postgresql-als-finanzquelle.md new file mode 100644 index 0000000..559ba8b --- /dev/null +++ b/docs/entscheidungen/0013-postgresql-als-finanzquelle.md @@ -0,0 +1,77 @@ +# ADR 0013: PostgreSQL als Finanzquelle + +> **Zielgruppe:** Finance-, Backend- und Security-Entwickler. +> **Zweck und Lernziel:** Verbindliche Quelle, Persistenzgrenze und Übergang von Google Sheets nach PostgreSQL festlegen. +> **Voraussetzungen:** [Finanz-Domäne](../architektur/finanz-domaene.md), [ADR 0002](0002-versionierte-domaenengrenze-und-integer-cents.md) +> **Kanonisch für:** PostgreSQL als Finanzquelle, Google Sheets als Importformat und das v1-Persistenzmodell mit internem Owner. +> **Verwandte Dokumente:** [ADR 0014](0014-google-oauth-nur-als-identitaet.md), [Architekturüberblick](../architektur/ueberblick.md), [ADR-Index](README.md) + +- **Status:** Angenommen + +## Kontext + +Google Sheets ist im implementierten Stand gleichzeitig Bearbeitungsoberfläche und fachliche Quelle. Jeder produktive Finance-Read benötigt deshalb Google-Token, eine ausgewählte Datei und einen vollständigen `batchGet`- und Parserdurchlauf. Das erschwert eine gezielte In-App-Bearbeitung und bindet die Finanzdaten dauerhaft an eine externe Dateiintegration. + +Der bestehende `FinanceDataV1`-Vertrag, die Integer-Cent-Repräsentation und die reinen Selektoren haben sich dagegen bewährt. Der Quellenwechsel soll diese fachliche Grenze erhalten und weder ein Schema v2 noch eine neue Berechnungsschicht einführen. + +## Entscheidung + +Nach dem einmaligen Cutover ist PostgreSQL die einzige produktive Quelle der Finanzzeilen. Google Sheets ist ausschließlich ein Importformat für den bestehenden Datenstand: kein dauerhafter Sync, kein Zurückschreiben und kein Laufzeit-Fallback auf Sheets. + +Das Persistenzmodell bildet exakt die Quellen des heutigen `FinanceDataV1` ab: + +| Persistenz | Abbildung | +| --- | --- | +| `owners` | interne stabile Owner-ID und eindeutige Zuordnung zur aktuell erlaubten Google-Identität | +| `finance_meta` | `schemaVersion`, `asOf`, `currency`, `monthlyIncomeCents`, `salaryDay` | +| `accounts`, `account_snapshots` | Konten und ihre Stände | +| `pockets`, `pocket_snapshots` | Pockets und ihre Stände | +| `budget_items` | Budgetquellen einschließlich Betrag, Fälligkeit und Aktivstatus | +| `debts`, `debt_snapshots` | Schulden und ihre Stände | +| `debt_milestones`, `relief_milestones` | Restschuld- und Entlastungsmeilensteine | + +`owners.id` ist eine interne UUID. Google `sub` wird eindeutig auf diesen Owner abgebildet, ist aber nicht selbst der fachliche Schlüssel jeder Finanzzeile. Jede Finance-Tabelle trägt ein nicht-nullbares `owner_id`; Primär- und Fremdschlüssel schließen `owner_id` ein, damit Beziehungen nicht versehentlich über Ownergrenzen hinweg aufgelöst werden können. Der Server leitet den Owner ausschließlich aus der verifizierten Sitzung ab. Ein Client darf `owner_id` weder wählen noch überschreiben. + +`owner_id` ist Persistenz- und Isolationsinformation und wird nicht Bestandteil von `FinanceDataV1` oder der Browserantwort. Für diesen Schnitt bleibt genau eine Allowlist-Identität zugelassen. `owner_id` allein ist noch keine vollständige Multi-User-Sicherheitsgrenze; diese wird erst vor der Private Alpha umgesetzt. + +Geld wird als `bigint` in Cents gespeichert und an der JavaScript-Grenze weiterhin als sicherer Integer validiert. Reine Kalendertage werden als `date`, Ereigniszeitpunkte als `timestamptz` gespeichert. Bei Meilensteinen muss zusätzlich erhalten bleiben, ob der v1-Wert einen Monat (`YYYY-MM`) oder einen exakten Tag (`YYYY-MM-DD`) bezeichnet; eine Normalisierung auf den Monatsersten darf diese Information nicht verlieren. + +Ein Repository liest für den Session-Owner den vollständigen Quellenstand und baut daraus exakt ein validiertes `FinanceDataV1`. Fehlt `finance_meta`, existiert noch kein gültiger Finanzstand. Dieser Fall wird außerhalb von `FinanceDataV1` als eigener Anwendungszustand behandelt; die Anwendung erfindet keinen künstlichen leeren Snapshot. + +SQL erzwingt strukturelle Integrität, Typen, Eindeutigkeit und Owner-gebundene Referenzen. Snapshot-Auswahl, Summen, Fälligkeitsprojektion, `safeToSpend` und andere Finanzlogik bleiben in Parsern, Selektoren und dem View-Model. Abgeleitete Kennzahlen werden nicht als zweite Wahrheit persistiert. + +## Übergang + +Diese ADR legt das verbindliche Zielbild fest, beschreibt aber nicht den bereits implementierten Zustand. Bis zum Cutover darf der vorhandene Sheets-Lesepfad unverändert weiterlaufen; neue Funktionen bauen ihn nicht weiter aus. + +Die Umsetzung erfolgt in klaren Schritten: + +1. ACC-71 erstellt Schema und Repository-Lesepfad, ohne die fachliche Grenze zu ändern. +2. ACC-29 weist mit derselben anonymen Fixture identische Cents, Fälligkeiten und Snapshot-Auswahl für Parser- und PostgreSQL-Pfad nach. +3. ACC-66 importiert den privaten Bestand vollständig, prüft die Parität und schaltet die einzige produktive Quelle auf PostgreSQL um. +4. ACC-72 baut anschließend den eng begrenzten In-App-Editor. + +Der Cutover ist eindeutig. Ein dauerhafter Feature-Flag-Dualbetrieb oder stiller Rückfall auf Sheets ist nicht vorgesehen. + +## Begründung + +Die interne Owner-ID entkoppelt Finanzdaten von einem externen Identitätsanbieter, ohne bereits eine Mandanten- oder Rollenarchitektur zu bauen. Das unveränderte `FinanceDataV1` hält Parser, Cache, Selektoren, View-Model und UI stabil. Ein einziger produktiver Lesepfad verhindert divergierende Datenstände und unklare Fehlerbehandlung. + +## Erwogene Alternativen + +Google Sheets als dauerhafte Quelle beizubehalten würde Editor und Laufzeit weiter an Picker, Token und externe Verfügbarkeit binden. Ein dauerhafter Dual-Source-Betrieb erzeugt Konflikt- und Prioritätsregeln ohne Produktnutzen. JSON-Dokumente in einer einzelnen Spalte würden relationale Integrität und gezielte sichere Bearbeitung erschweren. Finanzlogik oder vorberechnete UI-Kennzahlen in SQL würden eine zweite Berechnungsquelle neben den getesteten Selektoren schaffen. Google `sub` direkt auf jede Finanzzeile zu schreiben wäre kurzfristig kleiner, koppelte die Daten aber unnötig an Google und erschwerte ACC-64. + +## Konsequenzen + +### Positiv + +Die App erhält eine selbst kontrollierte, transaktionale Finanzquelle und kann gezielte Bearbeitung anbieten. Bestehende Cent-, Cache- und Selektorverträge bleiben erhalten. Owner-Zuordnung und relationale Integrität sind von Anfang an explizit. + +### Negativ + +Schema, Migration, Repository und Schreibgrenzen müssen sorgfältig umgesetzt und gegen die bestehende Fixture geprüft werden. Backups enthalten künftig hochsensible Finanzzeilen. Der einmalige Cutover benötigt eine kontrollierte Import- und Rückfallplanung, darf aber keinen dauerhaften zweiten Produktionspfad hinterlassen. + +## Implementierung und Tests + +- Aktueller, noch zu ersetzender Vertrag: [FinanceDataV1](../../src/finance/types.ts), [Sheets-Parser](../../src/finance/parser.ts), [PostgreSQL-Verbindungsrepository](../../api/_lib/repository.ts) +- Geplanter Nachweis: ACC-71, ACC-29, ACC-66 und ACC-72 in Linear diff --git a/docs/entscheidungen/0014-google-oauth-nur-als-identitaet.md b/docs/entscheidungen/0014-google-oauth-nur-als-identitaet.md new file mode 100644 index 0000000..db6427e --- /dev/null +++ b/docs/entscheidungen/0014-google-oauth-nur-als-identitaet.md @@ -0,0 +1,52 @@ +# ADR 0014: Google OAuth nur als Identität + +> **Zielgruppe:** Backend-, Security- und Authentifizierungsentwickler. +> **Zweck und Lernziel:** Die verbleibende Rolle von Google nach dem Quellenwechsel und die erforderlichen Berechtigungen festlegen. +> **Voraussetzungen:** [Backend und Sicherheit](../architektur/backend-und-sicherheit.md), [ADR 0004](0004-single-user-sicherheitsmodell.md) +> **Kanonisch für:** Google OAuth als Identitätsanbieter ohne Picker-, Drive- oder Sheets-Laufzeitzugriff. +> **Verwandte Dokumente:** [ADR 0013](0013-postgresql-als-finanzquelle.md), [Web-Sicherheit und OAuth](../grundlagen/web-sicherheit-und-oauth.md), [ADR-Index](README.md) + +- **Status:** Angenommen + +## Kontext + +Der implementierte OAuth-Fluss fordert `drive.file`, erzwingt ein Refresh-Token und speichert es verschlüsselt, weil der Server die ausgewählte Tabelle bei jedem Finance-Read erneut lesen muss. Mit PostgreSQL als Finanzquelle entfällt dieser Zweck. Die bestehende verifizierte Google-Anmeldung und die serverseitige E-Mail-Allowlist können für den privaten Betrieb dennoch bestehen bleiben. + +## Entscheidung + +Google OAuth dient nach dem Cutover ausschließlich der Authentifizierung. Der Authorization-Code-Fluss mit State, Nonce und PKCE bleibt; der Ziel-Scope ist `openid email profile`. ID-Token-Signatur, Issuer, Audience, Nonce, verifizierte E-Mail und Allowlist werden weiterhin serverseitig geprüft. + +Google `sub` identifiziert die externe Anmeldung und wird eindeutig einem internen `owners.id` zugeordnet. Die signierte Sitzung trägt weiterhin die verifizierte Identität; Finanzzugriffe lösen daraus serverseitig den Owner auf. ADR 0004 bleibt für diesen Schnitt unverändert: Genau eine konfigurierte Identität darf eine Sitzung erhalten. + +Picker, `drive.file`, Drive-/Sheets-Laufzeitzugriff und die Pflicht zu einem dauerhaft gespeicherten Refresh-Token entfallen. Ein beim OAuth-Tausch erhaltenes kurzlebiges Access-Token wird nicht für Finance persistiert. Die bestehenden Picker-Endpunkte, Spreadsheet-Zustände und Tokenfelder sind Übergangscode und werden beim eindeutigen Cutover entfernt. + +Der einmalige Import aus ACC-66 ist ein kontrollierter Operator-Pfad außerhalb der normalen Produktnutzung. Er kann eine lokale oder einmalig gelesene Tabellenrepräsentation an den bestehenden `validateFinanceWorkbook()`-Parser übergeben, speichert aber keinen dauerhaften Google-Grant und führt keinen Hintergrundsync ein. + +Logout beendet weiterhin nur die Sitzung. Disconnect- und PIN-Recovery-Verhalten müssen beim Cutover neu benannt und so angepasst werden, dass das Entfernen einer Google-Anmeldung nicht beiläufig PostgreSQL-Finanzdaten löscht. Löschung oder Export der Finanzdaten benötigen später eine eigene ausdrückliche Aktion. + +## Übergang + +Diese ADR ist der verbindliche Zielzustand. Bis ACC-66 den importierten Datenbestand verifiziert und den Cutover ausführt, bleibt der bestehende `drive.file`- und Refresh-Token-Fluss funktionsfähig. Er wird nicht für neue Features erweitert. Erst der Cutover entfernt ihn aus Code, Konfiguration, API, UI und Betriebsdokumentation. + +## Begründung + +Die Trennung folgt Least Privilege: Eine Anmeldung benötigt keinen Zugriff auf Drive-Dateien. Sie reduziert gespeicherte Secrets, externe Fehlerfälle und die Reichweite eines kompromittierten Grants. Gleichzeitig vermeidet sie einen vorgezogenen Wechsel des Identitätsanbieters, solange die harte Single-User-Allowlist dem aktuellen Produktumfang entspricht. + +## Erwogene Alternativen + +`drive.file` vorsorglich zu behalten hätte nach dem Import keinen aktuellen Zweck. Google vollständig zu entfernen würde unnötig gleichzeitig Authentifizierung und Datenquelle umbauen. Ein dauerhafter Import- oder Sync-Grant würde Google Sheets faktisch als zweite Quelle erhalten. Eine offene Google-Anmeldung bleibt durch ADR 0004 ausgeschlossen. + +## Konsequenzen + +### Positiv + +Keine dauerhaften Google-Refresh-Tokens für Finance, keine Picker-Abhängigkeit und deutlich kleinere OAuth-Berechtigung. Identität und Finanzquelle besitzen getrennte Verantwortungen. + +### Negativ + +Der einmalige Import benötigt einen bewusst betriebenen Pfad. Der spätere Invite-only-Betrieb braucht über die Allowlist hinaus eine vollständige Authentifizierungs- und Isolationsprüfung in ACC-64. + +## Implementierung und Tests + +- Aktueller, noch zu ersetzender Fluss: [Google-Client](../../api/_lib/google.ts), [OAuth-Callback](../../api/auth/google/callback.ts), [Google-Verbindungsrepository](../../api/_lib/repository.ts) +- Geplanter Cutover: ACC-66; spätere Invite-only-Erweiterung: ACC-64 diff --git a/docs/entscheidungen/README.md b/docs/entscheidungen/README.md index 3a9d638..e916e5b 100644 --- a/docs/entscheidungen/README.md +++ b/docs/entscheidungen/README.md @@ -6,13 +6,13 @@ > **Kanonisch für:** ADR-Statusbegriffe und Entscheidungsindex. > **Verwandte Dokumente:** [ADR-Vorlage](../vorlagen/adr.md), [Dokumentationsindex](../README.md) -Zulässige Statuswerte sind `Vorgeschlagen`, `Angenommen`, `Ersetzt` und `Verworfen`. Alle nachweisbar implementierten Entscheidungen sind als `Angenommen` markiert. Eine spätere Änderung ersetzt eine ADR, statt ihre historische Begründung umzuschreiben. +Zulässige Statuswerte sind `Vorgeschlagen`, `Angenommen`, `Ersetzt` und `Verworfen`. `Angenommen` bedeutet, dass eine Entscheidung für die folgende Arbeit verbindlich ist; der tatsächliche Implementierungsstand bleibt davon getrennt dokumentiert. Eine spätere Änderung ersetzt eine ADR, statt ihre historische Begründung umzuschreiben. | ADR | Entscheidung | Status | | --- | --- | --- | -| [0001](0001-google-sheets-als-datenquelle.md) | Google Sheets als Datenquelle | Angenommen | +| [0001](0001-google-sheets-als-datenquelle.md) | Google Sheets als Datenquelle | Ersetzt durch 0013 | | [0002](0002-versionierte-domaenengrenze-und-integer-cents.md) | Versionierte Domänengrenze und Integer-Cents | Angenommen | -| [0003](0003-serverseitiger-google-zugriff-und-drive-file.md) | Serverseitiger Google-Zugriff und `drive.file` | Angenommen | +| [0003](0003-serverseitiger-google-zugriff-und-drive-file.md) | Serverseitiger Google-Zugriff und `drive.file` | Ersetzt durch 0014 | | [0004](0004-single-user-sicherheitsmodell.md) | Single-User-Sicherheitsmodell | Angenommen | | [0005](0005-last-known-good-und-offline.md) | Last-known-good und Offline | Angenommen | | [0006](0006-provider-selektoren-und-view-model.md) | Provider, Selektoren und View-Model | Angenommen | @@ -22,3 +22,5 @@ Zulässige Statuswerte sind `Vorgeschlagen`, `Angenommen`, `Ersetzt` und `Verwor | [0010](0010-gehaltsbezogene-faelligkeitsprojektion.md) | Gehaltsbezogene Fälligkeitsprojektion | Angenommen | | [0011](0011-lokaler-privacy-modus.md) | Lokaler Privacy-Modus | Angenommen | | [0012](0012-app-vorschau-und-lokaler-pin-lock.md) | App-Vorschau und lokaler PIN-Lock | Angenommen | +| [0013](0013-postgresql-als-finanzquelle.md) | PostgreSQL als Finanzquelle | Angenommen | +| [0014](0014-google-oauth-nur-als-identitaet.md) | Google OAuth nur als Identität | Angenommen | diff --git a/docs/produkt/roadmap.md b/docs/produkt/roadmap.md index a0f683e..67e7032 100644 --- a/docs/produkt/roadmap.md +++ b/docs/produkt/roadmap.md @@ -18,28 +18,26 @@ Die Roadmap ist eine Absichtserklärung, kein Funktionsversprechen. „Now“ be ## Now -- SSOT-Dokumentation und lokalen manuellen Dokumentationscheck fertigstellen. -- Reale Produktionsabläufe für OAuth, Picker, Sheets, PostgreSQL, Offline-Start, Trennen und Wiederverbinden durch den Eigentümer abnehmen. -- Golden- und Axe-Abdeckung für Demnächst und Privacy-Modus schließen. -- Bestehende Qualitätsprüfungen dauerhaft grün halten. +- Architektur für den Quellenwechsel verbindlich festlegen und die sheetgebundenen ADRs ersetzen. +- Das heutige `FinanceDataV1` mit internem `owner_id` in PostgreSQL abbilden und ownergebunden wieder als denselben Vertrag lesen. +- Sheet-Parser und PostgreSQL-Lesepfad mit derselben anonymen Fixture auf identische Cents, Fälligkeiten und Snapshot-Auswahl prüfen. +- Den bestehenden privaten Datenstand einmalig importieren und danach eindeutig auf PostgreSQL als einzige produktive Quelle umschalten. +- Einen eng begrenzten In-App-Editor für Stände, Beträge, Fälligkeiten und Aktivstatus bauen. -Appearance-Grundimplementierung, Accessibility-Pass, Branding, CI, Demnächst und Privacy-Modus sind erreicht und deshalb keine allgemeinen offenen Arbeitspakete. +Kein Teil dieses Schnitts führt Schema v2, öffentliche Registrierung, Mandanten-UI, dauerhaften Sheet-Sync oder Zurückschreiben nach Sheets ein. ## Next -- Datensparsames Monitoring und strukturierte Betriebsdiagnose. -- Rate-Limits und Missbrauchsschutz für den privaten Betrieb bewerten. -- Workbook-Onboarding durch ein anonymes Template und Vorabvalidierung verbessern. -- Cache-Zustand und lokale Löschaktionen transparenter machen. -- Visuelle und barrierefreie Abdeckung seltener Paletten- und Fehlerzustände ergänzen. +- Erst nachdem die eigene PostgreSQL-Datenhaltung im Alltag trägt: Invite-only-Authentifizierung und strikt getrennte Nutzerkonten umsetzen. +- Danach ein angstfreies geführtes Erst-Onboarding ohne Google-Sheets-Pflicht für die Private Alpha bauen. +- Vor Aufnahme weiterer Personen Isolation, Löschung, Recovery, Rate-Limits und datensparsame Betriebsdiagnose vollständig prüfen. ## Later -- Historische Trends und Prognosen. -- Finance Data Schema v2 nur bei konkretem Bedarf und expliziter Migration. -- Optionale Hinweise auf geänderte oder veraltete Daten ohne Polling. -- Weitere lokale Datenschutzkontrollen für Offline-Daten. +- `FinanceDataV1` erst nach dem Quellenwechsel und nur bei konkretem Produktbedarf versioniert erweitern. +- Mehrere Einkommensquellen, vollständige regelmäßige Zahlungen, Sparziele, Zins-/Vertragsdaten und monatliche Ist-Ausgaben jeweils als eigene fachliche Schritte bewerten. +- Historische Trends und Prognosen erst auf einer erklärbaren, migrierten Datengrundlage aufbauen. ## Nicht Teil der Roadmap -Multi-User, Mandantenverwaltung, öffentlicher SaaS-Betrieb, Banktransaktionen und stilles Bearbeiten der Finanzquelle sind keine geplanten Erweiterungen. Ebenso sind Depot-/Investmentanalyse, Gesamtvermögensverwaltung, Finanzproduktvermittlung, Steuer-/Buchhaltungsfunktionen und Wachstum allein zur Konkurrenz mit universellen Finanzplattformen keine strategischen Ziele. +Öffentliche Registrierung, öffentlicher SaaS-Betrieb, geteilte Haushalte ohne belegten Bedarf, Banktransaktionen und stilles Bearbeiten externer Quellen sind keine geplanten Erweiterungen. Ebenso sind Depot-/Investmentanalyse, Gesamtvermögensverwaltung, Finanzproduktvermittlung, Steuer-/Buchhaltungsfunktionen und Wachstum allein zur Konkurrenz mit universellen Finanzplattformen keine strategischen Ziele. diff --git a/docs/referenz/finance-data-schema-v1.md b/docs/referenz/finance-data-schema-v1.md index 56a94cb..a56b8ce 100644 --- a/docs/referenz/finance-data-schema-v1.md +++ b/docs/referenz/finance-data-schema-v1.md @@ -8,6 +8,8 @@ ## Grundvertrag +Im aktuell implementierten Übergangsstand ist dieser Vertrag zugleich die laufende Google-Sheets-Quelle. Nach dem beschlossenen Cutover aus [ADR 0013](../entscheidungen/0013-postgresql-als-finanzquelle.md) bleibt er als einmaliges Importformat erhalten; der normalisierte `FinanceDataV1`-Vertrag wird dann produktiv aus PostgreSQL gelesen. + Die ausgewählte native Google-Sheets-Datei enthält genau die benötigten zehn underscore-präfigierten Maschinen-Tabs. Sichtbare Hilfs-Tabs sind zulässig, werden aber ignoriert. Die App liest je Maschinen-Tab `A:Z` mit `UNFORMATTED_VALUE`, verändert keine Zelle und erwartet die Header in Zeile 1. Leere Datenzeilen werden ignoriert. Pflicht-Tabs: diff --git a/docs/referenz/konfiguration.md b/docs/referenz/konfiguration.md index dc1f20d..1a69bab 100644 --- a/docs/referenz/konfiguration.md +++ b/docs/referenz/konfiguration.md @@ -27,20 +27,21 @@ Alle Variablen sind serverseitig erforderlich. `VERCEL_ENV=production` oder `NOD | Variable | Vertrag | | --- | --- | -| `VITE_USE_MOCK_API` | optional; nur exakt `true`, Vite Development und `import.meta.env.DEV` aktivieren anonyme Mock-API | +| `VITE_USE_MOCK_API` | optionaler lokaler Schalter; nur exakt `true` zusammen mit Vite Development aktiviert die anonyme Mock-API | +| `VITE_VERCEL_ENV` | öffentliche Vercel-Systemvariable; exakt `preview` aktiviert automatisch die anonyme Mock-API, `production` niemals | | `ACCURA_SOURCE_REPOSITORY_URL` | optionaler Build-Override; vollständige GitHub-Repository-URL, nur gemeinsam mit `ACCURA_SOURCE_COMMIT_SHA` | | `ACCURA_SOURCE_COMMIT_SHA` | optionaler Build-Override; vollständiger 40-stelliger Git-Commit-SHA, nur gemeinsam mit `ACCURA_SOURCE_REPOSITORY_URL` | | `VITE_VERCEL_GIT_REPO_OWNER` | öffentlicher, von Vercel bereitgestellter Repository-Owner für den versionsgebundenen Source-Link | | `VITE_VERCEL_GIT_REPO_SLUG` | öffentlicher, von Vercel bereitgestellter Repository-Name für den versionsgebundenen Source-Link | | `VITE_VERCEL_GIT_COMMIT_SHA` | öffentlicher, vollständiger Vercel-Deployment-Commit für den versionsgebundenen Source-Link | -Jede `VITE_`-Variable wird grundsätzlich als browseröffentlich behandelt. Niemals Secret, Token, Datenbank-URL oder persönliche Finanzdaten mit diesem Präfix setzen. +Jede `VITE_`-Variable wird grundsätzlich als browseröffentlich behandelt. Niemals Secret, Token, Datenbank-URL oder persönliche Finanzdaten mit diesem Präfix setzen. Vercels automatische Systemvariablen müssen für das Projekt aktiviert bleiben, damit Preview-Builds eindeutig erkannt werden. Explizite `ACCURA_SOURCE_*`-Overrides haben Vorrang vor den Vercel-Git-Werten. Ohne beide Quellen verwendet ein lokaler Build `git rev-parse HEAD`. Ein Produktionsbuild ohne gültigen vollständigen SHA bricht ab; ausschließlich der Dev-Server darf auf `master` zurückfallen. Repository-URL, vollständiger SHA, Kurz-SHA und daraus abgeleitete Rechtslinks werden als öffentliche Konstanten in das Browser-Bundle eingebettet und enthalten keine Geheimnisse. ## Konsistenzregeln -Wenn `APP_ORIGIN=https://accura.example` lautet, muss der Callback `https://accura.example/api/auth/google/callback` lauten. Preview-URLs benötigen entweder bewusst eigene Google-Redirect-Einträge und passende Variablen oder dürfen den realen OAuth-Fluss nicht verwenden. Development und Production sollten eigene Datenbank/Secrets nutzen. +Wenn `APP_ORIGIN=https://accura.example` lautet, muss der Callback `https://accura.example/api/auth/google/callback` lauten. Die Vercel-Preview verwendet ausschließlich anonyme Mock-Daten und darf keine produktiven Google- oder PostgreSQL-Secrets benötigen. Ein zukünftiges reales Integrationsenvironment müsste bewusst eigene Redirect-Einträge, Datenbank und Secrets erhalten. ## Rotation diff --git a/docs/referenz/quellcode-karte.md b/docs/referenz/quellcode-karte.md index c625ca4..47311e2 100644 --- a/docs/referenz/quellcode-karte.md +++ b/docs/referenz/quellcode-karte.md @@ -13,7 +13,7 @@ | `src/App.tsx` | App-Shell, Connection States, Zielnavigation, Lazy Loading | | `src/screens/` | vier Finance-Ansichten | | `src/components/` | gemeinsame UI-, Dialog-, Navigation-, Privacy- und Diagrammrollen | -| `src/data/` | Browser-API, Picker, Finance-Provider, IndexedDB-Finance-Cache | +| `src/data/` | Browser-API, Picker, Finance-Provider, Laufzeitmodus und IndexedDB-Finance-Cache | | `src/finance/` | Schemaheader, Parser, Laufzeitschema, Typen, Selektoren, Upcoming, View-Model | | `src/appearance/` | Präferenz, Paletten, Tokens, Worker und Wallpaper-IndexedDB | | `src/privacy/` | Geldmaskierung, App-Schutz-/PIN-Store, Expressive-PIN-Formen und gemeinsamer Context | @@ -21,6 +21,7 @@ | `src/design/` | zentrale CSS-Tokens, Schriftimport, Diagramm-/Motion-Helfer | | `src/styles/` | Basis, Shell, Primitives, Screens, Zustände, Responsive Regeln | | `src/mocks/` | ausschließlich anonyme Entwicklungsdaten und Mock-API | +| `build/` | geprüfte Buildzeit-Auflösung für Source-Link und Preview-Modus | | `api/` | Vercel Function Entry Points | | `api/_lib/` | Konfiguration, HTTP, Security, Google, Repository, Finance-Service | | `migrations/` | PostgreSQL-Migrationen | diff --git a/src/main.tsx b/src/main.tsx index b97bf32..aa8447c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -20,7 +20,7 @@ const initialProtection = initializeAppProtectionBeforeRender(); let financeApi = productionFinanceApi; let mockPicker: PickerLauncher | undefined; -if (import.meta.env.DEV && import.meta.env.VITE_USE_MOCK_API === 'true') { +if (__ACCURA_MOCK_API_ENABLED__) { financeApi = (await import('./mocks/mockFinanceApi')).mockFinanceApi; mockPicker = async () => ({ id: 'mock-spreadsheet-id', name: 'Anonyme Beispieldaten' }); } diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 4d55453..fff27bc 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -3,3 +3,4 @@ declare const __ACCURA_SOURCE_COMMIT_SHA__: string; declare const __ACCURA_SOURCE_SHORT_SHA__: string; declare const __ACCURA_SOURCE_URL__: string; +declare const __ACCURA_MOCK_API_ENABLED__: boolean; diff --git a/vite.config.ts b/vite.config.ts index d7f62ec..281cef5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,13 +2,16 @@ import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; import { VitePWA } from 'vite-plugin-pwa'; import { accuraManifest } from './src/branding'; +import { resolveMockApiEnabled } from './build/financeRuntimeMode'; import { resolveSourceInformation } from './build/sourceInformation'; export default defineConfig(({ command }) => { const source = resolveSourceInformation({ command }); + const mockApiEnabled = resolveMockApiEnabled({ command }); return { define: { + __ACCURA_MOCK_API_ENABLED__: JSON.stringify(mockApiEnabled), __ACCURA_SOURCE_COMMIT_SHA__: JSON.stringify(source.commitSha), __ACCURA_SOURCE_SHORT_SHA__: JSON.stringify(source.shortSha), __ACCURA_SOURCE_URL__: JSON.stringify(source.sourceUrl),