From 650a74da97e33c8fb022204015232201eae84e85 Mon Sep 17 00:00:00 2001 From: knuthtimo-lab Date: Wed, 19 Aug 2026 19:19:13 +0200 Subject: [PATCH] first commit --- README.md | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100755 README.md diff --git a/README.md b/README.md new file mode 100755 index 0000000..1e598fd --- /dev/null +++ b/README.md @@ -0,0 +1,330 @@ +# ScanReceipts — Receipt Scanner App + +Next.js 15 App Router App zum Scannen/Digitalisieren von Belegen (KI-Extraktion, +Review-UI, Export nach XLSX/CSV/JSON). Lokal läuft die App **ohne Datenbank** +(Local-First IndexedDB im Browser); PostgreSQL wird für Accounts, Abos +(Stripe) und den Admin-Bereich gebraucht. + +Diese Datei ist der Einstiegspunkt. Tiefere Details stehen in: + +| Thema | Datei | +|---|---| +| Auth / SMTP / Google Login im Detail | [AUTH_SETUP_GUIDE.md](AUTH_SETUP_GUIDE.md) | +| Stripe / Preise / Webhooks im Detail | [STRIPE_SETUP_GUIDE.md](STRIPE_SETUP_GUIDE.md) | +| nginx / Security-Header / blockierte Pfade | [docs/SECURITY_DEPLOYMENT.md](docs/SECURITY_DEPLOYMENT.md) | +| Architektur, Code-Layout, Feature-Liste | [PROJECT.md](PROJECT.md) | +| Was beim Security-Hardening gemacht wurde | [SECURITY_HARDENING.md](SECURITY_HARDENING.md), [SECURITY_VERIFICATION.md](SECURITY_VERIFICATION.md) | + +--- + +## 1. Voraussetzungen + +- Node.js 20+ und npm +- Docker Desktop (für Postgres lokal und/oder für den Produktions-Build) +- Ein `.env.local` (siehe unten) — ohne `DATABASE_URL` läuft die App im + Local-Only-Modus (kein Login/Stripe, aber Scannen funktioniert) + +> **Achtung Port-Konflikt:** Auf dieser Maschine laufen ~10 Docker-Stacks. Port +> `5432` ist von einem anderen Projekt belegt — dieses Projekt nutzt bewusst +> **`5436`** auf dem Host (siehe `docker-compose.yml`). Beim Anlegen von +> `DATABASE_URL` in `.env.local` immer `localhost:5436` verwenden, nicht `5432`. + +--- + +## 2. Schnellstart (lokale Entwicklung) + +```bash +npm install +cp .env.example .env.local +``` + +`.env.local` öffnen und mindestens `OPENROUTER_API_KEY` (oder einen anderen +Vision-Key) eintragen, damit das Scannen funktioniert. Alles andere +(Datenbank, Stripe, Google-Login, SMTP) ist optional für den reinen +Scan-Workflow. + +**Nur Postgres per Docker starten** (App läuft weiter direkt mit `npm run dev`, +nicht im Container): + +```bash +docker compose up -d postgres +``` + +Postgres ist danach unter `localhost:5436` erreichbar. In `.env.local`: + +``` +DATABASE_URL=postgresql://receipt_user:receipt_secure_password@localhost:5436/receipt_scanner +``` + +**Dev-Server starten:** + +```bash +npm run dev +``` + +→ http://localhost:3000. Der erste Request, der die DB anfasst, legt das +Schema automatisch an (siehe Abschnitt 3.1) — ein manueller Migrationsschritt +ist für lokale Entwicklung **nicht zwingend nötig**. + +**Server neustarten:** einfach `Strg+C` und `npm run dev` erneut ausführen. +Bei hängenden Prozessen/kaputtem `.next`-Cache (kommt auf dieser Maschine +gelegentlich vor): + +```bash +# .next Ordner löschen und neu starten +rm -rf .next +npm run dev +``` + +--- + +## 3. Datenbank + +Zwei Rollen sind vorgesehen (siehe [PROJECT.md](PROJECT.md#appendix-database-least-privilege) +für die volle Begründung): + +- `receipt_user` — **Owner-Rolle**, macht Migrationen/Schema-Änderungen (DDL). +- `receipt_app` — **Runtime-Rolle**, nur SELECT/INSERT/UPDATE/DELETE, kein DDL. + Wird von `scripts/db-permissions.sql` angelegt. In Produktion sollte die App + mit dieser eingeschränkten Rolle laufen (`APP_DATABASE_URL`), Migrationen + laufen weiter über die Owner-Rolle (`DATABASE_URL`). + +### 3.1 Datenbank neu erstellen / Schema anlegen + +Drei gleichwertige Wege, je nach Situation: + +1. **Automatisch (einfachster Weg):** Einfach die App starten + (`npm run dev` oder den Docker-Container). Beim ersten DB-Zugriff prüft + `src/lib/db/init.ts`, ob das Schema existiert, und legt es sonst automatisch + an (über die `drizzle`-Migrationsdateien, mit idempotentem SQL-Fallback, + falls die Migrationsdateien zur Laufzeit fehlen — z. B. im Docker-Image). +2. **Explizit per Drizzle Kit** (empfohlen während der Entwicklung, wenn man + das Schema in `src/lib/schema/db.ts` ändert): + ```bash + npm run db:generate # neue Migrationsdatei aus Schema-Änderungen erzeugen + npm run db:push # Schema direkt in die DB pushen (ohne Migrationsdatei) + npm run db:studio # Drizzle Studio GUI zum Browsen der Daten + ``` +3. **Frisches Docker-Volume:** `docker compose up postgres` auf einem noch nie + benutzten Volume führt automatisch `scripts/db-permissions.sql` aus + (Postgres-Init-Mechanismus) — legt dabei direkt auch die Runtime-Rolle + `receipt_app` an. + +### 3.2 Datenbank zurücksetzen (alle Daten löschen) + +**Docker-Setup (empfohlen für lokal):** + +```bash +docker compose down +docker volume rm receiptscannerapp_postgres_data # Name ggf. mit `docker volume ls` prüfen +docker compose up -d postgres +``` + +Das erzeugt ein komplett frisches Volume; Schema + Berechtigungen werden beim +nächsten App-Start automatisch wieder angelegt (siehe 3.1). + +**Ohne Docker / bestehende DB behalten, nur Tabellen leeren:** per `psql` oder +Drizzle Studio (`npm run db:studio`) die Tabellen manuell leeren — es gibt +aktuell kein `db:reset`-Script, das gezielt nur Daten löscht. + +### 3.3 Berechtigungen (Least-Privilege-Rolle) anwenden/prüfen + +Nötig, wenn eine **bereits bestehende** Datenbank auf die eingeschränkte +Runtime-Rolle nachgerüstet werden soll (bei einem frischen Docker-Volume passiert +das automatisch, siehe 3.1 Punkt 3): + +```bash +node scripts/apply-db-permissions.mjs # legt/aktualisiert die Rolle receipt_app +node scripts/verify-db-permissions.mjs # prüft: kein Superuser, kein DDL, DML funktioniert +``` + +Beide Scripts lesen `DATABASE_URL` aus `.env.local` (Owner-Verbindung nötig). + +### 3.4 Admin-User anlegen + +```bash +node scripts/create-admin.ts +``` + +Nutzt `ADMIN_EMAILS` (erste Adresse in der kommagetrennten Liste) und +optional `ADMIN_PASSWORD` (Default: `fiesta` — **in Produktion immer setzen**) +aus `.env.local`. Legt den User an oder setzt Passwort + E-Mail-Verifizierung +zurück, falls er schon existiert. + +--- + +## 4. Website starten / neustarten + +### 4.1 Lokal (ohne Docker) + +```bash +npm run dev # Entwicklung, mit Turbopack + Hot Reload +``` + +```bash +npm run build # Produktions-Build +npm run start # Produktions-Server aus dem Build starten (Port 3000) +``` + +### 4.2 Kompletter Stack per Docker Compose (App + Postgres) + +```bash +docker compose up -d --build # baut das Image und startet App + DB im Hintergrund +docker compose logs -f app # Logs der App live mitlesen +docker compose ps # Status/Healthchecks prüfen +``` + +**Neustarten** (z. B. nach `.env`-Änderungen, die kein Rebuild brauchen): + +```bash +docker compose restart app +``` + +**Neu bauen und neustarten** (nach Code-Änderungen): + +```bash +docker compose up -d --build app +``` + +**Alles stoppen** (Daten bleiben im Volume erhalten): + +```bash +docker compose down +``` + +`docker-compose.yml` liest seine Werte aus der Shell-Umgebung bzw. einer +`.env`-Datei im Repo-Root (nicht zu verwechseln mit `.env.local`, das nur +Next.js selbst liest) — für den Docker-Weg müssen die Variablen also in `.env` +stehen, siehe `.env.example` als Vorlage. + +--- + +## 5. Live-Server / Deployment-Checkliste + +### 5.1 Environment-Variablen + +Alle Variablen sind in [.env.example](.env.example) dokumentiert (mit +Erklärung, wofür sie sind). Kurzfassung, was auf dem Live-Server **zusätzlich +zur lokalen Config** wichtig ist: + +- `NODE_ENV=production` +- `NEXT_PUBLIC_APP_URL` → **muss** `https://` sein (die App ist HTTPS-only, + erzwingt HSTS). Wird zur Build-Zeit in den Client-Bundle eingebacken, also + als Docker-Build-Arg mitgeben (macht `docker-compose.yml` automatisch über + `build.args`). +- `COOKIE_DOMAIN` setzen, falls Dashboard/Admin auf Subdomains laufen + (z. B. `.scan-receipts.app`), sonst leer lassen. +- `CORS_ORIGINS` nur setzen, wenn wirklich Cross-Origin-API-Zugriff gebraucht + wird (nie `*`). +- `SMTP_*` sind in Produktion **Pflicht** — ohne SMTP schlägt die Registrierung + bewusst fehl, statt einen unbestätigbaren Account anzulegen. +- `GOOGLE_CLIENT_ID`/`SECRET` nur wenn Google-Login aktiv sein soll (Redirect-URI + in der Google Cloud Console muss exakt `/api/auth/google/callback` + sein). +- `STRIPE_*` siehe [STRIPE_SETUP_GUIDE.md](STRIPE_SETUP_GUIDE.md) — Webhook-Secret + ist pflicht, sonst werden keine Lizenzen vergeben. +- `ADMIN_EMAILS` + starkes `ADMIN_PASSWORD` für `scripts/create-admin.ts`. +- `APP_DATABASE_URL` + `APP_DATABASE_PASSWORD` setzen und die App damit laufen + lassen (nicht mit der Owner-`DATABASE_URL`) — siehe Abschnitt 3. + +Es liegt bereits ein `.env.deployment` als Referenz im Repo-Root — das ist die +Vorlage mit den auf dem Live-Server tatsächlich benötigten Keys (Werte selbst +niemals ins Git-Repo committen). + +### 5.2 Erstmaliges Deployment + +```bash +# auf dem Server, im Projektverzeichnis +docker compose up -d --build +``` + +1. Postgres startet zuerst, Healthcheck muss grün sein, bevor die App startet + (`depends_on: condition: service_healthy`, ist in `docker-compose.yml` + schon konfiguriert). +2. Auf einem frischen Volume werden Runtime-Rolle + Berechtigungen automatisch + angelegt (Abschnitt 3.1 Punkt 3). +3. Schema wird beim ersten Request automatisch angelegt (Abschnitt 3.1 Punkt 1). +4. Admin-User anlegen: `node scripts/create-admin.ts` (mit Produktions-`.env.local` + bzw. den Server-Umgebungsvariablen). +5. **nginx davorschalten** — die App selbst bindet nur intern an + `127.0.0.1:3000`, TLS/HSTS/Security-Header kommen von nginx: + - `nginx.conf.example` nach `/etc/nginx/conf.d/receipt-scanner.conf` + kopieren, Platzhalter (`example.com`, Zertifikatspfade) ersetzen. + - TLS-Zertifikat besorgen (z. B. Let's Encrypt). + - `nginx -t && systemctl reload nginx`. + - Details/Begründung: [docs/SECURITY_DEPLOYMENT.md](docs/SECURITY_DEPLOYMENT.md). +6. Stripe-Webhook-Endpoint auf `https:///api/stripe/webhook` im + Stripe-Dashboard eintragen (siehe STRIPE_SETUP_GUIDE.md Abschnitt 5). + +### 5.3 Redeploy nach Code-Änderungen + +```bash +git pull +docker compose up -d --build app +``` + +Datenbank-Migrationen laufen automatisch beim ersten Request nach dem Neustart +mit (Abschnitt 3.1). Bei Schema-Änderungen, die Drizzle nicht automatisch +sauber migrieren kann, vorher lokal gegen eine Kopie der Prod-DB mit +`npm run db:generate` eine Migration erzeugen und testen. + +### 5.4 Neustart ohne Rebuild + +```bash +docker compose restart app +``` + +### 5.5 Logs & Healthcheck prüfen + +```bash +docker compose logs -f app +docker compose logs -f postgres +curl -f http://127.0.0.1:3000/api/auth/providers # das nutzt auch der Docker-Healthcheck +``` + +### 5.6 Backup + +Das Postgres-Datenverzeichnis liegt im benannten Volume `postgres_data`. Dump +vor jedem größeren Deployment: + +```bash +docker exec scanreceipts_postgres pg_dump -U receipt_user receipt_scanner > backup_$(date +%Y%m%d_%H%M%S).sql +``` + +--- + +## 6. Nützliche Scripts (Auswahl) + +| Command | Zweck | +|---|---| +| `npm run dev` / `build` / `start` | Dev-Server / Produktions-Build / Produktions-Server | +| `npm run db:generate` / `db:push` / `db:studio` | Drizzle-Migrationen erzeugen / pushen / GUI | +| `npm run test` | E2E-Testsuite (`tests/e2e/runner.ts`) | +| `npm run test:auth` | Auth/DB-Integrationstest | +| `node scripts/create-admin.ts` | Admin-User anlegen/zurücksetzen | +| `node scripts/apply-db-permissions.mjs` | Least-Privilege-Runtime-Rolle anlegen/aktualisieren | +| `node scripts/verify-db-permissions.mjs` | Rechte-Setup verifizieren | +| `node scripts/verify_sensitive_paths.mjs` | Prüft, dass sensible Pfade (`.env`, `.git`, …) blockiert sind | +| `node scripts/verify_cors.mjs` | CORS-Policy verifizieren | +| `node scripts/verify_rate_limit.mjs` | Rate-Limiting (Passwort-Reset u. a.) verifizieren | + +Weitere `verify_*`-Scripts in `scripts/` decken einzelne Security-Hardening- +Maßnahmen ab (siehe [SECURITY_HARDENING.md](SECURITY_HARDENING.md) für den +Kontext, wofür jedes Script steht). + +--- + +## 7. Troubleshooting + +- **"DATABASE_URL is not set"** → `.env.local` fehlt oder wurde nicht geladen; + Next.js liest `.env.local` automatisch, Node-Scripts (drizzle-kit, die + `scripts/*.mjs`) laden es teils manuell — prüfen, ob die Datei existiert. +- **Port `5432` schon belegt** → dieses Projekt läuft absichtlich auf `5436`, + siehe Kasten oben in Abschnitt 1. +- **`.next`-Cache kaputt / komische Build-Fehler** → `rm -rf .next` und neu + starten (ist auf dieser Maschine schon mehrfach vorgekommen, siehe die + `.next-corrupt-*`-Ordner im Repo-Root). +- **Google-Login-Button erscheint nicht** → `GOOGLE_CLIENT_ID`/`SECRET` fehlen; + der Button rendert nur, wenn beide gesetzt sind. +- **Signup schlägt mit `mail_failed` fehl** → in Produktion (`NODE_ENV=production`) + ist SMTP Pflicht, es gibt keinen Dev-Fallback.