Some checks failed
Deploy to NAS / deploy (push) Failing after 26s
- Public feed (/) with infinite scroll via Intersection Observer - Self-registration (/register) - Admin area (/admin/entries, /admin/users) with user management - journal_entries: visibility (public/private) + hashtags fields - users: is_admin flag - DB schema updated (recreate DB to apply) - CI: run go test via docker run (golang:1.25-alpine) — fixes 'go not found' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
24 KiB
24 KiB
Pamietnik — Architekturdokumentation (arc42)
1. Einführung und Ziele
Pamietnik ist ein persönliches Journal bestehend aus drei Komponenten: einer Android-App, einer Web-App und einem Go-Backend-Server.
Fachliches Zielbild
- Android-App loggt GPS-Standortdaten im Hintergrund, cached offline, lädt bevorzugt per HTTP zum Server hoch
- Manuelles Hinzufügen von Punkten (ohne GPS) mit optionaler Notiz und Hashtags
- Server erkennt aus Logdaten „Stops" (längere Aufenthalte) und leitet Vorschläge ab
- Standortinformationen (Reverse-Geocoding) über kostenlose API (Nominatim/OSM)
- Web-Ansicht nach Login: Tagesübersicht und Tagesdetail mit Karte, Stops, Vorschlägen
- Datei-Export aus der App (Android SAF)
- Nutzung erfordert Account und Login
Qualitätsziele
| Priorität | Ziel | Maßnahme |
|---|---|---|
| 1 | Datensicherheit | Argon2id Passwort-Hashing, Session-Auth, HTTPS |
| 2 | Offline-Fähigkeit | Room + WorkManager auf Android, kein RAM-only Queue |
| 3 | Idempotenz | event_id (UUID) als Unique Key pro Device |
| 4 | Erweiterbarkeit | Geocoding-Provider über Config austauschbar |
| 5 | Datenschutz | Background-Location Disclosure (Google Play) |
Stakeholder
| Rolle | Erwartung |
|---|---|
| Mobiler Nutzer | Automatisches Tracking, Offline-Betrieb, Export |
| Web-Nutzer | Login, Tagesübersicht mit Karte, Stops und Vorschläge |
2. Randbedingungen
| Art | Bedingung |
|---|---|
| Technisch | Android: Kotlin + Jetpack Compose (DEC-CLIENT-01) |
| Technisch | Backend: Go, PostgreSQL (DEC-DB-01) |
| Technisch | Webapp: Vanilla TypeScript, keine Frameworks |
| Technisch | Server-Schnittstelle: HTTP/REST, kein MQTT (DEC-API-01) |
| Technisch | Karten: OpenStreetMap + MapLibre, konfigurierbare Tile-Quelle (DEC-MAP-01) |
| Technisch | API-Spec: OpenAPI 3.1 als openapi.yaml (DEC-OPENAPI-01) |
| Organisatorisch | Keine kommerziell lizenzierten Bibliotheken oder Schriften |
| Organisatorisch | Geocoding über kostenlose API, Policy-konform (DEC-GEO-01) |
| Organisatorisch | Alle Anforderungen in Markdown; Diagramme in Mermaid (DEC-DOC-01) |
3. Kontextsicht
Fachlicher Kontext
| Kommunikationspartner | Interaktion |
|---|---|
| Benutzer mobil | Start/Stop Trip, Status, Export, manuelle Punkte, Hashtags |
| Benutzer web | Login, Tagesübersicht, Tagesdetail, Stops, Vorschläge, Tag-Filter |
| Android OS / Location Services | Liefert GPS-Standortupdates |
| Webserver | Nimmt Trackpoints entgegen, berechnet Stops, stellt Website bereit |
| Geocoding-Dienst (Nominatim) | Liefert Adress-/Place-Infos zu Koordinaten |
| Dateisystem via SAF | Export-Ziel für Logs/Trips |
Technischer Kontext
flowchart LR
UserM[Nutzer mobil]
UserW[Nutzer Web]
App[Android App\nKotlin + Compose]
OS[Android Location Services]
Webapp[Webapp\nTS + nginx :9050]
API[Go Backend\nREST + Web UI]
DB[(PostgreSQL)]
GEO[Nominatim\nReverse-Geocoding]
SAF[Android SAF\nDatei-Export]
UserM -->|Start/Stop\nManuelle Punkte\nHashtags| App
OS -->|GPS Updates| App
App -->|HTTPS REST JSON\nPOST /v1/trackpoints:batch| API
UserW -->|HTTPS Browser| Webapp
Webapp -->|/v1/* proxy_pass| API
API --> DB
API -->|HTTPS cached| GEO
App -->|ACTION_CREATE_DOCUMENT| SAF
| Schnittstelle | Protokoll | Auth |
|---|---|---|
| Android → Backend | HTTPS REST JSON | Session Cookie (TBD: API-Key/JWT) |
| Browser → Backend | HTTPS | Session Cookie |
| Backend → Nominatim | HTTPS | — (Rate-Limit, gecached) |
| App → Dateisystem | Android SAF | OS-Permission |
4. Lösungsstrategie
| Entscheidung | ID | Begründung |
|---|---|---|
| Android: Kotlin + Jetpack Compose | DEC-CLIENT-01 | Android „Kotlin-first", moderne UI |
| Go Single Binary als Backend | — | Geringe Ressourcen, einfaches Deployment |
| Distroless Docker Image | — | Minimale Angriffsfläche |
Schema via go:embed + CREATE TABLE IF NOT EXISTS |
DEC-SCHEMA-01 | Keine Migrations-Infrastruktur, idempotenter Start |
| Room (SQLite) auf Android | DEC-DB-01 | Offline-First Persistenz |
| PostgreSQL im Backend | DEC-DB-01 | JSONB, UUID-Funktionen, zuverlässig |
| Session Cookie statt JWT (Web) | DEC-AUTH-01 | Serverseitige Invalidierung, einfacheres Threat Model |
| Argon2id für Passwort-Hashing | REQ-AUTH-01 | GPU-resistent, State-of-the-art |
| MapLibre + OpenStreetMap | DEC-MAP-01 | Open Source, keine Lizenzkosten, konfigurierbare Tiles |
| Nominatim als Geocoding-Provider | DEC-GEO-01 | Kostenlos, OSM-basiert, Provider austauschbar |
| Vanilla Web Components | — | Kein Framework-Overhead, langlebig, keine Lizenzkonflikte |
| OpenAPI 3.1 als API-Spec | DEC-OPENAPI-01 | Maschinenlesbar, Validierbar in CI |
5. Bausteinsicht
Ebene 1 — Systeme
flowchart TB
subgraph Mobile[Android App — app/]
UI[Compose UI\nScreens + ViewModels]
Domain[Domain / Use-Cases]
LocalDB[(Room SQLite\nLocal DB + Queue)]
Worker[WorkManager\nUpload Worker]
Service[Foreground Service\nGPS Tracking]
Export[Export SAF]
end
subgraph Server[Go Backend — backend/]
Router[chi Router\n+ Middleware]
IngestAPI[Ingest API\nPOST /v1/trackpoints]
QueryAPI[Query API\nGET /v1/*]
WebUI[Web UI\nGo Templates SSR]
Auth[Auth Store\nArgon2id + Sessions]
Stores[DB Stores\nTrackpoints / Stops\nSuggestions / Journal]
StopEngine[Stop Detection\n+ Suggestions]
GeoAdapter[Geocoding Adapter\n+ Cache + RateLimit]
DB[(PostgreSQL)]
end
subgraph Web[Webapp — webapp/]
SPA[Vanilla TS SPA\nWeb Components]
Map[TrackMap\nMapLibre GL]
Nginx[nginx\nProxy + Static Files]
end
Mobile -->|HTTPS Batch| IngestAPI
SPA -->|HTTPS /v1/*| Nginx
Nginx -->|proxy_pass| Router
Router --> IngestAPI
Router --> QueryAPI
Router --> WebUI
Router --> Auth
IngestAPI --> Stores
QueryAPI --> Stores
WebUI --> Stores
Auth --> DB
Stores --> DB
StopEngine --> GeoAdapter
GeoAdapter -->|HTTPS| GEO[Nominatim]
Ebene 2 — Backend-Pakete
backend/
├── cmd/
│ ├── server/ Einstiegspunkt: Pool → InitSchema → Router → HTTP Server
│ └── createuser/ CLI: User mit Argon2id-Hash anlegen
└── internal/
├── domain/ Shared Types: Trackpoint, Stop, Suggestion, Session, User
├── db/
│ ├── schema.sql Eingebettetes Schema (go:embed, IF NOT EXISTS)
│ ├── db.go NewPool + InitSchema
│ ├── trackpoints.go UpsertBatch, ListByDate, ListDays, EnsureDevice
│ ├── stops.go ListByDate
│ ├── suggestions.go ListByDate
│ ├── journal.go CRUD Journal Entries + Images; ListPublic, ListByUser
│ └── users.go ListUsers, DeleteUser
├── auth/
│ └── auth.go HashPassword, VerifyPassword, Login, Register, GetSession, Logout
└── api/
├── router.go chi Routing, Middleware-Gruppen
├── middleware.go RequireAuth, requireAdmin (Session Cookie → Context)
├── ingest.go HandleSingleTrackpoint, HandleBatchTrackpoints
├── query.go HandleListDays, HandleListTrackpoints, Stops, Suggestions
├── webui.go Web UI: Feed, Register, Days, Admin-Handlers
├── journal.go Journal Entry Endpoints (inkl. visibility + hashtags)
└── response.go writeJSON, writeError helpers
Ebene 2 — Android-Pakete
app/app/src/main/kotlin/de/jacek/pamietnik/
├── domain/ Trackpoint Domain Model
├── data/ Room Entities, DAOs, lokale DB
├── service/ Background Location Foreground Service
├── worker/ WorkManager Upload Worker
└── ui/
├── home/ HomeScreen (Compose) + HomeViewModel
├── navigation/ NavGraph
└── theme/ Compose Theme
6. Laufzeitsicht
R1 — Trip starten und Trackpoints senden
sequenceDiagram
participant App as Android App
participant Room as Room DB
participant Worker as WorkManager
participant API as Go Backend
participant DB as PostgreSQL
App->>Room: Trackpoint persistieren (source=gps, pending)
Worker->>Room: Pending Batch laden (NetworkConnected)
Worker->>API: POST /v1/trackpoints:batch + Cookie
API->>DB: EnsureDevice (INSERT ... ON CONFLICT DO NOTHING)
API->>DB: UpsertBatch (ON CONFLICT device+event_id DO NOTHING)
API-->>Worker: {accepted_ids, rejected: []}
Worker->>Room: Accepted als "sent" markieren
R2 — Offline → Online Retry (REQ-SYNC-02, REQ-SYNC-04)
sequenceDiagram
participant Worker as WorkManager
participant Room as Room DB
participant API as Go Backend
Worker->>API: POST /v1/trackpoints:batch
API--xWorker: Netzwerkfehler / Timeout
Worker->>Room: Pending belassen, retryCount++
Note over Worker: Exponential Backoff\nConstraint: NetworkConnected
Worker->>API: POST /v1/trackpoints:batch (gleiche event_ids)
API-->>Worker: {accepted_ids} (Duplikate via ON CONFLICT ignoriert)
Worker->>Room: Accepted als "sent" markieren
R3 — Web Login und Tagesansicht (REQ-WEB-01, REQ-WEB-02)
sequenceDiagram
participant B as Browser
participant Nginx as nginx :9050
participant API as Go Backend
participant DB as PostgreSQL
B->>Nginx: POST /login (username, password)
Nginx->>API: proxy_pass
API->>DB: User laden, Argon2id verify
API-->>B: Set-Cookie: session=..., 303 /days
B->>Nginx: GET /v1/days?from=&to= + Cookie
Nginx->>API: proxy_pass
API->>DB: trackpoints JOIN devices WHERE user_id = $1
API-->>B: [{date, count, first_ts, last_ts}]
B->>Nginx: GET /v1/trackpoints?date=YYYY-MM-DD + Cookie
API->>DB: trackpoints WHERE date = $2 AND user_id = $1
API-->>B: [Trackpoint...]
R4 — Manuellen Punkt hinzufügen (REQ-MAN-01 bis REQ-MAN-05)
sequenceDiagram
participant U as Nutzer
participant VM as ViewModel
participant Room as Room DB
participant Worker as WorkManager
participant API as Go Backend
U->>VM: Eingabe lat/lon/timestamp/note/tags
VM-->>U: Validierung as-the-user-types
VM->>Room: Trackpoint (source=manual, tags=[...], pending)
Worker->>Room: Fetch pending
Worker->>API: POST /v1/trackpoints:batch
API-->>Worker: Ack
Worker->>Room: Mark sent
R5 — Stop-Erkennung und Geocoding (REQ-SUG-01, REQ-GEO-01)
sequenceDiagram
participant API as Go Backend
participant SE as Stop Engine
participant GEO as Geocoding Adapter
participant NOM as Nominatim
participant DB as PostgreSQL
API->>SE: Trigger nach Batch-Ingest (async)
SE->>DB: Letzte Trackpoints lesen
SE->>SE: Stop erkennen (minDuration, radiusMeters)
SE->>GEO: Reverse-Geocode Stop-Koordinate
alt Cache Hit
GEO-->>SE: Place Info (gecached)
else Cache Miss
GEO->>NOM: HTTPS Reverse-Geocode
NOM-->>GEO: Place Info
GEO-->>SE: Place Info (jetzt gecached)
end
SE->>DB: Stop + Suggestion speichern
R6 — Schema-Initialisierung beim Server-Start
sequenceDiagram
participant S as cmd/server
participant DB as PostgreSQL
S->>DB: pgxpool.New + Ping
S->>DB: schema.sql ausführen (go:embed)\nCREATE TABLE IF NOT EXISTS ...
Note over DB: Idempotent — bestehende\nTabellen bleiben erhalten
S->>S: Stores + Router aufbauen
S->>S: HTTP Server starten :8080
7. Verteilungssicht
flowchart LR
Phone[Android Device]
Browser[Web Browser]
subgraph Docker[Docker Compose]
Nginx[webapp\nnginx:alpine\n:9050]
API[api\ndistroless\n:8080 intern]
PG[postgres\npostgres:16-alpine\ninternal]
end
GEO[Nominatim\nexternal HTTPS]
Phone -->|HTTPS| Nginx
Browser -->|HTTPS| Nginx
Nginx -->|/v1/* /login /logout\nproxy_pass| API
Nginx -->|/ static SPA| Nginx
API --> PG
API -->|cached Rate-Limited| GEO
Docker Compose Services:
| Service | Image | Port extern | Funktion |
|---|---|---|---|
postgres |
postgres:16-alpine | — (intern) | Datenhaltung |
api |
golang:1.25 → distroless | — (intern) | REST API + Web UI |
webapp |
node:22 → nginx:alpine | 9050 | SPA + API-Proxy |
Start:
docker-compose up --build
Schema wird beim API-Start automatisch initialisiert (keine separate Migration nötig).
7b. Datenbankschema
erDiagram
users {
TEXT user_id PK
TEXT username UK
TEXT password_hash
BOOLEAN is_admin
TIMESTAMPTZ created_at
}
sessions {
TEXT session_id PK
TEXT user_id FK
TIMESTAMPTZ created_at
TIMESTAMPTZ expires_at
}
devices {
TEXT device_id PK
TEXT user_id FK
TIMESTAMPTZ created_at
}
trackpoints {
BIGSERIAL id PK
TEXT event_id
TEXT device_id FK
TEXT trip_id
TIMESTAMPTZ ts
DOUBLE lat
DOUBLE lon
TEXT source
TEXT note
}
stops {
TEXT stop_id PK
TEXT device_id FK
TEXT trip_id
TIMESTAMPTZ start_ts
TIMESTAMPTZ end_ts
DOUBLE center_lat
DOUBLE center_lon
INT duration_s
TEXT place_label
}
suggestions {
TEXT suggestion_id PK
TEXT stop_id FK
TEXT type
TEXT title
TEXT text
TIMESTAMPTZ created_at
TIMESTAMPTZ dismissed_at
}
journal_entries {
TEXT entry_id PK
TEXT user_id FK
DATE entry_date
TIME entry_time
TEXT title
TEXT description
DOUBLE lat
DOUBLE lon
TEXT visibility
TEXT[] hashtags
TIMESTAMPTZ created_at
}
journal_images {
TEXT image_id PK
TEXT entry_id FK
TEXT filename
TEXT original_name
TEXT mime_type
BIGINT size_bytes
TIMESTAMPTZ created_at
}
users ||--o{ sessions : has
users ||--o{ devices : owns
users ||--o{ journal_entries : writes
devices ||--o{ trackpoints : records
stops ||--o{ suggestions : generates
journal_entries ||--o{ journal_images : contains
Wichtige Felder:
| Tabelle | Feld | Bedeutung |
|---|---|---|
users |
is_admin |
Admin-Flag für Zugang zum Admin-Bereich |
journal_entries |
visibility |
public = im öffentlichen Feed sichtbar; private = nur für Autor |
journal_entries |
hashtags |
Kommagetrennte Tags als TEXT[]-Array |
trackpoints |
(device_id, event_id) |
UNIQUE-Constraint für Idempotenz |
8. Querschnittskonzepte
Authentifizierung & Sessions (REQ-AUTH-01, REQ-AUTH-02, DEC-AUTH-01)
- Passwörter: Argon2id (
$argon2id$<saltHex>$<hashHex>) - Sessions in PostgreSQL (
sessions-Tabelle): sofortige Invalidierung bei Logout - Cookie:
HttpOnly,SameSite=Lax, 24h Lebensdauer - Android Upload-Auth: aktuell Session Cookie — TBD: API-Key oder JWT (DEC-AUTH-01)
Idempotenz (REQ-SYNC-04)
- Jeder Trackpoint trägt eine client-generierte
event_id(UUID empfohlen) - Unique Constraint
(device_id, event_id)in PostgreSQL ON CONFLICT DO NOTHING→ doppelter Upload = 200 OK, kein Fehler- Batch-Response enthält
accepted_idsundrejected[]für Queue-Steuerung
Offline-First (REQ-SYNC-01 bis REQ-SYNC-03)
- Jeder Punkt wird zuerst in Room (SQLite) persistiert
- Upload via WorkManager:
NetworkConnected-Constraint, Exponential Backoff source-Feld unterscheidet"gps"und"manual"
Device-Registrierung
EnsureDevice()beim ersten Ingest:INSERT ... ON CONFLICT DO NOTHING- Verknüpft
device_idmit authentifiziertemuser_id - Query-Endpoints filtern über
devices.user_id
Hashtags (REQ-TAG-01 bis REQ-TAG-06)
- Feld
tags: string[]im Trackpoint-Schema - Eingabe-Vorschläge aus vorhandenen Tags des Nutzers
- Server:
GET /v1/tags(Häufigkeit),GET /v1/trackpoints?tag=... - Webapp: Tag-Filter in Tagesdetail, Tag-Übersicht
Geocoding (REQ-GEO-01 bis REQ-GEO-03)
- Nur ereignisbasiert (pro Stop), niemals bulk/periodisch
- Cache vor jedem Provider-Call
- Provider über Config austauschbar (kein Hardcode)
- Nominatim: User-Agent korrekt setzen (Policy-Pflicht)
Karten (DEC-MAP-01, REQ-MAP-01, REQ-MAP-02)
- Android: MapLibre SDK
- Webapp: MapLibre GL JS (open source, lizenzfrei)
- Tile-Quelle konfigurierbar (
tileUrl-Property auf<track-map>)
9. Produktanforderungen (aus README)
3.1 Background-Logging
| ID | Anforderung |
|---|---|
| REQ-LOC-01 | App MUSS Standort im Vordergrund und Hintergrund erfassen |
| REQ-LOC-02 | COARSE (optional FINE) im Manifest + Runtime; App funktioniert mit „approximate" |
| REQ-LOC-03 | Background-Location korrekt angefordert |
| REQ-LOC-04 | foregroundServiceType="location" wenn Foreground Service |
3.2 Offline-First
| ID | Anforderung |
|---|---|
| REQ-SYNC-01 | Trackpoints zuerst lokal persistieren (kein RAM-Queue) |
| REQ-SYNC-02 | Upload bevorzugt; bei Fehler cachen und automatisch nachladen |
| REQ-SYNC-03 | WorkManager mit NetworkConnected + Retry/Backoff |
| REQ-SYNC-04 | Keine Duplikate bei Retries; Idempotenz via event_id |
3.3 Manuelle Punkte
| ID | Anforderung |
|---|---|
| REQ-MAN-01 | Manueller Punkt: lat, lon, timestamp (Pflicht); Name/Notiz (optional) |
| REQ-MAN-02 | Lokal in Room speichern |
| REQ-MAN-03 | In Upload-Flow integriert (inkl. event_id) |
| REQ-MAN-04 | Validierung as-the-user-types; verständliche Fehlermeldungen |
| REQ-MAN-05 | Vorschlag „aktueller Zeitpunkt / aktuelle Position" (editierbar) |
3.4 Export
| ID | Anforderung |
|---|---|
| REQ-EXP-01 | Export in Datei möglich |
| REQ-EXP-02 | Via SAF ACTION_CREATE_DOCUMENT; Nutzer wählt Speicherort |
3.5 Server Website
| ID | Anforderung |
|---|---|
| REQ-WEB-01 | Website nur nach Login zugänglich |
| REQ-WEB-02 | Pro Tag Punkte anzeigen (Liste + Detailansicht) |
| REQ-WEB-03 | Daten nutzerspezifisch (ein Nutzer sieht nur eigene Daten) |
| REQ-WEB-04 | Kartenansicht (Punkte/Stops pro Tag) |
3.6 Security / Auth
| ID | Anforderung |
|---|---|
| REQ-AUTH-01 | Passwörter NICHT im Klartext; Argon2id |
| REQ-AUTH-02 | Session Cookie; serverseitig verwaltbar (Logout/Expire) |
| REQ-AUTH-03 | Auth auch für Web-Query-Endpoints |
3.7 Stops / Vorschläge
| ID | Anforderung |
|---|---|
| REQ-SUG-01 | Stop-Erkennung (Mindestdauer, Radius konfigurierbar) |
| REQ-SUG-02 | Vorschläge aus Stops ableiten |
| REQ-SUG-03 | Pro Nutzer/Device/Trip zuordenbar; in Website sichtbar |
3.8 Geocoding
| ID | Anforderung |
|---|---|
| REQ-GEO-01 | Kostenloser Dienst (Nominatim); Caching + Rate-Limit |
| REQ-GEO-02 | Provider austauschbar via Config (ohne App-Update) |
| REQ-GEO-03 | Kein Bulk/periodisches Geocoding; ereignisbasiert (pro Stop) |
3.9 Karten
| ID | Anforderung |
|---|---|
| REQ-MAP-01 | OpenStreetMap-Basis; MapLibre als Renderer |
| REQ-MAP-02 | Tile-Quelle konfigurierbar |
3.10 OpenAPI
| ID | Anforderung |
|---|---|
| REQ-OPENAPI-01 | API als OpenAPI 3.1 dokumentiert |
| REQ-OPENAPI-02 | openapi.yaml im Repository versioniert |
| REQ-OPENAPI-03 | Alle Endpoints, Schemas, Fehler, Security-Schemes |
| REQ-OPENAPI-04 | CookieAuth via type: apiKey, in: cookie |
3.11 Google Play / Datenschutz
| ID | Anforderung |
|---|---|
| REQ-PRIV-01 | Background-Location Disclosure im UI; korrekte Deklaration |
3.13 Hashtags
| ID | Anforderung |
|---|---|
| REQ-TAG-01 | Trackpoints mit Hashtags versehen (#restaurant, ...) |
| REQ-TAG-02 | Tags lokal in Room + beim Upload übertragen (tags: string[]) |
| REQ-TAG-03 | Server persistiert Tags, abfragbar pro Trackpoint |
| REQ-TAG-04 | Webapp: Filter nach Hashtag |
| REQ-TAG-05 | Eingabe-Vorschläge aus vorhandenen Tags des Nutzers |
| REQ-TAG-06 | Webapp: Tag-Übersicht mit Häufigkeit |
10. Architekturentscheidungen
| ID | Entscheidung | Alternativen | Status |
|---|---|---|---|
| DEC-CLIENT-01 | Android (Kotlin + Compose) | iOS, Flutter | Entschieden |
| DEC-LOC-01 | Background Location Logging | Nur Vordergrund | Entschieden |
| DEC-API-01 | HTTP/REST + JSON | MQTT, gRPC, Protobuf | Entschieden (Protobuf offen) |
| DEC-DB-01 | Room (Android) + PostgreSQL (Server) | SQLite Server, MySQL | Entschieden |
| DEC-WEB-01 | Website mit Login (SSR + SPA) | nur API | Entschieden |
| DEC-AUTH-01 | Session Cookie für Web; JWT optional | JWT only | Session entschieden |
| DEC-MAP-01 | MapLibre + OSM, konfigurierbare Tiles | Google Maps | Entschieden |
| DEC-GEO-01 | Nominatim, Provider austauschbar | Google Places API | Entschieden |
| DEC-OPENAPI-01 | OpenAPI 3.1 YAML | kein Spec | Entschieden |
| DEC-SCHEMA-01 | go:embed schema.sql, IF NOT EXISTS |
golang-migrate | Entschieden |
11. Risiken und offene Punkte
Implementierungsstatus
flowchart TD
subgraph Impl[Implementiert]
A[REST API Ingest\nPOST /v1/trackpoints]
B[Session Auth\nArgon2id + Cookie]
C[Tagesabfragen\nGET /v1/days etc.]
D[Web UI\nGo Templates SSR]
E[SPA Webapp\nMapLibre + WebComponents]
F[Schema Init\ngo:embed + IF NOT EXISTS]
G[Device-Registrierung\nEnsureDevice]
end
subgraph Backlog[Backlog / TBD]
H[Stop Detection\nT061]
I[Geocoding Adapter\nT063]
J[Suggestions Engine\nT062]
K[Android Auth\nAPI-Key / JWT T050]
L[OpenAPI Spec\nT070]
M[Hashtag-Support\nT073-T087]
end
subgraph Risiko[Sicherheit / Production]
N[CSRF-Schutz\nkritisch]
O[TLS / HTTPS\nhoch]
P[Rate-Limiting\nmittel]
Q[Secrets via .env\nkritisch]
R[Pagination\nhoch]
S[Security-Header\nnginx mittel]
end
style Impl fill:#d4edda,stroke:#28a745
style Backlog fill:#fff3cd,stroke:#ffc107
style Risiko fill:#f8d7da,stroke:#dc3545
Security-Risiken (Architekt-Review)
quadrantChart
title Risiken — Schwere vs. Aufwand
x-axis Geringer Aufwand --> Hoher Aufwand
y-axis Geringes Risiko --> Hohes Risiko
quadrant-1 Sofort beheben
quadrant-2 Planen
quadrant-3 Beobachten
quadrant-4 Nice to have
CSRF-Schutz: [0.2, 0.9]
Secrets via .env: [0.1, 0.85]
TLS/HTTPS: [0.5, 0.8]
Rate-Limiting Login: [0.3, 0.75]
Pagination: [0.4, 0.6]
Android Auth: [0.7, 0.65]
Security-Header nginx: [0.15, 0.5]
Error-Disclosure: [0.2, 0.5]
EnsureDevice N+1: [0.35, 0.35]
DB Pool Config: [0.2, 0.4]
Stop Detection: [0.75, 0.5]
Geocoding: [0.8, 0.4]
OpenAPI Spec: [0.5, 0.2]
Hashtags: [0.65, 0.3]
Produktionsreife
flowchart LR
DEV[Development\n✓ Lauffähig]
MVP[MVP\nFehlend: CSRF, TLS\nSecrets, Rate-Limit]
PROD[Production Ready\n+ Stop Detection\n+ Geocoding\n+ Monitoring\n+ Backup]
DEV -->|Kritische Security-Fixes| MVP
MVP -->|Business-Logik\n+ Observability| PROD
style DEV fill:#d4edda,stroke:#28a745
style MVP fill:#fff3cd,stroke:#ffc107
style PROD fill:#cce5ff,stroke:#004085
Offene Entscheidungen
| Thema | Optionen | Auswirkung |
|---|---|---|
timestamp-Format |
epochMillis vs RFC3339 | Android + API Kompatibilität |
| Android Upload Auth | Session Cookie / API-Key / JWT | Security-Architektur |
| Payload | JSON vs Protobuf | Bandbreite auf Mobile |
| Batch-Limits | max Items / max Bytes | Denial-of-Service Schutz |
| Retention Policy | Löschen nach X Tagen | Storage-Kosten |
| Stop-Parameter | Mindestdauer, Radius | Qualität der Vorschläge |
| Geocoding Provider | Nominatim public / self-hosted | Datenschutz, Verfügbarkeit |