Rebrand-Rest aus dem Cards-Cutover: client.ts las noch den alten
PUBLIC_CARDS_API_URL, die Production-Compose exportiert aber
PUBLIC_WORDECK_API_URL. Folge: das Web-Bundle fiel auf den
Dev-Default localhost:3081 zurück und alle API-Calls liefen ins
Leere (Deck-Liste, Marketplace).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
res.json() konsumiert den Body-Stream auch bei SyntaxError, danach
schlägt res.text() mit 'body stream already read' fehl. Fix: text()
einmalig lesen, dann JSON.parse() versuchen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neuer Endpoint POST /api/v1/decks/from-image akzeptiert bis zu 5 Bilder
(PNG/JPG/WebP, max 10 MiB je) oder PDFs (max 30 MiB je) als multipart/form-data.
Alle Dateien werden in einem einzigen mana-llm Vision-Call verarbeitet
(mana/vision → llava → Gemini 2.5-flash → GPT-4o Fallback-Chain).
PDFs werden von Gemini nativ verstanden (Layout, Tabellen, Bilder im Dokument)
ohne Zwischenschritt über Text-Extraktion oder Rendering. Der google.py-Provider
reicht den MIME-Type aus dem data:-URI direkt an types.Part.from_bytes() weiter.
- llm-client: chatVisionJson() mit images[]-Array (mehrere Bilder/Dokumente)
- decks-generate: GeneratedDeckSchema + insertGeneratedDeck() exportiert
- decks-from-image: neuer Route-Handler, MIME-Filter für image/* + application/pdf
- index: neue Route gemountet
- client.ts: apiForm() für multipart-Uploads ohne JSON.stringify
- decks.ts: generateDeckFromImage(files, opts)
- NewDeckCard + /decks/new: Dropzone mit Multi-File, Thumbnail-Strip, PDF-Icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug: Browser-Client requested localhost:3081 statt cardecky-api.mana.how
nach Login. Ursache: API_BASE und authBaseUrl() lasen die Variable
über import.meta.env.PUBLIC_*, was unter SvelteKit nicht zuverlässig
inlined wird (Vite-direct, ohne SvelteKit-Wrapper-Hook).
Fix: \$env/dynamic/public liest die env zur Runtime aus den Node-
Server-Variablen (adapter-node) — Browser bekommt sie über den
SSR-Init-Snapshot. Damit muss die Variable nur als runtime-env am
Container hängen, nicht als Build-Arg.
docker-compose.production.yml: PUBLIC_CARDS_API_URL und
PUBLIC_MANA_AUTH_URL aus build.args nach environment verschoben.
Build-Pipeline: cards-web muss neu gebaut werden, sonst greift der
Wechsel von static→dynamic env nicht.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cards-Web läuft jetzt auch über die 15min-JWT-TTL hinaus weiter:
- Login schickt credentials:'include' → SSO-Cookie auf .mana.how
wird gesetzt (mana-auth-Standard).
- tryRefresh() ruft mana-auth POST /api/v1/auth/refresh mit Cookie-
Auth, holt frischen accessToken, legt ihn in localStorage. Multi-
Caller werden zu einer Promise gecoalesced (kein Refresh-Storm).
- ensureFreshToken() prüft beim Konstruktor + vor jedem API-Request,
ob der Token noch ≥60s gültig ist. Wenn nicht, proaktiver Refresh.
- API-Client: 401 → tryRefresh() → exactly-once Retry. Erst wenn das
auch 401 gibt, wird die Session lokal geleert (führt User zurück
auf /).
- uploadMedia (multipart) parallel mit demselben Pattern.
- Logout ruft mana-auth /logout mit Cookie-Auth, damit das SSO-Cookie
für andere *.mana.how-Apps auch weg ist (best-effort, nicht-fatal).
- Boot-Pfad: bei abgelaufenem Token im localStorage behalten wir das
User-Profil temporär und versuchen einen still-Refresh — User sieht
beim Reload kein Login-Flackern, wenn die Cookie-Session noch lebt.
svelte-check 384 files 0 errors, 7 Web-Tests grün, prod-Build sauber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Echte Anmeldung gegen auth.mana.how/api/v1/auth/login statt
Dev-Stub-User-ID. accessToken (EdDSA-JWT, 15 min TTL) + Profil
(email, name, tier) leben in localStorage; jeder API-Call schickt
`Authorization: Bearer <jwt>`. Bei 401 wird die Session lokal
geleert — User landet beim nächsten Render auf der Login-Page.
`devUser.id` bleibt eine Vereinfachte UI-Sentinel (gibt id wenn
JWT ODER Dev-Stub aktiv) — alle existierenden Importer
funktionieren unverändert. Dev-Stub-Pfad bleibt als Fallback für
Tests + Anki-Importer-Migration. Filename `dev-stub.svelte.ts`
behalten, Inhalt komplett umgebaut (Sprint 10d wäre der Rename).
Account-Page zeigt Email + Name + Tier statt nur UUID. Header
zeigt Email statt UUID. Login-Form auf Landing-Page mit Email +
Passwort, error-Anzeige, autocomplete-Hints für Browser-Manager.
uploadMedia (multipart) angepasst: Bearer first, X-User-Id-Stub
als Fallback.
svelte-check 384 files 0 errors, 7 Web-Tests grün, prod-Build
sauber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>