feat(260911-gwh): GREEN — favorites/settings binden, Startpfad umbenennen, Widget-Besitzriegel

- favorites.service.ts: alle fuenf Methoden nehmen tenantId als ersten
  Parameter, laufen je ueber EINEN Klienten tenantPrisma (7 gebundene
  Favoritenzugriffe, 5 Aufrufstellen); create() prueft vor der Icon-Suche,
  dass das Ziel-Widget dem Aufrufer gehoert (T-GWH-05, Befund F aus Aufgabe 1
  bestaetigt den Fremdschluessel-Durchgriff) -- Widget not found fuer alle
  drei Faelle (existiert nicht/Kollege/fremder Mandant)
- favorites.controller.ts: reicht tenantId an alle fuenf Aufrufe durch,
  extractContext unveraendert (dashboard-Praezedenzfall)
- settings.service.ts: getSmtpConfig/saveSmtpConfig/getDecryptedSmtpConfig
  je EIN Klient (3 gebundene Zugriffe, 3 Aufrufstellen) -- Befund K damit
  erfuellt; Startpfad umbenannt in loadAnySmtpConfigForStartupTransport(),
  bleibt bewusst ungebunden (sechster Fall der Hintergrunddienst-Falle,
  WINDOWS #TBD-GWH -- Aufgabe 3 vergibt die Nummer)
- mail.module.ts: ruft den umbenannten Startpfad auf, Kommentar nennt beide
  Zustaende statt "single-tenant default"
- Vier Falsifizierungsnachweise durchgefuehrt und zurueckgenommen (siehe
  SUMMARY): (a) 3 Faelle rot, (b) 4 Faelle rot, (c) 9 Faelle rot, (d) 4 Faelle
  rot

Bekannt und erwartet (siehe SUMMARY, Praezedenzfall 260911-fh9): zwischen
dieser Aufgabe und Aufgabe 3 ist rls-access-inventory.spec.ts rot (2
Faelle) -- die Klassifikationstabelle ist noch nicht nachgezogen, das ist
Aufgabe 3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
This commit is contained in:
2026-09-11 13:57:02 +02:00
parent 8f2c13a35f
commit b5f22e2c4a
4 changed files with 149 additions and 31 deletions
+19 -8
View File
@@ -22,6 +22,16 @@ import { FavoritesService } from './favorites.service';
*
* All routes are protected by the global JwtAuthGuard + TenantGuard.
*
* Mandantenquelle (260911-gwh): `extractContext` liest `req.tenantId ??
* req.user?.tenantId` — WORTGLEICH mit `dashboard.controller.ts`, unter
* dessen Bindung `WidgetInstance` liegt. `FavoriteLink` haengt ueber
* `widgetId` an `WidgetInstance`; eine andere Quelle (z. B. das Claim, wie
* bei `auth` fuer Selbstbedienung) wuerde Widget und Link unter einem
* `x-tenant-id`-Wechsel eines SUPER_ADMIN in verschiedenen Mandanten
* auseinanderreissen. Das Favoriten-Frontend sendet die `x-tenant-id`-
* Kopfzeile heute nicht — die Entscheidung haengt an der Bauform, nicht am
* heutigen Aufrufer.
*
* Routes:
* - GET /favorites?widgetId= — list favorites for a widget instance
* - POST /favorites — create a favorite (triggers server-side icon discovery)
@@ -52,9 +62,9 @@ export class FavoritesController {
@Query('widgetId', ParseUUIDPipe) widgetId: string,
@Req() req: Request,
) {
const { userId } = this.extractContext(req);
const { userId, tenantId } = this.extractContext(req);
return this.favoritesService.list(userId, widgetId);
return this.favoritesService.list(tenantId, userId, widgetId);
}
@Post()
@@ -64,7 +74,7 @@ export class FavoritesController {
) {
const { userId, tenantId } = this.extractContext(req);
return this.favoritesService.create(userId, tenantId, dto);
return this.favoritesService.create(tenantId, userId, dto);
}
/**
@@ -82,8 +92,9 @@ export class FavoritesController {
@Req() req: Request,
@Res() res: Response,
) {
const { userId } = this.extractContext(req);
const { userId, tenantId } = this.extractContext(req);
const { contentType, body } = await this.favoritesService.getIconBytes(
tenantId,
id,
userId,
);
@@ -99,9 +110,9 @@ export class FavoritesController {
@Body() dto: UpdateFavoriteDto,
@Req() req: Request,
) {
const { userId } = this.extractContext(req);
const { userId, tenantId } = this.extractContext(req);
return this.favoritesService.update(id, userId, dto);
return this.favoritesService.update(tenantId, id, userId, dto);
}
@Delete(':id')
@@ -109,8 +120,8 @@ export class FavoritesController {
@Param('id') id: string,
@Req() req: Request,
) {
const { userId } = this.extractContext(req);
const { userId, tenantId } = this.extractContext(req);
return this.favoritesService.remove(id, userId);
return this.favoritesService.remove(tenantId, id, userId);
}
}