docs & feat: update project plan to E2E encrypted server storage & initialize monorepo client/server codebases

This commit is contained in:
2026-05-27 21:22:02 +02:00
parent d4b3cc2d74
commit db8b454a9e
43 changed files with 10646 additions and 206 deletions
Regular → Executable
View File
Regular → Executable
+12
View File
@@ -1 +1,13 @@
# Dependency directories
node_modules/
# Production build outputs
dist/
client/dist/
server/dist/
# Local env files
.env
.env.local
.env.*.local
*.log
Regular → Executable
View File
Regular → Executable
+13 -8
View File
@@ -6,7 +6,7 @@ Kapteins Daagbox is a modern, mobile-first Progressive Web Application (PWA) des
## Core Value
Providing a private-by-design, fully offline-capable mobile maritime logbook that respects absolute user privacy by storing data 100% locally on the device while assisting the skipper with GPS position capture and automated weather integration.
Providing a private-by-design, fully offline-capable mobile maritime logbook that respects absolute user privacy by storing data in an end-to-end (E2E) encrypted server-side database (with local-first browser cache), while assisting the skipper with GPS position capture and automated weather integration.
## Requirements
@@ -22,13 +22,16 @@ Providing a private-by-design, fully offline-capable mobile maritime logbook tha
- [ ] **LOG-01**: Logbook entry form capturing nautical/journey events
- [ ] **LOG-02**: Automated weather and sea state pre-filling using OpenWeatherMap API
- [ ] **LOG-03**: GPS device integration to capture current coordinates
- [ ] **DATA-01**: Local-only storage (no cloud storage, no registration/central servers)
- [ ] **DATA-02**: Complete offline capability to ensure usability at sea
- [ ] **AUTH-01**: Passwordless user registration and authentication via Passkeys (WebAuthn)
- [ ] **CRYPTO-01**: Client-side End-to-End Encryption (E2E) of all sensitive user data using Web Crypto API
- [ ] **LOGBOOK-01**: Multi-logbook support allowing a user to manage any number of logbooks
- [ ] **DATA-01**: Local-first database sync to zero-knowledge server storage
- [ ] **DATA-02**: Complete offline capability to ensure usability at sea with background sync
- [ ] **DATA-03**: CSV export of logged data for easy sharing (download, email, etc.)
### Out of Scope
- **Centralized cloud storage / Server sync** — Excluded by design to ensure data privacy.
- **Cleartext server-side storage** — Excluded by design to ensure absolute data privacy.
- **Social sharing or community features** — Focus is purely on private logbook management.
## Context
@@ -39,16 +42,18 @@ Providing a private-by-design, fully offline-capable mobile maritime logbook tha
## Constraints
- **Storage**: Must be stored exclusively client-side (IndexedDB / LocalStorage / Origin Private File System).
- **Privacy**: No external telemetry or cloud database connections.
- **Offline**: The app must load and operate fully without internet access (using Service Workers).
- **Storage**: Client-side IndexedDB for offline-first caching, synced with an end-to-end (E2E) encrypted server database.
- **Privacy**: Zero-knowledge architecture. No unencrypted user data on the server; no external telemetry.
- **Offline**: The app must load and operate fully without internet access (using Service Workers), caching operations to sync later.
- **Languages**: German and English.
## Key Decisions
| Decision | Rationale | Outcome |
|----------|-----------|---------|
| Local-Only Storage | Absolute privacy requirement | — Pending |
| E2E Server Sync | Allows multi-device access and backup while retaining zero-knowledge privacy | — Pending |
| Passkey Auth | Passwordless, highly secure login using standard WebAuthn | — Pending |
| Multi-Logbook Setup | Supports skippers managing multiple boats/journeys | — Pending |
| PWA Architecture | Ensures cross-platform installation on iOS/Android without App Store overhead | — Pending |
## Evolution
Regular → Executable
+28 -13
View File
@@ -1,7 +1,7 @@
# Requirements: Kapteins Daagbox
**Defined:** 2026-05-26
**Core Value:** A private, offline-first mobile ship's logbook that stores all data locally, offering GPS/weather assistance and easy CSV sharing.
**Core Value:** A private, offline-first mobile ship's logbook that stores E2E encrypted data on a server with local caching, supporting passwordless Passkeys and multiple logbooks.
## v1 Requirements
@@ -33,9 +33,19 @@ These requirements represent the core scope for the initial release.
- [ ] **INT-02**: Settings panel to store and save custom OpenWeatherMap API keys locally in the browser (LocalStorage).
- [ ] **INT-03**: Weather pre-fill function invoking OpenWeatherMap API using coordinates to auto-populate wind, pressure, and weather state (when online).
### Authentication & Multi-Logbook (AUTH)
- [ ] **AUTH-01**: Passwordless user registration and login via Passkeys (WebAuthn).
- [ ] **AUTH-02**: Creating, renaming, and deleting multiple logbooks per user.
- [ ] **AUTH-03**: Switching active logbooks in the UI, re-loading stored cache.
### Cryptography (CRYPTO)
- [ ] **CRYPTO-01**: Client-side encryption of vessel profiles, crew files, deviation tables, and log entries using AES-GCM-256 before syncing.
- [ ] **CRYPTO-02**: Derivation of the primary E2E symmetric key using WebAuthn PRF (Pseudo-Random Function) extension, with a 12-word recovery phrase fallback.
- [ ] **CRYPTO-03**: Secure local persistence of derived E2E key in the browser (e.g. encrypted in memory or standard session context, not plain LocalStorage).
### System & Offline (SYS)
- [ ] **SYS-01**: Service worker installation caching all assets (HTML, CSS, JS, Fonts) to allow complete offline launch.
- [ ] **SYS-02**: 100% Client-side data storage in IndexedDB (Dexie) with no cloud backup or central server sync.
- [ ] **SYS-02**: Local-first caching in IndexedDB (Dexie) syncing securely with E2E encrypted server storage.
- [ ] **SYS-03**: Client-side CSV generation converting log entries into download-ready CSV files.
- [ ] **SYS-04**: File sharing triggering browser Web Share API or custom email protocols with the CSV data.
@@ -54,9 +64,8 @@ These requirements are deferred to future milestones.
| Feature | Reason |
|---------|--------|
| Cloud synchronization / DB backup | Violates local-only privacy constraint. |
| Skipper/Crew social sharing profiles | Excluded to keep app purely local and private. |
| Global registration/auth system | Focus is on zero-registration instant access. |
| Plaintext server storage / backend processing | Excluded by design to enforce absolute privacy. |
| Skipper/Crew social sharing profiles | Excluded to keep app purely private. |
## Traceability
@@ -65,11 +74,11 @@ These requirements are deferred to future milestones.
| UI-01 | Phase 4 | Pending |
| UI-02 | Phase 1 | Pending |
| UI-03 | Phase 1 | Pending |
| VESSEL-01 | Phase 2 | Pending |
| VESSEL-02 | Phase 2 | Pending |
| VESSEL-03 | Phase 2 | Pending |
| DEV-01 | Phase 2 | Pending |
| DEV-02 | Phase 2 | Pending |
| VESSEL-01 | Phase 3 | Pending |
| VESSEL-02 | Phase 3 | Pending |
| VESSEL-03 | Phase 3 | Pending |
| DEV-01 | Phase 3 | Pending |
| DEV-02 | Phase 3 | Pending |
| LOG-01 | Phase 3 | Pending |
| LOG-02 | Phase 3 | Pending |
| LOG-03 | Phase 3 | Pending |
@@ -78,14 +87,20 @@ These requirements are deferred to future milestones.
| INT-01 | Phase 3 | Pending |
| INT-02 | Phase 3 | Pending |
| INT-03 | Phase 3 | Pending |
| AUTH-01 | Phase 1 | Pending |
| AUTH-02 | Phase 2 | Pending |
| AUTH-03 | Phase 2 | Pending |
| CRYPTO-01 | Phase 1 | Pending |
| CRYPTO-02 | Phase 1 | Pending |
| CRYPTO-03 | Phase 1 | Pending |
| SYS-01 | Phase 1 | Pending |
| SYS-02 | Phase 1 | Pending |
| SYS-02 | Phase 2 | Pending |
| SYS-03 | Phase 4 | Pending |
| SYS-04 | Phase 4 | Pending |
**Coverage:**
- v1 requirements: 20 total
- Mapped to phases: 20
- v1 requirements: 26 total
- Mapped to phases: 26
- Unmapped: 0 ✓
---
Regular → Executable
+41 -40
View File
@@ -10,69 +10,70 @@ Kapteins Daagbox will be built in four logical phases following a clean data-to-
- Integer phases (1, 2, 3, 4): Planned milestone work
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
- [ ] **Phase 1: Foundation & Data Infrastructure** - Initialize PWA, database, and translations
- [ ] **Phase 2: Master Data Management (Stammdaten)** - Build boat profile, crew manager, and deviation table
- [ ] **Phase 3: Logbook Entries & Integration** - Form sheets, GPS coordinates, and Weather API integrations
- [ ] **Phase 4: CSV Export & UI Polish** - CSV generation, share triggers, and adaptive CSS layout
- [ ] **Phase 1: Foundation, Auth & E2E Crypto** - Setup client PWA, backend server, WebAuthn Passkeys, and E2E Web Crypto modules.
- [ ] **Phase 2: Sync Protocol & Multi-Logbooks** - Build sync backend/client cache and multi-logbook management views.
- [ ] **Phase 3: Master Data & Log entries** - Implement vessel forms, crew files, compass deviation grids, and log entries.
- [ ] **Phase 4: CSV Export & UI Polish** - CSV builder, Web Share API, sync/connection indicators, and OS themes.
## Phase Details
### Phase 1: Foundation & Data Infrastructure
**Goal**: Initialize the PWA development bundle, service worker caching, IndexedDB database client, and translation hooks.
### Phase 1: Foundation, Auth & E2E Crypto
**Goal**: Setup client/backend codebases, WebAuthn Passkey registration/login APIs, and client-side encryption key derivation (PRF & recovery word helper).
**Depends on**: Nothing (first phase)
**Requirements**: UI-02, UI-03, SYS-01, SYS-02
**Requirements**: UI-02, UI-03, SYS-01, AUTH-01, CRYPTO-01, CRYPTO-02, CRYPTO-03
**Success Criteria**:
1. App loads instantly with offline assets from a service worker.
2. German and English languages are switchable via menu, and the initial language is auto-detected from browser locales.
3. Dexie.js database client is initialized with working Yacht, Crew, Deviation, and LogEntry tables.
**Plans**: 2 plans
1. App shell runs offline via Service Worker, with German/English translations active.
2. Backend server (TypeScript/Node/Postgres) provides functional endpoints for WebAuthn registration and assertion.
3. User can register/login passwordlessly, generating and storing a secure, client-side derived E2E key (via PRF/recovery fallback).
**Plans**: 3 plans
Plans:
- [ ] 01-01: Initialize Vite React TS, configure vite-plugin-pwa, set up the layout shell, and add react-i18next translations.
- [ ] 02-01: Initialize Dexie.js schemas and implement a Database Settings view to check storage capability.
- [x] 01-01: Initialize Vite React TS client, Node.js Express TS server, Prisma PostgreSQL schema, and react-i18next locales.
- [ ] 01-02: Implement WebAuthn backend/frontend flows using SimpleWebAuthn library to register and log in users.
- [ ] 01-03: Setup client-side Web Crypto helper deriving E2E keys from biometric PRF credentials or a 12-word recovery phrase.
### Phase 2: Master Data Management (Stammdaten)
**Goal**: Implement forms for vessel profile metadata, skipper and crew personal files, and the compass deviation grid.
### Phase 2: Sync Protocol & Multi-Logbooks
**Goal**: Setup the offline-first IndexedDB database caching, multiple logbooks CRUD API, and background synchronization/conflict protocol.
**Depends on**: Phase 1
**Requirements**: VESSEL-01, VESSEL-02, VESSEL-03, DEV-01, DEV-02
**Requirements**: AUTH-02, AUTH-03, SYS-02
**Success Criteria**:
1. User can successfully save and update the Yacht profile.
2. User can add, edit, and remove up to 6 crew records.
3. User can input compass headings (MgK) and save corresponding magnetic deviation values.
1. User can create, rename, delete, and switch between multiple logbooks in the UI.
2. Database cache stores encrypted objects locally in Dexie.js tables.
3. Data modifications (CRUD) sync automatically to backend DB payloads when online, handling conflicts gracefully.
**Plans**: 2 plans
Plans:
- [ ] 02-01: Build Yacht and Crew editing components and wire them to Dexie.js collections.
- [ ] 02-02: Build the Deviation grid (Steuertafel) covering 000° to 360° headings.
- [ ] 02-01: Build Dexie.js caching models, user logbook dashboard, and switching UI.
- [ ] 02-02: Implement sync manager that schedules push/pull of encrypted delta packets, conflict markers, and background retries.
### Phase 3: Logbook Entries & Integration
**Goal**: Develop the journey listing, entry form sheets, GPS coordinate prefill, and OpenWeatherMap assistance lookup.
### Phase 3: Master Data & Log entries
**Goal**: Implement the user interface forms for Yacht profiles, Crew management, Deviation grids, and Logbook entries, saving encrypted representations locally.
**Depends on**: Phase 2
**Requirements**: LOG-01, LOG-02, LOG-03, LOG-04, LOG-05, INT-01, INT-02, INT-03
**Requirements**: VESSEL-01, VESSEL-02, VESSEL-03, DEV-01, DEV-02, LOG-01, LOG-02, LOG-03, LOG-04, LOG-05, INT-01, INT-02, INT-03
**Success Criteria**:
1. User can create daily logbook entries, record hourly events, and track water/diesel consumption.
2. User can pre-fill current coordinates with one tap (via browser Geolocation).
3. User can fetch and prefill weather description, pressure, and wind speed/direction using OpenWeatherMap API (with offline fallbacks).
4. User can sign off the daily entry.
**Plans**: 2 plans
1. User can edit Yacht, Crew profiles, and Deviation grids, saving them as E2E-encrypted records.
2. User can write daily log entries, query Geolocation coordinates, and pull OpenWeatherMap data.
3. Entry data is E2E-encrypted and written to local sync queue.
**Plans**: 3 plans
Plans:
- [ ] 03-01: Build logbook list view and daily log entry header, sails, and consumption forms.
- [ ] 03-02: Implement event log forms, Geolocation API integration, and OpenWeatherMap service helper.
- [ ] 03-01: Build Yacht/Crew profiles forms and the deviation table UI, integrating client-side E2E encrypt/decrypt.
- [ ] 03-02: Implement Logbook entry list, daily header details, and consumption tracking forms.
- [ ] 03-03: Implement logbook event records, browser Geolocation tracker, and OpenWeatherMap integration.
### Phase 4: CSV Export & UI Polish
**Goal**: Implement client-side CSV file download, share dialog hooks, local storage persistent request, and platform CSS themes.
**Goal**: Build local CSV reporting builders, browser Web Share triggers, connection/sync indicators, and adaptive OS styling.
**Depends on**: Phase 3
**Requirements**: SYS-03, SYS-04, UI-01
**Success Criteria**:
1. User can download a standard CSV file matching their logbook entries.
2. User can launch the native mail or share panel with the generated CSV data.
3. The interface renders elements in platform-adaptive styles matching iOS Cupertino or Android Material.
1. App exports standard unencrypted CSV files compiled on-the-fly client-side (after decrypting entries).
2. Native email/message sharing dialogs trigger successfully.
3. UI features sync status (e.g. "Synced / Offline / Unsynced changes") and adapts visual look to Material or Cupertino styles.
**Plans**: 2 plans
Plans:
- [ ] 04-01: Implement CSV export service and link it to the Web Share API.
- [ ] 04-02: Implement storage persistence prompt, PWA install prompt warnings, and apply OS-adaptive UI themes.
- [ ] 04-01: Create client-side decryption CSV builder and hook it up to standard browser download and Web Share API.
- [ ] 04-02: Implement online/offline connection state detectors, sync progress bars, and OS-adaptive UI themes.
## Progress
@@ -81,7 +82,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Foundation & Data Infrastructure | 0/2 | Not started | - |
| 2. Master Data Management (Stammdaten) | 0/2 | Not started | - |
| 3. Logbook Entries & Integration | 0/2 | Not started | - |
| 1. Foundation, Auth & E2E Crypto | 1/3 | In progress | - |
| 2. Sync Protocol & Multi-Logbooks | 0/2 | Not started | - |
| 3. Master Data & Log entries | 0/3 | Not started | - |
| 4. CSV Export & UI Polish | 0/2 | Not started | - |
Regular → Executable
+16 -14
View File
@@ -4,36 +4,36 @@
See: .planning/PROJECT.md (updated 2026-05-26)
**Core value:** A private, offline-first mobile ship's logbook that stores all data locally, offering GPS/weather assistance and easy CSV sharing.
**Current focus:** Phase 1: Foundation & Data Infrastructure
**Core value:** A private, offline-first mobile ship's logbook that stores E2E encrypted data on a server with local caching, supporting passwordless Passkeys and multiple logbooks.
**Current focus:** Phase 1: Foundation, Auth & E2E Crypto
## Current Position
Phase: 1 of 4 (Foundation & Data Infrastructure)
Plan: 1 of 2 in current phase
Phase: 1 of 4 (Foundation, Auth & E2E Crypto)
Plan: 2 of 3 in current phase
Status: Ready to plan
Last activity: 2026-05-26 — Project initialized
Last activity: 2026-05-27 — Plan 01-01 executed (Monorepo codebase, Vite React client, Express server, Prisma schema setup)
Progress: [░░░░░░░░░] 0%
Progress: [░░░░░░░░░] 10%
## Performance Metrics
**Velocity:**
- Total plans completed: 0
- Average duration: 0 min
- Total execution time: 0.0 hours
- Total plans completed: 1
- Average duration: 15 min
- Total execution time: 0.25 hours
**By Phase:**
| Phase | Plans | Total | Avg/Plan |
|-------|-------|-------|----------|
| 1. Foundation & Data Infrastructure | 0/2 | - | - |
| 2. Master Data Management | 0/2 | - | - |
| 3. Logbook Entries & Integration | 0/2 | - | - |
| 1. Foundation, Auth & E2E Crypto | 1/3 | - | - |
| 2. Sync Protocol & Multi-Logbooks | 0/2 | - | - |
| 3. Master Data & Log entries | 0/3 | - | - |
| 4. CSV Export & UI Polish | 0/2 | - | - |
**Recent Trend:**
- Last 5 plans: []
- Last 5 plans: [01-01]
- Trend: Stable
*Updated after each plan completion*
@@ -45,7 +45,9 @@ Progress: [░░░░░░░░░░] 0%
Decisions are logged in PROJECT.md Key Decisions table.
Recent decisions affecting current work:
- [Init]: Local-Only Storage — IndexedDB selected to satisfy absolute privacy constraints.
- [Init]: E2E Server Sync — Allows multi-device access and backup while retaining zero-knowledge privacy.
- [Init]: Passkey Auth — Passwordless, highly secure login using standard WebAuthn.
- [Init]: Multi-Logbook Setup — Supports skippers managing multiple boats/journeys.
- [Init]: PWA Architecture — React PWA selected for fast, lightweight loading and cross-platform installation.
### Pending Todos
Regular → Executable
View File
+70 -36
View File
@@ -6,14 +6,14 @@
## Component Architecture
Since this is a client-side only PWA, the entire architecture runs in the user's browser sandbox. The structure is separated into UI Components, Services (business logic), and Storage.
The application uses an offline-first PWA architecture synced with a remote database. End-to-end (E2E) encryption is managed entirely on the client, ensuring the server only receives encrypted payloads.
```
┌────────────────────────────────────────────────────────┐
│ User Interface │
│ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ │
│ │ Stammdaten │ │ Logbook Entry │ │ Settings/ │ │
│ │ (Forms, Crew)│ │ (Forms, List) │ │ Language │ │
│ │ Stammdaten │ │ Logbook Entry │ │ Auth & Logs │ │
│ │ (Forms, Crew)│ │ (Forms, List) │ │ (Passkey) │ │
│ └──────┬───────┘ └───────┬────────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼──────────┘
│ │ │
@@ -22,67 +22,101 @@ Since this is a client-side only PWA, the entire architecture runs in the user's
│ ┌────────────────┐ ┌────────────────┐ ┌────────────┐ │
│ │ Geolocation │ │ Weather Service│ │ Export │ │
│ │ Service (GPS) │ │ (OpenWeather) │ │ Service │ │
│ └───────────────┘ └───────────────┘ └───────────┘ │
└─────────┼────────────────────────────────────────────
│ └───────────────┘ └───────────────┘ └───────────┘ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────┐ │
│ │ WebAuthn Client│ │ E2E Cryptography│ │ Sync │ │
│ │ (Passkey Auth) │ │ (Web Crypto) │ │ Service │ │
│ └────────┬───────┘ └───────┬────────┘ └────┬───────┘ │
└───────────┼─────────────────┼───────────────┼──────────┘
│ │ │
┌────────────────────────────▼───────────────▼──────────┐
Data Infrastructure
┌────────────────────────────▼───────────────▼──────────┐
Local Data Infrastructure │
│ ┌──────────────────────────────────────────────────┐ │
│ │ IndexedDB / Dexie.js (Data Persistence) │ │
│ │ IndexedDB / Dexie.js (Data Persistence Cache) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Service Worker (Asset Cache & Offline) │ │
│ └──────────────────────────┬───────────────────────┘ │
└─────────────────────────────┼──────────────────────────┘
│ HTTPS (Encrypted Payloads & WebAuthn JSON)
┌────────────────────────────────────────────────────────┐
│ Backend Infrastructure │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Node.js Express API (WebAuthn Validation) │ │
│ └──────────────────────────┬───────────────────────┘ │
│ ┌──────────────────────────▼───────────────────────┐ │
│ │ PostgreSQL Database (Encrypted Payloads) │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
```
### 1. UI Layer
- **Responsive Layout Shell**: Single page app with a bottom navigation bar for mobile feel and sidebar for tablet/desktop. Responsive and adaptive depending on device size.
- **Form Views**:
- **Responsive Layout Shell**: Single page app with bottom navigation bar for mobile feel and sidebar for tablet/desktop. Responsive and adaptive depending on device size.
- **Form & Management Views**:
- **Auth Onboarding Panel**: Handles Passkey registration/login and generates or inputs the 12-word recovery key.
- **Logbook Dashboard**: Overview panel listing available user logbooks, option to create a new logbook, and active logbook selection.
- **Stammdaten Form**: Single tabbed view separating Boat Profile, Crew profiles, and the Deviation Table.
- **Logbook List**: Chronological display of journal entries with details and summaries.
- **Logbook Entry Form**: Interactive form with sub-sections for Nautical logs, Weather inputs, Sails, Course, and Consumption controls.
- **Adaptive UI Handler**: Standard CSS variables and OS-detection class selectors (`.platform-ios`, `.platform-android`) to render inputs and dialogs matching Cupertino or Material styles.
### 2. Services Layer
- **E2E Cryptography Service**: Uses standard Web Crypto API. Handles AES-GCM encryption/decryption of logbook payloads. Manages key derivation via PBKDF2/HKDF using WebAuthn PRF inputs or a 12-word recovery phrase.
- **WebAuthn Authentication Service**: Connects to the browser's credentials API (`navigator.credentials`) and `@simplewebauthn/browser`. Communicates registration and authentication challenges to the server.
- **Synchronization Service**: Tracks offline data mutations in a local transaction log. Resolves push/pull updates with the server API when online using conflict resolution algorithms (client-wins/last-write-wins).
- **GPS Service**: Interface to browser Geolocation API (`navigator.geolocation`). Provides DMS (Degrees, Minutes, Seconds) coordinate formatting.
- **Weather Service**: Performs asynchronous REST requests to OpenWeatherMap API using coordinates. Handles offline fallback gracefully.
- **Export Service**: Generates a CSV file using standard RFC 4180 parameters, creates a dynamic Blob URL, and triggers browser download or invokes `navigator.share` (Web Share API) for native email/message share.
- **Export Service**: Client-side decrypts entries, generates a CSV file using standard RFC 4180 parameters, creates a dynamic Blob URL, and triggers browser download or invokes `navigator.share` (Web Share API) for native email/message share.
- **Translation Service**: standard `i18next` engine. Automatically detects system locale on first start (`navigator.language`), falls back to English, and persists user selection in LocalStorage.
### 3. Data Infrastructure
- **IndexedDB Storage Scheme (Dexie.js)**:
- Table `yacht`: Single record containing vessel specs.
- Table `crew`: Skipper + Crew members.
- Table `deviation`: 37 records mapping `heading` (MgK 0-360) to `deviation` (Abl).
- Table `entries`: Logbook records containing dates, coordinates, courses, weather, sails, and daily consumption.
### 3. Local Data Infrastructure
- **IndexedDB Caching (Dexie.js)**:
- Table `yacht`: Yacht records, schema: `id, logbookId, encryptedData, updatedAt`.
- Table `crew`: Crew profiles, schema: `id, logbookId, encryptedData, updatedAt`.
- Table `deviation`: Compass deviations, schema: `id, logbookId, encryptedData, updatedAt`.
- Table `entries`: Logbook records, schema: `id, logbookId, encryptedData, updatedAt`.
- Table `logbooks`: Logbook metadata, schema: `id, encryptedTitle, updatedAt, isSynced`.
- Table `syncQueue`: Sync status tracking local pending mutations.
- **Offline Shell**: Service Worker configured using Workbox via `vite-plugin-pwa`. Caches CSS, JS, HTML, fonts, and icons for immediate load.
### 4. Backend Infrastructure
- **Node.js Server (TypeScript & Express)**:
- API Routes: `/api/auth/register-options`, `/api/auth/register-verify`, `/api/auth/login-options`, `/api/auth/login-verify`, `/api/logbooks`, `/api/sync`.
- Uses `@simplewebauthn/server` for validating Passkey cryptography.
- **PostgreSQL Database**:
- `User`: id, username, createdAt.
- `Credential`: id, userId, credentialId, publicKey, counter, transports.
- `Logbook`: id, userId, encryptedTitle, createdAt, updatedAt.
- `Payloads` (Yacht, Crew, Deviation, Entries): E2E encrypted string blobs stored by logbookId.
## Data Flow
### 1. Fetching GPS & Weather
1. User clicks "Auto-Fill GPS/Weather" on a new log entry.
2. Geolocation Service queries `navigator.geolocation.getCurrentPosition()`.
3. Coordinates are returned and set in form state.
4. If internet is available, Weather Service requests OpenWeatherMap using the coordinates.
5. API response (pressure, wind strength, direction, weather icons) is parsed and merged into the form state.
6. User reviews the pre-filled fields and saves the entry.
### 1. WebAuthn Registration and Key Derivation
1. User registers passwordlessly using their device biometric authenticator (Passkey).
2. During registration, the WebAuthn PRF extension is invoked to derive a unique symmetric key input from the hardware credential.
3. If PRF is unsupported, or as a recovery fallback, the client generates a 12-word BIP39 recovery phrase and derives a master key via PBKDF2.
4. The client generates a random 256-bit User Master Key.
5. The User Master Key is E2E-encrypted with the PRF-derived key and (separately) with the recovery-derived key.
6. The encrypted User Master Key payloads are sent to the server and stored.
7. During subsequent logins, the authenticator is used to verify identity and regenerate the PRF key. The client requests the encrypted User Master Key from the server, decrypts it locally, and stores it in memory.
### 2. Saving to Database & Local Cache
1. Form state triggers `dexie` write operation: `db.entries.add(formData)`.
2. IndexedDB saves the record locally.
3. The UI queries IndexedDB reactively using `useLiveQuery` from `dexie-react-hooks`, updating lists instantly.
4. No network requests are made, ensuring zero lag.
### 2. Encryption and Synchronization
1. User makes edits to a logbook entry locally.
2. The client fetches the User Master Key from memory, encrypts the data payload using AES-GCM (generating a IV and authentication tag), and writes it to the local IndexedDB.
3. A local mutation event is created in the `syncQueue` table.
4. The Sync Service attempts to POST the encrypted payload (`{ logbookId, payloadId, encryptedData, iv, tag, updatedAt }`) to the backend `/api/sync` endpoint.
5. The backend validates the user's session and commits the encrypted payload directly to PostgreSQL.
6. If the user is offline, the syncQueue retains the event and retries once network connectivity is restored (`navigator.onLine`).
## Suggested Build Order
1. **Setup & PWA Shell**: Initialize Vite React TS, configure `vite-plugin-pwa` with service worker, and setup simple landing shell.
2. **Database Layer**: Implement Dexie.js database schemas, collections, and mock data.
3. **Master Data View (Stammdaten)**: Implement boat profile form, crew cards, and the Deviation grid.
4. **Logbook Entry Forms**: Build the main log entry form, course inputs, and consumption controller.
5. **GPS & Weather Integrations**: Implement device Geolocation fetching and OpenWeatherMap query helpers.
6. **Data Export & Language Switcher**: Add CSV generation/sharing and `react-i18next` localization.
7. **Adaptive UI Polish**: Apply iOS/Android CSS themes and polish responsiveness.
1. **Setup & Foundations**: Initialize client Vite PWA, Express TS backend server, Prisma schema and PostgreSQL database docker/connection.
2. **Auth & Crypto Layer**: Implement Passkey registration and assertion using WebAuthn. Build Web Crypto API utilities for PRF/recovery key E2E master key decryption.
3. **Multi-Logbook & Sync**: Implement multi-logbook dashboards, switching logic, IndexedDB local cache tables, and sync queue protocols.
4. **Forms UI & Client Encryption**: Implement Yacht, Crew profiles, and Deviation grid editors that write and read encrypted data.
5. **Logbook Entries & Integrations**: Build entry forms, Geolocation GPS fetcher, and OpenWeatherMap lookup service.
6. **Decryption Export & Styling**: Implement client-side decryption CSV builder, sync indicator bars, and platform CSS adaptations.
---
*Architecture research for: Kapteins Daagbox PWA*
Regular → Executable
+14 -8
View File
@@ -21,9 +21,12 @@
- **OpenWeatherMap Integration**: Automatically query and pre-fill wind direction/strength, pressure, and weather state based on geographical coordinates.
- **GPS Coordinates Capture**: Fetch current latitude/longitude via device GPS and pre-fill coordinates into log entries.
### 4. Data Management & Privacy
- **Local Database (IndexedDB)**: Direct database queries and local storage in browser sandbox.
- **CSV Data Export**: Generate and download formatted CSV logbooks directly, or trigger local email/message sharing.
### 4. Data Management, Auth & Cryptography
- **Passkey Accounts (WebAuthn)**: Passwordless user registration and login using device authenticators (biometrics, secure keys).
- **Client-Side E2E Cryptography**: Transparent client-side AES-GCM-256 encryption. WebAuthn PRF and BIP39 recovery word helpers for zero-knowledge key derivation.
- **Multi-Logbook Manager**: Dashboard interface allowing skippers to create and switch between multiple ship logbooks under one account.
- **Offline-First Synchronization**: Sync local changes (IndexedDB cache) to remote PostgreSQL via transaction logs and delta packet exchanges, offering conflict resolution markers.
- **CSV Data Export**: Generate and download unencrypted CSV logbooks compiled on-the-fly client-side (after decrypting entries), or trigger local email/message sharing.
- **Offline Assets & Service Worker**: Cache all HTML, JS, CSS, and assets so the application runs completely disconnected.
## Feature Scoping: Table Stakes vs Differentiators
@@ -34,8 +37,11 @@
| Crew Registry | Stammdaten | Table Stake | Low | Up to 6 profiles, standard fields. |
| Logbook Form | Logbuch | Table Stake | Medium | Complex form containing wind, course, and sails. |
| Deviation Table | Stammdaten | Table Stake | Low | Grid mapping MgK to Abl. |
| CSV Export | Data | Table Stake | Medium | Client-side CSV generation and download trigger. |
| Local Storage | Data | Table Stake | Medium | IndexedDB schema setup and migration. |
| CSV Export | Data | Table Stake | Medium | Client-side decryption and CSV download trigger. |
| Passkey Auth | Auth | Table Stake | Medium | WebAuthn biometrics setup (SimpleWebAuthn). |
| E2E Cryptography | Crypto | Table Stake | High | Web Crypto API, PRF derivation & recovery fallback. |
| Sync Manager | Data | Table Stake | High | Local queue processing, background pushes, conflict management. |
| Multi-Logbook UI | UI | Table Stake | Medium | Dashboard to create, delete, and switch logbooks. |
| Offline PWA | System | Table Stake | Medium | Service Worker configuration. |
| GPS Fetching | Assistance | Differentiator | Low | HTML5 Geolocation API integration. |
| OpenWeather API | Assistance | Differentiator | Medium | Needs API key, coordinates, and fallback for offline. |
@@ -43,9 +49,9 @@
## Anti-Features (Do Not Build)
- **Cloud DB Sync**: Violates local-only privacy constraint.
- **Central User Login / Registration**: No remote accounts; the app is immediately active upon loading.
- **Remote Sharing Server**: Logbook files must be exported directly from the device (CSV download or local email handler).
- **Cleartext Server-Side Storage / Sync**: The server must never store unencrypted vessel, crew, deviation, or journal entry data.
- **Classic Username / Password Login**: Passwords introduce security risks and weak encryption bases. Enforce biometric/hardware Passkeys (WebAuthn) instead.
- **Server-Side Data Analytics**: The backend has zero visibility into user logs, avoiding tracking.
## Dependencies & Risk Analysis
Regular → Executable
+24 -1
View File
@@ -47,6 +47,29 @@
- Implement an auto-save mechanism that writes the active form draft to IndexedDB (or LocalStorage) every 10 seconds.
- On launching the logbook entry form, check if a draft exists and offer to restore it.
### 6. Passkey Authenticator Compatibility & WebAuthn Limitations
- **Problem**: Biometric hardware/WebAuthn APIs might be restricted on legacy operating systems, specific Android skins, or in private/incognito browsing windows.
- **Warning Signs**: Browser throws authentication errors or `navigator.credentials` returns undefined.
- **Prevention Strategy**:
- Check `PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()` on onboarding.
- Inform the user of browser/device capability limitations and recommend updating their system.
- Fall back cleanly to hardware USB security keys if biometrics fail.
### 7. Permanent Data Loss due to E2E Encryption Key Loss
- **Problem**: In a zero-knowledge architecture, the server does not store plaintext passwords or master keys. If the user registers a Passkey, loses their device, and loses their 12-word recovery phrase, the server cannot recover their logbooks.
- **Warning Signs**: Skipper logs out or switches devices and cannot decrypt downloaded database chunks.
- **Prevention Strategy**:
- Enforce a mandatory recovery phrase validation step (e.g. asking the user to re-enter words 3, 7, and 11) during sign-up.
- Display clear warnings in the settings dashboard about the zero-knowledge nature of the server.
### 8. Concurrent Sync Conflicts from Offline Edits
- **Problem**: Skipper modifies a logbook entry or crew member on device A (phone) and device B (tablet) while both are offline at sea. Upon re-establishing internet, conflicting updates are pushed to the server.
- **Warning Signs**: Data edits are silently overwritten or entries become duplicated/corrupted.
- **Prevention Strategy**:
- Use atomic delta packages containing object timestamps.
- Apply Last-Write-Wins (LWW) strategy on standard field updates based on local timestamps.
- For journal entry splits, append logs chronologically instead of overwriting, and flag conflict states to the user for manual merge.
---
*Pitfalls research for: Kapteins Daagbox PWA*
*Researched: 2026-05-26*
*Researched: 2026-05-26 (Updated 2026-05-27)*
Regular → Executable
+23 -13
View File
@@ -13,9 +13,12 @@ We recommend a **React + TypeScript + Vite** stack wrapped with **vite-plugin-pw
| Technology | Version | Purpose | Why Recommended |
|------------|---------|---------|-----------------|
| **React** | 18.x / 19.x | UI Library | Component-driven architecture allows building a modular, reactive UI that easily handles state transitions for logbook forms. |
| **TypeScript** | 5.x | Language | Enforces strict type safety across logbook entries and crew models, preventing runtime errors. |
| **Vite** | 5.x | Build Tool | Extremely fast bundler and dev server; offers direct support for PWAs via plugins. |
| **TailwindCSS** / **Vanilla CSS** | 3.x / 4.x | Styling | Allows responsive, adaptive styling to match Android/iOS aesthetics and handles mobile viewport constraints. |
| **TypeScript** | 5.x | Language | Enforces strict type safety across client and server. |
| **Vite** | 5.x | Build Tool | Fast client packager and Service Worker compiler. |
| **Node.js (Express)** | 20.x | Backend Server | Lightweight API backend to handle WebAuthn challenges and database storage. |
| **PostgreSQL** | 16.x | Relational DB | Robust storage of credentials and E2E-encrypted JSON payloads. |
| **Prisma** | 5.x | ORM | Type-safe SQL database client interface. |
| **TailwindCSS** / **Vanilla CSS** | 3.x / 4.x | Styling | Responsive styling matching iOS/Android. |
### Supporting Libraries
@@ -23,7 +26,10 @@ We recommend a **React + TypeScript + Vite** stack wrapped with **vite-plugin-pw
|---------|---------|---------|-------------|
| **Dexie.js** | 4.x | IndexedDB Wrapper | Required for robust, structured offline-first storage of ship data and log entries. |
| **vite-plugin-pwa** | 0.20.x | PWA / Service Worker | Handles automatic service worker registration, offline caching of assets, and install prompts. |
| **react-i18next** | 14.x | Multilingual (l18n) | Seamless translation management for German and English with automatic language detection. |
| **react-i18next** | 14.x | Multilingual (l18n) | Seamless translation management for German and English. |
| **@simplewebauthn/server** | 9.x / 10.x | Passkey Verification | Validates WebAuthn registration/login assertions on the server. |
| **@simplewebauthn/browser** | 9.x / 10.x | Passkey Client helper | Connects WebAuthn client requests to browser credentials API. |
| **bip39** | 3.x | Recovery phrases | Generates 12-word mnemonic phrases for E2E encryption fallbacks. |
| **lucide-react** | 0.300.x | SVG Icons | Light, modern icon library for weather states, navigation, and logbook actions. |
### Development Tools
@@ -36,14 +42,18 @@ We recommend a **React + TypeScript + Vite** stack wrapped with **vite-plugin-pw
## Installation
```bash
# Core & UI
# Client Core & UI
npm install react react-dom lucide-react
# Storage, PWA & Localization
npm install dexie dexie-react-hooks react-i18next i18next i18next-browser-languagedetector
# Client Storage, PWA, Localization & Cryptography
npm install dexie dexie-react-hooks react-i18next i18next i18next-browser-languagedetector @simplewebauthn/browser bip39
# Dev Dependencies
# Client Dev Dependencies
npm install -D typescript @types/react @types/react-dom vite @vitejs/plugin-react vite-plugin-pwa
# Backend Core, ORM & Auth
npm install express dotenv cors @simplewebauthn/server @prisma/client
npm install -D tsx @types/express @types/node @types/cors prisma
```
## Alternatives Considered
@@ -57,18 +67,18 @@ npm install -D typescript @types/react @types/react-dom vite @vitejs/plugin-reac
| Avoid | Why | Use Instead |
|-------|-----|-------------|
| **Firebase / Supabase** | Requires online connection for core operations; violates "local-only" strict privacy constraint. | Dexie.js + Local IndexedDB |
| **Plain Firebase / Supabase DB Sync** | Default syncing exposes raw unencrypted database schemas to the cloud provider, violating zero-knowledge privacy. | Custom Node/Express server + E2E encrypted payloads database |
| **Bootstrap** | Heavy, outdated styling that doesn't adapt well to modern native mobile looks. | TailwindCSS / CSS variables |
## Stack Patterns by Variant
**If strict local privacy is required:**
- Use IndexedDB (via Dexie.js) for storage.
- Because IndexedDB runs entirely in the user's browser sandbox and has no cloud sync, guaranteeing absolute data ownership.
**If strict user privacy with multi-device access is required:**
- Use Client-Side E2E Encryption (AES-GCM-256) combined with Local-first IndexedDB (via Dexie.js) synced to a zero-knowledge PostgreSQL server.
- Because client-side encryption ensures the server operator only holds encrypted payloads, retaining absolute user data privacy.
**If poor connectivity is expected:**
- Configure Workbox Service Worker in "CacheFirst" or "StaleWhileRevalidate" mode.
- Because it ensures the application shell loads immediately even with 0% network connection.
- Because it ensures the application shell loads immediately even with 0% network connection, queueing synchronization packets until the device is online.
## Version Compatibility
Regular → Executable
+50 -52
View File
@@ -7,90 +7,86 @@
## Executive Summary
Kapteins Daagbox is an offline-first PWA that serves as a digital ship's logbook for private yachts. It places a primary emphasis on complete data privacy by storing all yacht, crew, and journal entries entirely local to the user's browser database (IndexedDB).
Kapteins Daagbox is an offline-first PWA synced with a zero-knowledge backend server, serving as a secure digital ship's logbook for private yachts. It guarantees data privacy via client-side End-to-End Encryption (E2E), ensuring the server only stores encrypted payloads.
The recommended approach uses **Vite + React + TypeScript + Dexie.js + TailwindCSS**, which guarantees a lightweight bundle that loads quickly on low-bandwidth mobile connections at sea. The app incorporates browser Geolocation APIs for automatic GPS coordinate capture and integrates with OpenWeatherMap for sea/weather pre-filling.
The recommended stack features a **React + TS + Vite** frontend, a **Node.js Express + Prisma + PostgreSQL** backend, **Dexie.js** for local caching, and **WebAuthn** for passwordless Passkey login. E2E key derivation uses biometric/hardware PRF input or a 12-word recovery phrase.
Key risks include data loss due to OS storage reclamation policies (particularly on iOS Safari) and API timeouts when connections fail. These are mitigated by instructing the user to install the app as a PWA on their Home Screen, enabling persistent storage APIs, and using local draft autosaving along with regular CSV backups.
Key risks include loss of client-side encryption keys (causing unrecoverable server data), browser compatibility with WebAuthn, and concurrent offline synchronization conflicts. These are mitigated by a mandatory recovery phrase verification on registration, using standard security key fallbacks, and applying Last-Write-Wins timestamps to sync packets.
## Key Findings
### Recommended Stack
We recommend React with Vite and TypeScript for fast rendering and development. [STACK.md](STACK.md) details the tools and configurations.
We recommend a React with Vite client paired with a Node.js + Express backend using Prisma and PostgreSQL. [STACK.md](STACK.md) details the configurations.
**Core technologies:**
- **React + TS + Vite**: Builds a fast, responsive mobile app compiled to lightweight static assets.
- **Dexie.js (IndexedDB)**: An asynchronous, robust client-side database with reactive binding for UI state management.
- **vite-plugin-pwa (Workbox)**: Configures offline asset caching, service worker hooks, and updates.
- **react-i18next**: Handles German/English localizations and browser language auto-detection.
- **React + TS + Vite / Node.js Express**: Full-stack TypeScript environment.
- **PostgreSQL & Prisma**: Backend storage for user credentials and encrypted payloads.
- **Dexie.js (IndexedDB)**: Client-side local transaction cache.
- **WebAuthn (@simplewebauthn)**: Passkey integration for passwordless logins.
- **Web Crypto API**: High-performance, browser-native client encryption.
### Expected Features
Detailed scoping is tracked in [FEATURES.md](FEATURES.md).
**Must have (table stakes):**
- **Vessel Profile & Crew Registry**: Stammdaten forms capturing yacht specifications and up to 6 crew records.
- **Steuertafel (Deviation table)**: Grid aligning compass headings with magnetic deviation.
- **Logbook Forms**: Rich fields capturing journey details, course, wind, sails, sea state, and daily fuel/water checks.
- **Local Database & Offline Capability**: Persistent client-side storage and offline asset availability.
- **CSV Data Export**: Generating and downloading logs in standard CSV format.
- **Passkey Auth & E2E Encryption**: Biometric user onboarding and client-side AES-GCM data encryption.
- **Multi-Logbook Manager**: Dashboard UI to manage and switch between multiple ship logbooks.
- **Stammdaten Forms**: Yacht profile, skipper/crew profiles, and Deviation grids.
- **Sync Protocol**: Local-first caching synced securely to PostgreSQL payloads.
- **CSV Data Export**: Decrypted on-the-fly client-side CSV downloads.
**Should have (differentiators):**
- **GPS Pre-filling**: Instant fetch of coordinates via browser Geolocation.
- **Weather API Integration**: Prefill coordinates' weather and wind parameters via OpenWeatherMap.
- **Adaptive OS UI**: CSS themes styled to blend with Android Material or iOS Cupertino designs.
- **GPS & Weather Integration**: Browser Geolocation API and OpenWeatherMap lookup with offline fallbacks.
- **Adaptive OS UI**: Platforms adaptive Cupertino (iOS) and Material (Android) themes.
### Architecture Approach
Detailed in [ARCHITECTURE.md](ARCHITECTURE.md).
**Major components:**
1. **IndexedDB (Dexie)**: Structured database tables for yacht metadata, crew, compass deviations, and logbook entries.
2. **App Shell UI**: A single-page layout with responsive page navigation, form views, and a storage status check.
3. **Application Services**: Isolated logic handlers for GPS acquisition, OpenWeather API requests, translation bundles, and CSV builders.
4. **Service Worker Cache**: Local static asset container ensuring immediate PWA load times.
- **Zero-Knowledge Backend**: Handles Passkey verification and stores E2E encrypted string blocks without access to user keys.
- **IndexedDB Caching Layer**: Local cache database holding encrypted versions of active logbooks and syncQueue.
- **E2E Cryptographic Service**: Web Crypto utilities implementing PRF-derived keys and BIP39 fallback phrases.
- **Offline PWA Shell**: Service worker caching front-end assets for offline launch.
### Critical Pitfalls
Mitigation steps are outlined in [PITFALLS.md](PITFALLS.md).
1. **iOS Safari Storage Purge**: Safely bypassed by adding instructions for PWA installation (preventing Safari's 7-day inactivity purge) and requesting persistent storage.
2. **Offline Weather API Timeouts**: Resolved by checking connection status and setting a strict 5-second timeout on requests.
3. **GPS Fetch Delays**: Mitigated by setting high-accuracy timeouts, validating precision, and offering full manual entry.
4. **SW Caching Stale Updates**: Handled by adding update prompts instead of automatic service worker overrides.
1. **E2E Key Recovery Loss**: Force recovery phrase validation during sign-up to prevent permanent data loss.
2. **WebAuthn Compatibility**: Check authenticator status and fallback to hardware security keys.
3. **Offline Sync Conflicts**: Utilize transaction logs, last-write-wins rules, and append-only entries with merge warnings.
4. **Offline Weather/GPS Timeouts**: Set 5-second weather fetch timeouts and 10-second GPS lock limits.
## Implications for Roadmap
Based on research and dependencies, we suggest a 4-phase rollout:
Based on research and dependencies, we suggest a revised 4-phase rollout:
### Phase 1: Foundation & Data Infrastructure
- **Rationale**: Setting up the development bundle, service worker, and local database first ensures all subsequent screens can read/write data in real-time.
- **Delivers**: PWA shell, Dexie DB database models, and English/German language configuration.
- **Addresses**: DATA-01 (Local storage), DATA-02 (Offline), GEN-02 (Multilingual).
- **Avoids**: Service worker stale caching updates (set up correctly at the beginning).
### Phase 1: Foundation, Auth & E2E Crypto
- **Rationale**: Creating the client PWA and Express backend repositories first allows building the core security boundary (Passkeys and Web Crypto key derivation) before handling user data.
- **Delivers**: Node backend, Prisma schema, WebAuthn options endpoints, and Web Crypto PRF/recovery helpers.
- **Addresses**: AUTH-01 (Passkeys), CRYPTO-01/02/03 (E2E encryption), UI-02/03 (Languages).
### Phase 2: Vessel & Crew Management (Stammdaten)
- **Rationale**: Master data must be created before a skipper can log a journey.
- **Delivers**: Vessel specifications form, Crew lists management, and the Compass Deviation Grid.
- **Addresses**: MASTER-01 (Vessel/Crew data), Steuertafel.
- **Uses**: Dexie tables for yacht, crew, and deviation.
### Phase 2: Sync Protocol & Multi-Logbooks
- **Rationale**: Developing the caching database, sync protocol, and multi-logbook views next ensures all subsequent UI features sync automatically in the background.
- **Delivers**: Multi-logbook dashboards, Local IndexedDB cache tables, and synchronization transaction queue API.
- **Addresses**: AUTH-02/03 (Multi-logbooks), SYS-02 (Local-first sync).
### Phase 3: Logbook Entries & Integration
- **Rationale**: Core logbook entries require the vessel profile and deviation calculations defined in Phase 2.
- **Delivers**: Log list, log entry form with weather/sea/sail selectors, GPS position pre-fill, and OpenWeather API hook.
- **Addresses**: LOG-01 (Forms), LOG-02 (Weather), LOG-03 (GPS).
- **Avoids**: GPS fetch delays and offline API hangs (using timeouts and fallbacks).
### Phase 3: Master Data & Log entries
- **Rationale**: The UI forms can now read/write securely from the E2E-encrypted sync layer built in Phase 2.
- **Delivers**: Yacht forms, Crew lists, deviation grids, and journal entry records synced to the database.
- **Addresses**: VESSEL-01/02/03 (Boat/crew profiles), DEV-01/02 (Deviation grid), LOG-01/02/03/04/05 (Journal logs), INT-01/02/03 (GPS & Weather).
### Phase 4: Data Export & UI Polish
- **Rationale**: Finalizing data portability and polishing visual alignment.
- **Delivers**: CSV export, local mail/share handlers, storage persistence checks, and iOS/Android adaptive themes.
- **Addresses**: DATA-03 (CSV Export), GEN-01 (Mobile-first adaptive UI).
- **Avoids**: iOS storage purges (by implementing persistent storage requests and PWA install warnings).
### Phase 4: CSV Export & UI Polish
- **Rationale**: Compiling exports and polishing OS-native layouts completes the user cycle.
- **Delivers**: Client-side decryption CSV builder, sync progress indicators, and Material/Cupertino styles.
- **Addresses**: SYS-03/04 (CSV Share/Export), UI-01 (Adaptive UI layout).
### Phase Ordering Rationale
The suggested order establishes database schemas first, followed by static profiles, dynamic transaction forms, and finally export utilities. This minimizes developer friction by ensuring database queries can be fully tested in mock views before building the complex logbook interface.
The suggested order establishes the backend server and cryptographic vault first. Next, it handles multi-logbook syncing so that when the master forms and log entries are coded in Phase 3, they plug immediately into a working, encrypted offline sync pipeline.
### Research Flags
@@ -101,22 +97,24 @@ The suggested order establishes database schemas first, followed by static profi
| Area | Confidence | Notes |
|------|------------|-------|
| Stack | HIGH | Vite React TS is standard for offline PWAs. Dexie is well-maintained and reliable. |
| Features | HIGH | Extracted directly from official PDF example logbook layout. |
| Architecture | HIGH | Standard client-side-only architecture without server dependencies. |
| Pitfalls | HIGH | Service worker caching and iOS storage limits are thoroughly documented in developer circles. |
| Stack | HIGH | Express, Prisma, Postgres, and Dexie are highly reliable. SimpleWebAuthn makes Passkey integration straightforward. |
| Features | HIGH | Expanded requirements map directly to nautical guidelines while preserving user ownership. |
| Architecture | HIGH | Hybrid client-side E2E zero-knowledge design has been proven by services like Proton. |
| Pitfalls | HIGH | Identified key recovery and offline sync conflict risks early with mitigation strategies. |
**Overall confidence:** HIGH
### Gaps to Address
- **OpenWeatherMap API Key**: The app requires an API key. We will implement a setting allowing users to provide their own OpenWeatherMap API key (saved in LocalStorage) so that the app remains open-source, client-side, and avoids developer key exposure.
- **OpenWeatherMap API Key**: Stored client-side in LocalStorage to avoid server exposure or leakage of developer keys.
- **Database Hosting**: Will require configuring a PostgreSQL instance (e.g. Docker container locally or cloud host for production).
## Sources
- [Vite PWA Docs](https://vite-pwa-org.netlify.app/)
- [Dexie.js Documentation](https://dexie.org/)
- [Apple WebKit Storage Guidelines](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/)
- [WebAuthn Specification & SimpleWebAuthn Docs](https://webauthn.guide/)
- [Web Crypto API Reference (W3C)](https://www.w3.org/TR/WebCryptoAPI/)
---
*Research completed: 2026-05-26*
Regular → Executable
+62 -20
View File
@@ -1,68 +1,110 @@
<!-- GSD:project-start source:PROJECT.md -->
## Project
# Kapteins Daagbox (Kapteins Daagbog)
**Kapteins Daagbox (Kapteins Daagbog)**
## What This Is
Kapteins Daagbox is a modern, mobile-first Progressive Web Application (PWA) designed for private yacht owners and captains to manage their ship's logbook digitally. The application enables users to log vessel master data, owner details, crew information, and daily logbook entries conforming to official maritime standards.
**Core Value:** Providing a private-by-design, fully offline-capable mobile maritime logbook that respects absolute user privacy by storing data 100% locally on the device while assisting the skipper with GPS position capture and automated weather integration.
## Core Value
### Constraints
Providing a private-by-design, fully offline-capable mobile maritime logbook that respects absolute user privacy by storing data in an end-to-end (E2E) encrypted server-side database (with local-first browser cache), while assisting the skipper with GPS position capture and automated weather integration.
- **Storage**: Must be stored exclusively client-side (IndexedDB / LocalStorage / Origin Private File System).
- **Privacy**: No external telemetry or cloud database connections.
- **Offline**: The app must load and operate fully without internet access (using Service Workers).
## Constraints
- **Storage**: Client-side IndexedDB for offline-first caching, synced with an end-to-end (E2E) encrypted server database.
- **Privacy**: Zero-knowledge architecture. No unencrypted user data on the server; no external telemetry.
- **Offline**: The app must load and operate fully without internet access (using Service Workers), caching operations to sync later.
- **Languages**: German and English.
<!-- GSD:project-end -->
<!-- GSD:stack-start source:research/STACK.md -->
## Technology Stack
# Stack Research
## Recommended Stack
We recommend a **React + TypeScript + Vite** stack wrapped with **vite-plugin-pwa** (Workbox) and **Dexie.js** (IndexedDB) for local data storage. This web stack compiles to extremely lightweight assets that load fast on poor connections, works 100% offline, and can easily be packaged using Capacitor if native iOS/Android store apps are required later.
### Core Technologies
| Technology | Version | Purpose | Why Recommended |
|------------|---------|---------|-----------------|
| **React** | 18.x / 19.x | UI Library | Component-driven architecture allows building a modular, reactive UI that easily handles state transitions for logbook forms. |
| **TypeScript** | 5.x | Language | Enforces strict type safety across logbook entries and crew models, preventing runtime errors. |
| **Vite** | 5.x | Build Tool | Extremely fast bundler and dev server; offers direct support for PWAs via plugins. |
| **TailwindCSS** / **Vanilla CSS** | 3.x / 4.x | Styling | Allows responsive, adaptive styling to match Android/iOS aesthetics and handles mobile viewport constraints. |
| **TypeScript** | 5.x | Language | Enforces strict type safety across client and server. |
| **Vite** | 5.x | Build Tool | Fast client packager and Service Worker compiler. |
| **Node.js (Express)** | 20.x | Backend Server | Lightweight API backend to handle WebAuthn challenges and database storage. |
| **PostgreSQL** | 16.x | Relational DB | Robust storage of credentials and E2E-encrypted JSON payloads. |
| **Prisma** | 5.x | ORM | Type-safe SQL database client interface. |
| **TailwindCSS** / **Vanilla CSS** | 3.x / 4.x | Styling | Responsive styling matching iOS/Android. |
### Supporting Libraries
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| **Dexie.js** | 4.x | IndexedDB Wrapper | Required for robust, structured offline-first storage of ship data and log entries. |
| **vite-plugin-pwa** | 0.20.x | PWA / Service Worker | Handles automatic service worker registration, offline caching of assets, and install prompts. |
| **react-i18next** | 14.x | Multilingual (l18n) | Seamless translation management for German and English with automatic language detection. |
| **react-i18next** | 14.x | Multilingual (l18n) | Seamless translation management for German and English. |
| **@simplewebauthn/server** | 9.x / 10.x | Passkey Verification | Validates WebAuthn registration/login assertions on the server. |
| **@simplewebauthn/browser** | 9.x / 10.x | Passkey Client helper | Connects WebAuthn client requests to browser credentials API. |
| **bip39** | 3.x | Recovery phrases | Generates 12-word mnemonic phrases for E2E encryption fallbacks. |
| **lucide-react** | 0.300.x | SVG Icons | Light, modern icon library for weather states, navigation, and logbook actions. |
### Development Tools
| Tool | Purpose | Notes |
|------|---------|-------|
| **ESLint / Prettier** | Code linting and formatting | Standardizes code style and catches early bugs. |
| **ESLint / Prettier** | Code linter and formatting | Standardizes code style and catches early bugs. |
| **Lighthouse / DevTools** | PWA and Performance auditing | Essential for testing offline loading and installability criteria. |
## Installation
# Core & UI
# Storage, PWA & Localization
# Dev Dependencies
```bash
# Client Core & UI
npm install react react-dom lucide-react
# Client Storage, PWA, Localization & Cryptography
npm install dexie dexie-react-hooks react-i18next i18next i18next-browser-languagedetector @simplewebauthn/browser bip39
# Client Dev Dependencies
npm install -D typescript @types/react @types/react-dom vite @vitejs/plugin-react vite-plugin-pwa
# Backend Core, ORM & Auth
npm install express dotenv cors @simplewebauthn/server @prisma/client
npm install -D tsx @types/express @types/node @types/cors prisma
```
## Alternatives Considered
| Recommended | Alternative | When to Use Alternative |
|-------------|-------------|-------------------------|
| **Vite React PWA** | **Flutter Web PWA** | Flutter is strong for native compilation, but Flutter Web suffers from large canvas-kit bundles (2.5MB+ JS load size), which makes it poor for remote maritime connections. Use Flutter if native store presence is the absolute priority from Day 1 and web-performance is secondary. |
| **Dexie.js (IndexedDB)** | **LocalStorage** | LocalStorage is simpler but limited to ~5MB and is synchronous. IndexedDB handles large datasets (e.g. photos in logbooks, years of log entries) asynchronously and is recommended for production. |
## What NOT to Use
| Avoid | Why | Use Instead |
|-------|-----|-------------|
| **Firebase / Supabase** | Requires online connection for core operations; violates "local-only" strict privacy constraint. | Dexie.js + Local IndexedDB |
| **Plain Firebase / Supabase DB Sync** | Default syncing exposes raw unencrypted database schemas to the cloud provider, violating zero-knowledge privacy. | Custom Node/Express server + E2E encrypted payloads database |
| **Bootstrap** | Heavy, outdated styling that doesn't adapt well to modern native mobile looks. | TailwindCSS / CSS variables |
## Stack Patterns by Variant
- Use IndexedDB (via Dexie.js) for storage.
- Because IndexedDB runs entirely in the user's browser sandbox and has no cloud sync, guaranteeing absolute data ownership.
**If strict user privacy with multi-device access is required:**
- Use Client-Side E2E Encryption (AES-GCM-256) combined with Local-first IndexedDB (via Dexie.js) synced to a zero-knowledge PostgreSQL server.
- Because client-side encryption ensures the server operator only holds encrypted payloads, retaining absolute user data privacy.
**If poor connectivity is expected:**
- Configure Workbox Service Worker in "CacheFirst" or "StaleWhileRevalidate" mode.
- Because it ensures the application shell loads immediately even with 0% network connection.
- Because it ensures the application shell loads immediately even with 0% network connection, queueing synchronization packets until the device is online.
## Version Compatibility
| Package A | Compatible With | Notes |
|-----------|-----------------|-------|
| `vite-plugin-pwa@0.20.x` | `vite@5.x` | Standard integration for Vite-based PWAs. |
| `dexie-react-hooks@4.x` | `react@18.x` | Allows React components to reactively observe IndexedDB query changes. |
## Sources
- [Vite PWA Docs](https://vite-pwa-org.netlify.app/) — PWA caching and service worker strategies.
- [Dexie.js Documentation](https://dexie.org/) — Schema migrations and IndexedDB reactive binding.
- [Web Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) — Browser geolocation capabilities.
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+73
View File
@@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
+22
View File
@@ -0,0 +1,22 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
},
},
])
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>client</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+7472
View File
File diff suppressed because it is too large Load Diff
+39
View File
@@ -0,0 +1,39 @@
{
"name": "client",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@simplewebauthn/browser": "^13.3.0",
"bip39": "^3.1.0",
"dexie": "^4.4.2",
"dexie-react-hooks": "^4.4.0",
"i18next": "^26.3.0",
"i18next-browser-languagedetector": "^8.2.1",
"lucide-react": "^1.16.0",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-i18next": "^17.0.8"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^24.12.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.3.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.6.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.12",
"vite-plugin-pwa": "^1.3.0"
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 KiB

+184
View File
@@ -0,0 +1,184 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}
+122
View File
@@ -0,0 +1,122 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
type="button"
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
}
export default App
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

+25
View File
@@ -0,0 +1,25 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import enTranslation from './locales/en.json'
import deTranslation from './locales/de.json'
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: enTranslation,
de: deTranslation
},
fallbackLng: 'en',
interpolation: {
escapeValue: false // React already escapes values (prevents XSS)
},
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage']
}
})
export default i18n
+49
View File
@@ -0,0 +1,49 @@
{
"translation": {
"app": {
"name": "Kapteins Daagbox",
"tagline": "Privates Yacht-Logbuch"
},
"nav": {
"dashboard": "Dashboard",
"vessel": "Schiffsdaten",
"crew": "Crew-Liste",
"deviation": "Ablenkungstabelle",
"logs": "Logbucheinträge",
"settings": "Einstellungen"
},
"auth": {
"welcome": "Willkommen bei Kapteins Daagbox",
"tagline": "Sicheres, E2E-verschlüsseltes maritimes Logbuch.",
"register": "Mit Passkey registrieren",
"login": "Mit Passkey anmelden",
"recovery_title": "Ihr Wiederherstellungsschlüssel",
"recovery_warning": "WICHTIG: Schreiben Sie diese 12 Wörter auf. Wenn Sie Ihren Passkey und diese Wörter verlieren, können Ihre Daten nicht wiederhergestellt werden.",
"confirm_recovery": "Ich habe die Wörter aufgeschrieben",
"status_logged_in": "Angemeldet",
"status_logged_out": "Abgemeldet"
},
"sync": {
"status_synced": "Synchronisiert",
"status_offline": "Offline-Cache",
"status_unsynced": "Unsynchronisierte Änderungen"
},
"vessel": {
"title": "Schiffs-Stammdaten",
"name": "Yachtname",
"port": "Heimathafen",
"owner": "Eigner",
"callsign": "Funk-Rufzeichen",
"mmsi": "MMSI-Nr"
},
"logs": {
"title": "Logbuch-Journal",
"new_entry": "Neuer Eintrag",
"date": "Datum",
"route": "Reise von/nach",
"coordinates": "Koordinaten",
"weather": "Wetterbedingungen",
"save": "Eintrag speichern"
}
}
}
+49
View File
@@ -0,0 +1,49 @@
{
"translation": {
"app": {
"name": "Kapteins Daagbox",
"tagline": "Private Yacht Logbook"
},
"nav": {
"dashboard": "Dashboard",
"vessel": "Vessel Profile",
"crew": "Crew List",
"deviation": "Deviation Table",
"logs": "Logbook Entries",
"settings": "Settings"
},
"auth": {
"welcome": "Welcome to Kapteins Daagbox",
"tagline": "Secure, E2E encrypted maritime logbook.",
"register": "Register with Passkey",
"login": "Login with Passkey",
"recovery_title": "Your Recovery Phrase",
"recovery_warning": "IMPORTANT: Write down these 12 words. If you lose your Passkey and these words, your data cannot be recovered.",
"confirm_recovery": "I have written down the recovery phrase",
"status_logged_in": "Logged in",
"status_logged_out": "Logged out"
},
"sync": {
"status_synced": "Synced",
"status_offline": "Offline Cache",
"status_unsynced": "Unsynced changes"
},
"vessel": {
"title": "Vessel Master Data",
"name": "Yacht Name",
"port": "Home Port",
"owner": "Owner",
"callsign": "Call Sign",
"mmsi": "MMSI Number"
},
"logs": {
"title": "Logbook Journal",
"new_entry": "New Log Entry",
"date": "Date",
"route": "Route",
"coordinates": "Coordinates",
"weather": "Weather Conditions",
"save": "Save Entry"
}
}
}
+111
View File
@@ -0,0 +1,111 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
body {
margin: 0;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
+11
View File
@@ -0,0 +1,11 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import './i18n'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
+7
View File
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
+24
View File
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"module": "esnext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}
+32
View File
@@ -0,0 +1,32 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePWA } from 'vite-plugin-pwa'
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'prompt',
includeAssets: ['favicon.ico', 'logo.png'],
manifest: {
name: 'Kapteins Daagbox',
short_name: 'Daagbox',
description: 'Digital maritime ship logbook with E2E encryption and Passkeys',
theme_color: '#1e293b',
background_color: '#0f172a',
display: 'standalone',
start_url: '/',
orientation: 'portrait',
icons: [
{
src: 'logo.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable'
}
]
}
})
]
})
Regular → Executable
View File
+1826
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "server",
"version": "1.0.0",
"description": "Backend API for Kapteins Daagbox",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx watch src/index.ts"
},
"dependencies": {
"@prisma/client": "^5.10.2",
"@simplewebauthn/server": "^9.0.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/node": "^20.11.24",
"prisma": "^5.10.2",
"tsx": "^4.7.1",
"typescript": "^5.3.3"
}
}
+90
View File
@@ -0,0 +1,90 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(uuid())
username String @unique
createdAt DateTime @default(now())
credentials Credential[]
logbooks Logbook[]
}
model Credential {
id String @id @default(uuid())
userId String
credentialId String @unique
publicKey Bytes
counter BigInt
transports String[] // WebAuthn transports list
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model Logbook {
id String @id @default(uuid())
userId String
encryptedTitle String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
yachts YachtPayload[]
crews CrewPayload[]
deviations DeviationPayload[]
entries EntryPayload[]
@@index([userId])
}
model YachtPayload {
id String @id @default(uuid())
logbookId String @unique
encryptedData String
iv String
tag String
updatedAt DateTime @updatedAt
logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade)
}
model CrewPayload {
id String @id @default(uuid())
logbookId String
payloadId String
encryptedData String
iv String
tag String
updatedAt DateTime @updatedAt
logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade)
@@unique([logbookId, payloadId])
}
model DeviationPayload {
id String @id @default(uuid())
logbookId String @unique
encryptedData String
iv String
tag String
updatedAt DateTime @updatedAt
logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade)
}
model EntryPayload {
id String @id @default(uuid())
logbookId String
payloadId String
encryptedData String
iv String
tag String
updatedAt DateTime @updatedAt
logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade)
@@unique([logbookId, payloadId])
@@index([logbookId])
}
+24
View File
@@ -0,0 +1,24 @@
import express from 'express'
import cors from 'cors'
import dotenv from 'dotenv'
dotenv.config()
const app = express()
const PORT = process.env.PORT || 5000
app.use(cors())
app.use(express.json())
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
service: 'Kapteins Daagbox Backend'
})
})
app.listen(PORT, () => {
console.log(`[server] Server running on http://localhost:${PORT}`)
})
+16
View File
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"]
}