docs: AirCard-Implementierungsplan + Spec-Korrekturen aus Plan-Review

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-04 07:12:40 +00:00
1 parent 0cd12c1ed3
commit ba93d8f8c5
2 files changed
+415 -9

No files matched your search

@@ -48,7 +48,7 @@ Gitea knso/AirCardIOS ── Push main ── Webhook ──► aircard-build: g
**Warum `/v1` über die eigene Domain:** Appwrite setzt das Session-Cookie auf die Domain des Requests (`'.' + Host`).
Läuft `/v1` über `aircard-ios.org`, ist das Cookie first-party (`.aircard-ios.org`, Secure, HttpOnly),
Safari blockiert es nicht, und das SDK braucht keinen localStorage-Fallback. Geprüft im Quellcode von Appwrite 1.8.1 und per Probe-Request mit Host `aircard-ios.org` (200 auf `/v1/health/version`).
Der Header-Matcher `X-Appwrite-Project: aircard` sorgt dafür, dass über diese Domain nur das AirCard-Projekt erreichbar ist (keine Console, kein Portal). Das Web-SDK sendet den Header bei jedem Aufruf.
Der Header-Matcher `X-Appwrite-Project: aircard` schaltet die Route nur für SDK-Aufrufe frei (das Web-SDK sendet den Header bei jedem Aufruf). Er isoliert **nicht**: Appwrite wertet `?project=` vor dem Header aus – wie unter appwrite.webklar.com ohnehin öffentlich. Dateien werden deshalb nie über `getFileView`/`getFilePreview`-URLs (Projekt als Query, ohne Header) geladen, sondern per `fetch` mit Header. Alle anderen `/v1`-Pfade beantwortet nginx mit 404, damit ein Routingfehler nicht als Seite durchgeht.
**Fallback**, falls die Route im Live-Test scheitert: Endpoint `https://appwrite.webklar.com/v1` wie bei eship (Web-Plattform `aircard-ios.org` eintragen, SDK nutzt dann den Cookie-Fallback).
@@ -99,7 +99,7 @@ Der Header-Matcher `X-Appwrite-Project: aircard` sorgt dafür, dass über diese
Unten links (x 0–620, y 779–969) bleibt frei, weil Wallet dort die letzten vier Ziffern einblendet.
*Nur Bild* zeichnet nur das Hintergrundbild.
Details **Hell** = Chip-Linien, Nummer, PLATINUM, Kontaktlos-Symbol und einfarbige Logos in Weiß (#FFFFFF); **Dunkel** = in #111111. Der Chip selbst ist eine helle bzw. dunkle Metallfläche (Verlauf).
Details **Hell** = Nummer, PLATINUM, Kontaktlos-Symbol und einfarbige Logos in Weiß (#FFFFFF); **Dunkel** = in #111111. Der Chip ist eine helle bzw. dunkle Metallfläche (Verlauf) mit Kontaktlinien in Dunkelgrau (#7A7A7A) bzw. fast Schwarz (#0A0A0A), unabhängig von der Details-Farbe.
### Logo-Varianten (Kartennetzwerk, 18 Stück)
@@ -109,10 +109,10 @@ Details **Hell** = Chip-Linien, Nummer, PLATINUM, Kontaktlos-Symbol und einfarbi
| Mastercard | Farbe (Symbol), Metallisches Silber (Symbol als Verlauf), Wortmarke (Symbol + „mastercard“ darunter), Silber + Text, Klassisch (altes Logo mit „MasterCard“ in den Kreisen) |
| UnionPay | Farbe, Metallisches Silber, Weißer Rahmen (Farblogo mit weißer Kontur), Englisch (ohne chinesische Zeichen) |
| American Express | Blau (Blue Box), Umriss (weiß/transparent), Select (Blue Box, kleiner) |
| JCB | Farbe |
| Discover | Wortmarke |
| JCB | JCB |
| Discover | Discover |
Dazu bei *Logo*: AirCards Symbol, AirCards Text, Apple, Kein Logo.
Dazu bei *Logo*: AirCards · Symbol, AirCards · Text, Apple, Kein Logo (Wortlaut wie im Screenshot).
Das AirCards-Symbol und der AirCards-Schriftzug werden nach dem Screenshot neu gezeichnet (eigenes SVG).
Quellen: bereinigte SVGs aus `/root/reports/aircard-marks-research/` (Wikimedia Commons bzw. offizielle Dateien, mit svgo bereinigt).
@@ -170,14 +170,14 @@ Dateien unter `/home/webklar/apps/aircard-ios.org/`:
|---|---|
| `build/` | Klon des Repos (Arbeitsordner des Build-Containers) |
| `out/` | gebaute Seite, von nginx ausgeliefert |
| `deploy.sh` | aus ticket abgeleitet: Repo `knso/AirCardIOS`, Branch `main`, Unterordner `web/`, `npm ci`, `vite build`, `dist/` → `out/` |
| `deploy.sh` | nach dem Vorbild ticket: Repo `knso/AirCardIOS`, Branch `main`, Unterordner `web/`, `npm ci`, `vite build`, `dist/` → `out/`; merkt sich den zuletzt **versuchten** Commit, damit ein kaputter Build nicht alle 15 Minuten wiederholt wird |
| `webhook-server.js` | Kopie von ticket mit eigenen Variablen, Port 8087, **schlägt fehl, wenn das Secret fehlt** (statt durchzulassen) |
| `nginx/aircard-ios.org.conf` | SPA-Fallback auf `index.html`, `^~ /assets/` 1 Jahr Cache, `index.html` ohne Cache, Sicherheits-Header, gzip |
| `nginx/aircard-ios.org.conf` | SPA-Fallback auf `index.html`, `^~ /assets/` 1 Jahr Cache, `index.html` ohne Cache, `^~ /v1/` → 404, Sicherheits-Header, gzip |
| `dev/` | Arbeitskopie für die Entwicklung (nicht gemountet) |
Compose (`/home/webklar/apps/docker-compose.yml`, vorher Backup `docker-compose.yml.bak-2026-10-04-aircard`):
- `aircard-build`: `node:20-alpine`, `init: true`, `mem_limit: 768m`, `NODE_OPTIONS=--max-old-space-size=512`, Build-Variablen `VITE_APPWRITE_ENDPOINT=https://aircard-ios.org/v1` und `VITE_APPWRITE_PROJECT_ID=aircard`, Webhook-Router `Host(aircard-ios.org) && Path(/webhook/deploy)` mit Priorität 200.
- `aircard-build`: `node:22-alpine`, `init: true`, `mem_limit: 768m`, `NODE_OPTIONS=--max-old-space-size=512`, Build-Variablen `VITE_APPWRITE_ENDPOINT=https://aircard-ios.org/v1` und `VITE_APPWRITE_PROJECT_ID=aircard`, Webhook-Router `Host(aircard-ios.org) && Path(/webhook/deploy)` mit Priorität 200.
- `aircard`: `nginx:alpine`, `mem_limit: 64m`, Router `Host(aircard-ios.org)` (websecure, Let's Encrypt) + HTTP→HTTPS-Weiterleitung.
- Appwrite-Route als Datei `/home/webklar/apps/traefik/dynamic/aircard.yml` (Hot-Reload, kein Eingriff am Appwrite-Container): `Host(aircard-ios.org) && PathPrefix(/v1) && Header(X-Appwrite-Project, aircard)` → `appwrite@docker`, Priorität 150.
- Gestartet wird **nur** `docker compose up -d aircard-build aircard` (ein nacktes `up -d` würde eship-build und lernen-build wieder anlegen).
@@ -208,7 +208,7 @@ Last auf dem Server: nginx ca. 7 MB, Build-Container im Leerlauf 20–50 MB, bei
- **Unit-Tests (Vitest)** für die reinen Module: `geometry` (alle Boxen im Canvas, Netzwerk-Logo in der Box rechts unten, nichts im Wallet-Freibereich), `crop` (Cover-Fit für Hoch-/Querformat, Zoom-Grenzen, Verschieben bleibt in der Fläche), `config` (Rundlauf serialize/parse, kaputtes JSON → Standard, unbekannte Variante → Standard), Dateiprüfung (Typ/Größe), `marks` (alle 18 Netzwerk- und 4 Logo-Varianten registriert, jede SVG-Datei vorhanden und ladbar).
- **Setup-Skript**: `--check` gegen das echte Projekt muss „alles vorhanden“ melden.
- **Abnahme nach Deploy** (im Browser über Claude in Chrome):
- `curl -H 'X-Appwrite-Project: aircard' https://aircard-ios.org/v1/health/version` → `1.8.1`; ohne Header → Seite, nicht Appwrite.
- `curl -H 'X-Appwrite-Project: aircard' https://aircard-ios.org/v1/health/version` → `1.8.1`; ohne Header → 404 von nginx.
- HTTP → 301 HTTPS, Deep-Link `/karten` → 200, unsignierter POST auf `/webhook/deploy` → 401.
- Bild hochladen, jede Vorlage, Stichprobe Netzwerk-Varianten, Hell/Dunkel, Download → PNG 1536 × 969.
- Registrieren, Speichern, Neuladen, Karte öffnen, Umbenennen, Löschen. Cookie `a_session_aircard` mit Domain `.aircard-ios.org`, kein `cookieFallback` im localStorage.