import { Expose, Transform } from 'class-transformer'; import { ArrayMaxSize, IsArray, IsIn, IsOptional, IsString, MaxLength, } from 'class-validator'; /** * Rumpf von `POST /bug-reports` (quick-260914-m97, Fehler-melden-Knopf). * * Die Felder kommen als `multipart/form-data` (das Bild liegt als Datei * `screenshot` daneben, siehe Controller) — multer liefert deshalb alle * Textfelder als Strings. Ein EINZELNES wiederholtes Feld `errors` kommt * als String, mehrere als Array, keines als undefined (append-field, * gemessen zur Planungszeit); ohne die Normalisierung unten wuerde * `@IsArray()` bei genau einer Fehlermeldung scheitern. * * Mandant und Benutzer stehen BEWUSST NICHT in diesem DTO (T-M97-06): der * Dienst nimmt beides ausschliesslich aus dem Sitzungsnachweis * (`@CurrentUser()`), und `whitelist: true` der globalen ValidationPipe * entfernt jedes Fremdfeld, das ein Client hier trotzdem mitschickt. * * Vier weitere Felder (`clientKind`, `clientOs`, `clientVersion`, * `clientCommit`) kommen seit quick-260918-gza vom Web-Client dazu — im * Browser als `clientKind=browser` mit leeren uebrigen Feldern, in der * Desktop-App aus dem Cookie `tessera_desktop_client`. Sie sind optional, * damit aeltere Web-Baue weiterhin gueltig senden (der Dienst faellt ohne * sie auf `browser` zurueck); `whitelist: true` verlangt ihre Deklaration * hier, sonst wuerde die Pipe sie stillschweigend entfernen. Sie dienen * ausschliesslich der Anzeige in der Mail und sind laengenbegrenzt * (T-GZA-01) — siehe `origin.ts`. */ export class BugReportDto { /** Freitext „Was ist passiert?“ — optional, hoechstens 4000 Zeichen. */ @IsOptional() @IsString() @MaxLength(4000) description?: string; /** Pfad plus Suchteil der Seite, ohne Host. */ @IsString() @MaxLength(2000) page!: string; @IsString() @MaxLength(100) webVersion!: string; @IsString() @MaxLength(20) webChannel!: string; /** Kurzer Commit-Hash; Leerstring ist erlaubt (lokaler Bau ohne Stempel). */ @IsString() @MaxLength(64) webCommit!: string; @IsString() @MaxLength(1000) userAgent!: string; /** `x` des Browserfensters. */ @IsString() @MaxLength(50) viewport!: string; /** ISO-Zeitstempel des Browsers zum Sendezeitpunkt. */ @IsString() @MaxLength(50) clientTime!: string; /** * Die letzten Fehlermeldungen aus dem Browser-Ringpuffer, je * `[] : `. `@Expose()` sorgt dafuer, dass die * Normalisierung auch laeuft, wenn das Feld im Rumpf GANZ fehlt * (class-transformer ruft `@Transform` sonst nur fuer vorhandene * Schluessel auf — gemessen: ohne `@Expose()` scheitert `@IsArray()`). */ @Expose() @Transform(({ value }) => value === undefined || value === null ? [] : Array.isArray(value) ? value : [value], ) @IsArray() @ArrayMaxSize(30) @IsString({ each: true }) @MaxLength(1000, { each: true }) errors!: string[]; /** `desktop` oder `browser` — vom Web-Client gesetzt, siehe Kopfkommentar. */ @IsOptional() @IsIn(['desktop', 'browser']) clientKind?: 'desktop' | 'browser'; /** Betriebssystem der Desktop-App (`windows`, `linux`, `macos`, …), unbeglaubigt. */ @IsOptional() @IsString() @MaxLength(20) clientOs?: string; /** Version der Desktop-App (`CARGO_PKG_VERSION`), unbeglaubigt. */ @IsOptional() @IsString() @MaxLength(40) clientVersion?: string; /** Commit-Stempel der Desktop-App (`APP_COMMIT`), darf leer sein. */ @IsOptional() @IsString() @MaxLength(40) clientCommit?: string; }