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:
@@ -14,6 +14,46 @@ const publicRoutes = ['/login', '/reset-password'];
|
||||
|
||||
const DESKTOP_COOKIE = 'tessera_desktop';
|
||||
|
||||
/**
|
||||
* Zweites Cookie (quick-260918-gza): traegt Version, Commit-Stempel und
|
||||
* Betriebssystem der Desktop-App, aus denen der Fehler-melden-Knopf die
|
||||
* Herkunft einer Meldung fuellt (Betreff-Kuerzel `[Desktop/Windows]` bzw.
|
||||
* `[Desktop/Linux]` statt `[Browser]` -- WebView2 und WebKitGTK sehen im
|
||||
* User-Agent sonst wie ein gewoehnlicher Browser aus). `httpOnly: false`
|
||||
* ist Absicht: `getDesktopClientInfo()` (apps/web/src/lib/desktop-client.ts)
|
||||
* liest es aus Seiten-JavaScript, Version/Betriebssystem sind kein Geheimnis
|
||||
* -- dieselbe Vertrauensstufe wie der User-Agent, den jede Seite ohnehin
|
||||
* liest. Next.js serialisiert den Wert mit `encodeURIComponent`
|
||||
* (`|` wird `%7C`, gemessen zur Planungszeit).
|
||||
*/
|
||||
const DESKTOP_CLIENT_COOKIE = 'tessera_desktop_client';
|
||||
|
||||
/** Nur `[A-Za-z0-9.+_-]`, hoechstens 40 Zeichen, mindestens 1 Zeichen. */
|
||||
const DESKTOP_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9.+_-]{0,39}$/;
|
||||
/** Leer erlaubt (kein Commit-Stempel im lokalen Bau), sonst hoechstens 40 Hex-/Alnum-Zeichen. */
|
||||
const DESKTOP_COMMIT_RE = /^[A-Za-z0-9]{0,40}$/;
|
||||
/** `std::env::consts::OS`-Werte sind kleingeschrieben ASCII, hoechstens 20 Zeichen. */
|
||||
const DESKTOP_OS_RE = /^[a-z]{1,20}$/;
|
||||
|
||||
/**
|
||||
* Baut den Wert fuer `tessera_desktop_client` aus den Query-Parametern
|
||||
* `dv`/`dc`/`dos` (T-GZA-03: feste Muster, Gesamtlaenge begrenzt). Fehlt
|
||||
* einer der drei Parameter oder passt er nicht auf sein Muster, liefert
|
||||
* die Funktion `null` -- die Middleware setzt das Cookie dann NICHT (ein
|
||||
* alter Desktop-Client ohne diese Parameter bleibt ueber `tessera_desktop`
|
||||
* als Desktop-App erkennbar, nur ohne Details).
|
||||
*/
|
||||
function buildDesktopClientCookieValue(params: URLSearchParams): string | null {
|
||||
const dv = params.get('dv');
|
||||
const dc = params.get('dc');
|
||||
const dos = params.get('dos');
|
||||
if (dv === null || dc === null || dos === null) return null;
|
||||
if (!DESKTOP_VERSION_RE.test(dv) || !DESKTOP_COMMIT_RE.test(dc) || !DESKTOP_OS_RE.test(dos)) {
|
||||
return null;
|
||||
}
|
||||
return `${dv}|${dc}|${dos}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt das Cookie `tessera_desktop`, wenn die Anfrage `?desktop=1` traegt
|
||||
* (260917-h2s). Der Desktop-Client (apps/desktop/src-tauri/src/lib.rs,
|
||||
@@ -22,17 +62,25 @@ const DESKTOP_COOKIE = 'tessera_desktop';
|
||||
* Fruehausstieg fuer oeffentliche Routen und auf Redirects -- sonst geht die
|
||||
* Kennung beim 307 nach /login verloren. `httpOnly: false` ist Absicht (wird
|
||||
* von `isDesktopClient()` in apps/web/src/lib/desktop-client.ts gelesen);
|
||||
* der Wert ist kein Geheimnis.
|
||||
* der Wert ist kein Geheimnis. Seit quick-260918-gza setzt dieselbe Funktion
|
||||
* zusaetzlich das Cookie `tessera_desktop_client` (Version/Commit/OS), sofern
|
||||
* die Anfrage gueltige Werte fuer `dv`/`dc`/`dos` mitbringt -- ohne sie wird
|
||||
* das zweite Cookie weder gesetzt noch geloescht.
|
||||
*/
|
||||
function withDesktopCookie(req: NextRequest, res: NextResponse): NextResponse {
|
||||
if (req.nextUrl.searchParams.get('desktop') === '1') {
|
||||
res.cookies.set(DESKTOP_COOKIE, '1', {
|
||||
const cookieOptions = {
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
sameSite: 'lax',
|
||||
sameSite: 'lax' as const,
|
||||
httpOnly: false,
|
||||
secure: req.nextUrl.protocol === 'https:',
|
||||
});
|
||||
};
|
||||
res.cookies.set(DESKTOP_COOKIE, '1', cookieOptions);
|
||||
const info = buildDesktopClientCookieValue(req.nextUrl.searchParams);
|
||||
if (info !== null) {
|
||||
res.cookies.set(DESKTOP_CLIENT_COOKIE, info, cookieOptions);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user