eac86ec655
Dokumentiert Kompass-Dial, Benutzerprofil, Feedback/Ntfy, Demo-URL, Tests und aktualisierte Env-Variablen. Co-authored-by: Cursor <cursoragent@cursor.com>
275 lines
13 KiB
Markdown
275 lines
13 KiB
Markdown
# Kapteins Daagbok
|
|
|
|
Digitales Yacht-Logbuch als Progressive Web App (PWA) — **kostenlos**, **werbefrei**, offline-fähig, End-to-End-verschlüsselt und mit Passkey-Anmeldung.
|
|
|
|
**Live:** [kapteins-daagbok.eu](https://kapteins-daagbok.eu) · **Demo:** [kapteins-daagbok.eu/demo](https://kapteins-daagbok.eu/demo)
|
|
|
|
## Überblick
|
|
|
|
Kapteins Daagbok richtet sich an private Skipper und Yachtbesitzer, die ihr Bordlogbuch digital führen möchten. Die App speichert Schiffsdaten, Crew-Profile und Reisetage (Törns) in einem Format, das an übliche nautische Logbuch-Vorlagen angelehnt ist.
|
|
|
|
Alle sensiblen Inhalte werden **clientseitig verschlüsselt** (Web Crypto API). Der Server sieht nur ciphertext — eine Zero-Knowledge-Architektur. Daten liegen zusätzlich lokal in IndexedDB (Dexie.js) und synchronisieren im Hintergrund, sodass die App **auch offline** auf See nutzbar ist.
|
|
|
|
## Funktionen
|
|
|
|
- **Passkey-Authentifizierung** (WebAuthn) mit optionaler Recovery-Phrase und lokalem PIN-Fallback
|
|
- **Mehrere Logbücher** pro Benutzerkonto — eigene Logbücher und per Einladung geteilte Logbücher (Crew-Zugang) klar getrennt
|
|
- **Reisetage** mit Hafen, Wetter, Tankständen, Ereignissen und Tagesnummer
|
|
- **Kompass-Dial** für MgK- und RwK-Kurse — Ring-Eingabe, Gradfeld, Schrittweite 1°/5°/10° (maritime Orientierung: 0° = Nord)
|
|
- **GPS-Tracks** (GPX/KML/GeoJSON-Upload, Karte, Statistiken)
|
|
- **Foto-Anhänge** pro Reisetag
|
|
- **Passkey-Signaturen** für Skipper und Crew (hybride elektronische Signatur)
|
|
- **Schiffsdaten** und **Crew-Profile** (Skipper + Mitglieder)
|
|
- **Statistik-Dashboard** — Strecken, Verbrauch, Segel/Motor, Hafenkette (pro Logbuch oder accountweit)
|
|
- **Benutzerprofil** — kontoweite Einstellungen: Darstellung (Theme, Hell/Dunkel), OpenWeatherMap-API-Key, Web Push, PWA-Installation, Onboarding-Tour, Passkey-Verwaltung, Account-Statistik
|
|
- **Kollaboration** — Crew per Einladungslink einladen (Schreib- oder Lesezugriff)
|
|
- **Push-Benachrichtigungen** (optional) — Logbuch-Eigner per Web Push informieren, wenn Crew Änderungen synchronisiert (ohne Klartext-Inhalte; Opt-in im Benutzerprofil)
|
|
- **Read-only-Freigabe** — öffentlicher Lese-Link für Dritte
|
|
- **Export** — PDF pro Reisetag, CSV-Download/-Teilen
|
|
- **Backup & Wiederherstellung** — vollständiges verschlüsseltes Logbuch-Backup (Einträge, Fotos, GPS, Crew, Schiff) als `.daagbok.json`; Restore auf gleichem oder neuem Account
|
|
- **Feedback** — Bug-, Feature- und allgemeine Rückmeldungen aus der App (serverseitig via [Ntfy](https://ntfy.sh) oder self-hosted)
|
|
- **PWA** — installierbar auf iOS/Android, Offline-Modus, Update-Hinweise
|
|
- **Mehrsprachig** — Deutsch und Englisch
|
|
- **Demo-Logbuch & Onboarding-Tour** für neue Nutzer (auch unter `/demo` ohne Anmeldung)
|
|
|
|
### Benutzerprofil vs. Logbuch-Einstellungen
|
|
|
|
| Bereich | Inhalt |
|
|
|---------|--------|
|
|
| **Benutzerprofil** | Theme, Farbschema, Wetter-API-Key, Push, PWA, Tour, Passkeys, Account löschen |
|
|
| **Logbuch-Einstellungen** | Crew-Einladungen, öffentliche Freigabe, Backup & Wiederherstellung (nur Eigner) |
|
|
|
|
## Architektur
|
|
|
|
```
|
|
┌─────────────────┐ HTTPS/API ┌─────────────────┐
|
|
│ React PWA │ ◄──────────────────► │ Express API │
|
|
│ Vite + Dexie │ (nur ciphertext) │ Prisma + PG │
|
|
│ IndexedDB │ │ PostgreSQL │
|
|
└─────────────────┘ └─────────────────┘
|
|
```
|
|
|
|
| Schicht | Technologie |
|
|
|---------|-------------|
|
|
| Frontend | React 19, TypeScript, Vite, vite-plugin-pwa |
|
|
| Lokaler Speicher | Dexie.js (IndexedDB), Hintergrund-Sync |
|
|
| Backend | Node.js, Express, Prisma |
|
|
| Datenbank | PostgreSQL 16 |
|
|
| Auth | WebAuthn (Passkeys) + signiertes HttpOnly-Session-Cookie (`daagbok_session`) |
|
|
| Krypto | Web Crypto API (AES-GCM), BIP39 Recovery |
|
|
| Push (optional) | Web Push (VAPID), Custom Service Worker (`injectManifest`) |
|
|
| Feedback (optional) | Ntfy (HTTP Publish) |
|
|
|
|
### Rollen & Zugriff
|
|
|
|
| Rolle | Bedeutung |
|
|
|-------|-----------|
|
|
| **Owner** | Logbuch angelegt; voller Zugriff, Einladungen, Backup, Löschen; optional Push bei Crew-Änderungen |
|
|
| **Collaborator (WRITE)** | Per Einladung; Einträge bearbeiten und als Crew signieren |
|
|
| **Collaborator (READ)** | Nur Lesen (z. B. öffentlicher Share-Link) |
|
|
|
|
Skipper- und Crew-Profile im Logbuch sind **Inhaltsdaten** (verschlüsselt), nicht an den Account gebunden. Ein Account kann gleichzeitig Owner eines eigenen und Collaborator in fremden Logbüchern sein.
|
|
|
|
### Authentifizierung & Session
|
|
|
|
| Schicht | Verhalten |
|
|
|---------|-----------|
|
|
| **Login** | WebAuthn (`/api/auth/login-verify`) — danach HttpOnly-Cookie, 7 Tage gültig |
|
|
| **API-Aufrufe** | Cookie `credentials: 'include'` (Client: `apiFetch`) — kein `X-User-Id` |
|
|
| **Master-Key** | Nur im RAM; nach Reload Entsperren per Passkey oder lokalem PIN |
|
|
| **Step-up** | Konto löschen, PRF-Enrollment: frische Passkey-Bestätigung (`/api/auth/reauth-*`) |
|
|
| **Sync WRITE** | Server lehnt Schreib-Sync für Collaborator mit `READ` ab |
|
|
|
|
Öffentliche Routen (ohne Session): Registrierung/Login-Optionen, Einladungsdetails, Read-only-Share (`share-pull`), Health-Check, VAPID-Public-Key.
|
|
|
|
## Backup & Wiederherstellung
|
|
|
|
Nur der **Logbuch-Eigner** kann unter **Logbuch-Einstellungen → Backup & Wiederherstellung** ein vollständiges Backup erstellen:
|
|
|
|
1. Backup-Passphrase wählen (min. 8 Zeichen, getrennt von der Datei aufbewahren)
|
|
2. Download als `.daagbok.json` — enthält alle verschlüsselten Payloads inkl. **Fotos** und GPS-Tracks
|
|
3. **Wiederherstellen** in einem beliebigen Account (nach Registrierung/Login): Datei + Passphrase
|
|
|
|
Vor dem Löschen eines Logbuchs weist die App auf diese Funktion hin. Crew-Einladungen und Passkey-Signaturen werden nicht mitübertragen — Inhalte bleiben lesbar, Signaturen auf neuem Account ggf. nicht mehr verifizierbar.
|
|
|
|
## Push-Benachrichtigungen (optional)
|
|
|
|
Logbuch-**Eigner** können im **Benutzerprofil** Web Push aktivieren. Sobald ein eingeladenes Crewmitglied mit Schreibrechten (`WRITE`) Änderungen synchronisiert, erhält der Owner eine **generische** Benachrichtigung — der Server kennt keine Logbuch-Inhalte (Zero-Knowledge).
|
|
|
|
| Aspekt | Verhalten |
|
|
|--------|-----------|
|
|
| Auslöser | Erfolgreicher Sync-Push durch Collaborator (`create`/`update`) |
|
|
| Aggregation | Mehrere Änderungen in einem Sync → eine Benachrichtigung pro Logbuch |
|
|
| Drosselung | Max. eine Push-Nachricht pro Logbuch alle 3 Minuten |
|
|
| Klick | Öffnet die App auf dem betroffenen Logbuch |
|
|
|
|
**Voraussetzungen:**
|
|
|
|
- HTTPS (Produktion)
|
|
- VAPID-Schlüssel auf dem Server (`VAPID_PUBLIC_KEY`, `VAPID_PRIVATE_KEY`, `VAPID_SUBJECT`)
|
|
- Browser-Berechtigung „Benachrichtigungen“; auf **iOS** installierte PWA ab iOS 16.4+
|
|
|
|
Schlüssel erzeugen: `npx web-push generate-vapid-keys` (im `server/`-Verzeichnis oder global).
|
|
|
|
Ausführlicher Implementierungs- und Testplan: [docs/push-notifications-plan.md](docs/push-notifications-plan.md).
|
|
|
|
## Feedback (optional)
|
|
|
|
Eingeloggte Nutzer können über das Feedback-Formular in der App Rückmeldungen senden. Der Server leitet sie an einen **Ntfy**-Topic weiter (kein Klartext-Logbuch auf dem Server).
|
|
|
|
| Variable | Bedeutung |
|
|
|----------|-----------|
|
|
| `NTFY_SERVER` | Basis-URL (Standard: `https://ntfy.sh`) |
|
|
| `NTFY_TOPIC` | Topic-Name (ohne URL) |
|
|
| `NTFY_TOKEN` | Optional: Access-Token für geschützte Topics |
|
|
|
|
Ohne `NTFY_TOPIC` antwortet die API mit „nicht konfiguriert“. Rate-Limiting und einfacher Spam-Schutz sind serverseitig aktiv.
|
|
|
|
## Projektstruktur
|
|
|
|
```
|
|
kapteins-daagbok/
|
|
├── client/ # React-PWA (Frontend)
|
|
│ ├── src/
|
|
│ │ ├── components/ # UI (u. a. CourseDialInput, UserProfilePage, FeedbackModal)
|
|
│ │ ├── services/ # Auth, Sync, Krypto, Backup, Push, Analytics, …
|
|
│ │ ├── sw.ts # Service Worker (Precache + Web Push)
|
|
│ │ └── i18n/ # DE/EN-Übersetzungen
|
|
│ └── Dockerfile # Nginx-Produktions-Image
|
|
├── server/ # Express-API + Prisma
|
|
│ ├── src/routes/ # auth, logbooks, sync, collaboration, sign, push, feedback, weather
|
|
│ ├── src/services/ # z. B. pushNotify, ntfyNotify
|
|
│ └── prisma/ # Datenbankschema
|
|
├── docs/ # Projektdokumentation
|
|
├── scripts/ # Dev- und Deploy-Skripte
|
|
├── docker-compose.yml # Produktions-Stack (DB + Backend + Frontend)
|
|
└── VERSION # App-Version (Build & Footer)
|
|
```
|
|
|
|
## Voraussetzungen
|
|
|
|
- **Node.js** 20+
|
|
- **npm**
|
|
- **Docker** (für PostgreSQL in der Entwicklung oder den vollständigen Stack)
|
|
- Optional: eigener OpenWeatherMap-API-Key im **Benutzerprofil** (sonst serverseitiger Key aus `.env`)
|
|
- Optional: VAPID-Schlüssel für Web Push (siehe Abschnitt Push-Benachrichtigungen)
|
|
- Optional: Ntfy-Topic für Feedback (siehe Abschnitt Feedback)
|
|
|
|
## Lokale Entwicklung
|
|
|
|
### 1. Abhängigkeiten installieren
|
|
|
|
```bash
|
|
cd server && npm ci && cd ..
|
|
cd client && npm ci && cd ..
|
|
```
|
|
|
|
### 2. Umgebungsvariablen
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Kopiere `.env.example` nach `.env` und passe mindestens an:
|
|
|
|
| Variable | Dev (Vite) | Produktion |
|
|
|----------|------------|------------|
|
|
| `RP_ID` | `localhost` | `kapteins-daagbok.eu` |
|
|
| `ORIGIN` | `http://localhost:5173` | `https://kapteins-daagbok.eu` |
|
|
| `SESSION_SECRET` | empfohlen (≥ 32 Zeichen) | **Pflicht** |
|
|
|
|
`ORIGIN` muss **exakt** der Frontend-URL entsprechen (CORS + Session-Cookie). Das Backend lädt `.env` aus dem Projektroot und optional `server/.env`.
|
|
|
|
```
|
|
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/daagbox?schema=public"
|
|
OpenWeatherMapAPIKey= # Fallback für Wetter-Abruf, wenn Nutzer keinen eigenen Key hat
|
|
RP_ID=localhost
|
|
ORIGIN=http://localhost:5173
|
|
SESSION_SECRET= # openssl rand -base64 48 (in Prod Pflicht)
|
|
# Optional — Web Push (npx web-push generate-vapid-keys)
|
|
VAPID_PUBLIC_KEY=
|
|
VAPID_PRIVATE_KEY=
|
|
VAPID_SUBJECT=mailto:support@kapteins-daagbok.eu
|
|
# Optional — Feedback via Ntfy
|
|
NTFY_SERVER=https://ntfy.sh
|
|
NTFY_TOPIC=
|
|
NTFY_TOKEN=
|
|
```
|
|
|
|
`./scripts/start-dev.sh` prüft `ORIGIN` und `SESSION_SECRET` beim Start und gibt Hinweise aus.
|
|
|
|
### 3. Datenbank & Schema
|
|
|
|
Das Dev-Skript startet PostgreSQL in Docker (`postgres-daagbox`). Schema anwenden:
|
|
|
|
```bash
|
|
cd server && npx prisma db push && cd ..
|
|
```
|
|
|
|
### 4. Dev-Server starten
|
|
|
|
```bash
|
|
./scripts/start-dev.sh
|
|
```
|
|
|
|
| Dienst | URL |
|
|
|--------|-----|
|
|
| Frontend (Vite) | http://localhost:5173 |
|
|
| Backend API | http://localhost:5000 |
|
|
| Health Check | http://localhost:5000/api/health |
|
|
| Public Demo | http://localhost:5173/demo |
|
|
|
|
### 5. Tests (Frontend)
|
|
|
|
```bash
|
|
cd client && npm test
|
|
```
|
|
|
|
Vitest-Unit-Tests für Utils, i18n und Services (z. B. Kurswinkel, Benutzereinstellungen).
|
|
|
|
## Docker (produktionsnah)
|
|
|
|
Gesamten Stack lokal bauen und starten:
|
|
|
|
```bash
|
|
./scripts/start-dev-docker.sh
|
|
```
|
|
|
|
Frontend: http://localhost · API: http://localhost/api/health · Demo: http://localhost/demo
|
|
|
|
Umgebungsvariablen in `.env` setzen — mindestens `RP_ID`, `ORIGIN` (z. B. `http://localhost`) und `SESSION_SECRET`. Für Push die VAPID-Variablen an den Backend-Container durchreichen (`docker-compose.yml` → `backend.environment`). Für Feedback `NTFY_*` setzen.
|
|
|
|
## Deployment
|
|
|
|
Produktions-Update auf den Server (konfigurierbar via Umgebungsvariablen):
|
|
|
|
```bash
|
|
./scripts/update-prod.sh
|
|
```
|
|
|
|
Standard-Ziel: `root@10.0.0.25:/opt/kapteins-daagbok` — per `REMOTE_HOST`, `REMOTE_USER`, `REMOTE_DIR` überschreibbar.
|
|
|
|
Auf dem Server müssen `server/.env` (oder gleichwertige Umgebung) u. a. `DATABASE_URL`, `RP_ID`, `ORIGIN`, `SESSION_SECRET` (≥ 32 Zeichen) und bei Push `VAPID_*` enthalten. Optional `NTFY_*` für Feedback. Nach Schema-Änderungen: `npx prisma db push` im Backend-Container.
|
|
|
|
## Dokumentation
|
|
|
|
| Dokument | Inhalt |
|
|
|----------|--------|
|
|
| [docs/plausible-events.md](docs/plausible-events.md) | Custom Events für Plausible Analytics |
|
|
| [docs/push-notifications-plan.md](docs/push-notifications-plan.md) | Web Push: Architektur, API, Testplan |
|
|
| [docs/plan-compass-course-dial.md](docs/plan-compass-course-dial.md) | Kompass-Dial: UX- und Implementierungsplan |
|
|
| [docs/marketing/kapteins-daagbok-beta-flyer.pdf](docs/marketing/kapteins-daagbok-beta-flyer.pdf) | Beta-Flyer (DIN A4) zum Ausdrucken — Quelle: `docs/marketing/beta-flyer.html`, neu erzeugen: `cd client && npm run generate:flyer` |
|
|
| [.planning/PROJECT.md](.planning/PROJECT.md) | Produktvision und Anforderungen (GSD) |
|
|
|
|
## Analytics
|
|
|
|
Die App nutzt [Plausible Analytics](https://plausible.io/) (self-hosted) für anonyme Nutzungsmetriken — ohne Cookies und ohne personenbezogene Daten in Event-Properties. Details und Goal-Namen: [docs/plausible-events.md](docs/plausible-events.md).
|
|
|
|
## Version
|
|
|
|
Aktuelle Version: siehe [VERSION](VERSION) (wird im App-Footer und beim Docker-Build eingebunden).
|
|
|
|
---
|
|
|
|
© 2026 KnorrLabs/Markus F.J. Busche · [kapteins-daagbok.eu](https://kapteins-daagbok.eu)
|