feat(api): Favoriten — Symbol trotz Zertifikatsfehler holen, Reihenfolge per PUT /favorites/order speichern
- icon-discovery.service.ts: undicis eigenes fetch mit Modul-Singleton
LENIENT_TLS_AGENT (Agent({ connect: { rejectUnauthorized: false } }))
als dispatcher in fetchWithRedirectGuard, der einzigen Ausgangsstelle
fuer HTML-Ermittlung und Icon-Byte-Holen; SSRF-Schutz unveraendert
- undici 7.28.0 (bereits im Lockfile aufgeloest) als direkte Abhaengigkeit
von @tessera/api via pnpm add --offline
- PUT /favorites/order (ReorderFavoritesDto) vor den :id-Routen;
FavoritesService.reorder() setzt position=index fuer die Favoriten
eines Widgets in EINER withTenantTransaction, userId+widgetId in jeder
Bedingung (zweites Netz), eine BadRequestException fuer alle
Abweichungen (T-JDD-06)
- getIcon: X-Content-Type-Options nosniff + restriktive CSP (T-JDD-02)
- 10 neue Tests (3 Dispatcher, 7 reorder); volle API-Suite 68 Dateien/
1101 Tests und type-check gruen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,12 +8,14 @@ import {
|
||||
ParseUUIDPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
Req,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { CreateFavoriteDto } from './dto/create-favorite.dto';
|
||||
import { ReorderFavoritesDto } from './dto/reorder-favorites.dto';
|
||||
import { UpdateFavoriteDto } from './dto/update-favorite.dto';
|
||||
import { FavoritesService } from './favorites.service';
|
||||
|
||||
@@ -35,6 +37,8 @@ import { FavoritesService } from './favorites.service';
|
||||
* Routes:
|
||||
* - GET /favorites?widgetId= — list favorites for a widget instance
|
||||
* - POST /favorites — create a favorite (triggers server-side icon discovery)
|
||||
* - PUT /favorites/order — reorder favorites for a widget instance (260917-jdd)
|
||||
* - GET /favorites/:id/icon — stream a favorite's stored icon bytes
|
||||
* - PATCH /favorites/:id — update a favorite (ownership verified in service)
|
||||
* - DELETE /favorites/:id — delete a favorite (ownership verified in service)
|
||||
*/
|
||||
@@ -77,6 +81,23 @@ export class FavoritesController {
|
||||
return this.favoritesService.create(tenantId, userId, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /favorites/order — persists the display order for a widget's
|
||||
* favorites (260917-jdd). Declared BEFORE the `:id` routes below on
|
||||
* purpose (NestJS route order — a later `:id` route would otherwise
|
||||
* shadow the literal segment "order"; precedent tenders.controller.ts
|
||||
* Z. 636-648).
|
||||
*/
|
||||
@Put('order')
|
||||
async reorder(
|
||||
@Body() dto: ReorderFavoritesDto,
|
||||
@Req() req: Request,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
|
||||
return this.favoritesService.reorder(tenantId, userId, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /favorites/:id/icon — streams the stored icon bytes for a favorite
|
||||
* owned by the caller, from Tessera's own origin. This avoids the browser
|
||||
@@ -101,6 +122,12 @@ export class FavoritesController {
|
||||
|
||||
res.setHeader('Content-Type', contentType);
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||
// 260917-jdd: die Bytes kommen jetzt auch von Hosts ohne gueltiges
|
||||
// Zertifikat. Als <img>-Unterressource ignoriert der Browser diese
|
||||
// Header, aber ein direkt im Tab geoeffnetes SVG laeuft damit ohne
|
||||
// Skript und ohne Tessera-Origin (T-JDD-02).
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||
res.setHeader('Content-Security-Policy', "default-src 'none'; sandbox");
|
||||
res.send(body);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user