feat(web): Herkunft der Fehlermeldung — Cookie tessera_desktop_client und Client-Felder in der Nutzlast
Die Middleware liest jetzt zusaetzlich dv/dc/dos aus der Anfrage und legt daraus das Cookie tessera_desktop_client an (bereinigt per Muster, nur wenn alle drei Werte gueltig sind); desktop-client.ts liest es zurueck. Der Fehler-melden-Dialog fuellt daraus vier neue Nutzlastfelder (clientKind/clientOs/clientVersion/clientCommit), damit die API die Herkunft der Meldung ausweisen kann. Ohne das zweite Cookie (alter Client) bleibt es bei "Desktop-App (unbekannt)". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh
This commit is contained in:
@@ -9,6 +9,58 @@ import { useEffect, useState } from 'react';
|
||||
*/
|
||||
export const DESKTOP_COOKIE_NAME = 'tessera_desktop';
|
||||
|
||||
/**
|
||||
* Gegenstueck zu `buildDesktopClientCookieValue()` in der Middleware
|
||||
* (quick-260918-gza): dort werden `dv`/`dc`/`dos` mit `${dv}|${dc}|${dos}`
|
||||
* zusammengesetzt und von Next per `encodeURIComponent` serialisiert --
|
||||
* `|` wird `%7C` (gemessen). `document.cookie` liefert deshalb die
|
||||
* KODIERTE Form; `parseDesktopClientCookie()` dekodiert (in try/catch,
|
||||
* falls der Wert bereits roh vorliegt, z. B. direkt gesetzt im Test).
|
||||
*/
|
||||
export const DESKTOP_CLIENT_COOKIE_NAME = 'tessera_desktop_client';
|
||||
|
||||
export interface DesktopClientInfo {
|
||||
version: string;
|
||||
commit: string;
|
||||
os: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parst den Wert des Cookies `tessera_desktop_client` aus einem rohen
|
||||
* `document.cookie`-String. `null` bei fehlendem Cookie oder einer Form,
|
||||
* die nicht genau drei durch `|` getrennte Teile mit nicht-leerer Version
|
||||
* und nicht-leerem Betriebssystem ergibt (Commit darf leer sein).
|
||||
*/
|
||||
export function parseDesktopClientCookie(cookieString: string): DesktopClientInfo | null {
|
||||
const entry = cookieString
|
||||
.split(';')
|
||||
.map((c) => c.trim())
|
||||
.find((c) => c.startsWith(`${DESKTOP_CLIENT_COOKIE_NAME}=`));
|
||||
if (!entry) return null;
|
||||
|
||||
const raw = entry.slice(DESKTOP_CLIENT_COOKIE_NAME.length + 1);
|
||||
let value = raw;
|
||||
try {
|
||||
value = decodeURIComponent(raw);
|
||||
} catch {
|
||||
// Rohwert (z. B. bereits dekodiert im Test) -- unveraendert weiterverwenden.
|
||||
}
|
||||
|
||||
const parts = value.split('|');
|
||||
if (parts.length !== 3) return null;
|
||||
const [version, commit, os] = parts;
|
||||
if (!version || !os) return null;
|
||||
return { version, commit, os };
|
||||
}
|
||||
|
||||
/** `null` ohne `document` (SSR) oder ohne gueltiges Cookie. */
|
||||
export function getDesktopClientInfo(): DesktopClientInfo | null {
|
||||
if (typeof document === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return parseDesktopClientCookie(document.cookie);
|
||||
}
|
||||
|
||||
export function isDesktopClient(): boolean {
|
||||
if (typeof document === 'undefined') {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user