Phase 10c: Cards-Web SSO-Login gegen mana-auth
Some checks are pending
CI / validate (push) Waiting to run
Some checks are pending
CI / validate (push) Waiting to run
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>
This commit is contained in:
parent
a960d09e5b
commit
7119756ce6
6 changed files with 236 additions and 55 deletions
|
|
@ -1,7 +1,12 @@
|
|||
/**
|
||||
* Cards-API-Client. Dünner Fetch-Wrapper, der `X-User-Id`-Header aus
|
||||
* dem Dev-Auth-Stub setzt. Phase 2 ersetzt das durch ein Bearer-Token
|
||||
* aus @mana/shared-auth.
|
||||
* Cards-API-Client. Dünner Fetch-Wrapper.
|
||||
*
|
||||
* Phase 10c: schickt `Authorization: Bearer <jwt>` aus der echten
|
||||
* mana-auth-Session. Wenn kein JWT da ist (Stub-Modus für Tests),
|
||||
* fällt er auf den `X-User-Id`-Header zurück.
|
||||
*
|
||||
* 401 → wir leeren die Session und werfen ApiError. Aufrufer kann
|
||||
* darauf reagieren (z.B. Redirect auf `/`).
|
||||
*/
|
||||
|
||||
import { devUser } from '$lib/auth/dev-stub.svelte.ts';
|
||||
|
|
@ -33,8 +38,10 @@ export async function api<T>(path: string, opts: RequestOptions = {}): Promise<T
|
|||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (devUser.id) {
|
||||
headers['X-User-Id'] = devUser.id;
|
||||
if (devUser.token) {
|
||||
headers['Authorization'] = `Bearer ${devUser.token}`;
|
||||
} else if (devUser.stubId) {
|
||||
headers['X-User-Id'] = devUser.stubId;
|
||||
}
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
method: opts.method ?? 'GET',
|
||||
|
|
@ -43,6 +50,11 @@ export async function api<T>(path: string, opts: RequestOptions = {}): Promise<T
|
|||
signal: opts.signal,
|
||||
});
|
||||
if (!res.ok) {
|
||||
if (res.status === 401 && devUser.token) {
|
||||
// Token vermutlich abgelaufen — Session lokal aufräumen, sodass
|
||||
// die Landing-Page einen Re-Login zeigt.
|
||||
devUser.clear();
|
||||
}
|
||||
let body: unknown = null;
|
||||
try {
|
||||
body = await res.json();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ export async function uploadMedia(file: File | Blob, filename?: string): Promise
|
|||
form.append('file', wrapped);
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
if (devUser.id) headers['X-User-Id'] = devUser.id;
|
||||
if (devUser.token) headers['Authorization'] = `Bearer ${devUser.token}`;
|
||||
else if (devUser.stubId) headers['X-User-Id'] = devUser.stubId;
|
||||
|
||||
const res = await fetch(`${API_BASE}/api/v1/media/upload`, {
|
||||
method: 'POST',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue