feat(11-05): GET/PUT /modules/tender-radar/triage routes + favOnly filter
Adds the batch-triage read/write routes (declared before @Get(':id') per
the route-order pitfall, T-11-13) and wires them through
TenderTriageService with userId/tenantId always derived from the request
context, never the body (T-11-10 / V4 IDOR). Extends TenderQueryDto/
buildTenderWhere with favOnly (UI-04): the controller resolves the
current user's favorited tenderIds server-side before building the
where-clause, and an empty favorites list yields zero matches instead of
the unfiltered catalog. Both batch-ids and favIds in-lists are bounded
(T-11-11 DoS). tenders.controller.spec.ts constructor calls updated for
the new TenderTriageService dependency (Rule 3 — required to keep the
existing suite compiling/passing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,9 +24,6 @@ import {
|
||||
* SORT_MAP, never from a raw user-supplied field name.
|
||||
*
|
||||
* Used for: GET /modules/tender-radar?page=1&limit=20&status=active&q=...
|
||||
*
|
||||
* favOnly filter is added in a later Phase-11 plan (11-05) — deliberately
|
||||
* NOT added here.
|
||||
*/
|
||||
export class TenderQueryDto {
|
||||
/**
|
||||
@@ -166,4 +163,17 @@ export class TenderQueryDto {
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
cpv?: string[];
|
||||
|
||||
/**
|
||||
* Merklisten-Filter (UI-04, D-10, T-11-10/11). When true, the controller
|
||||
* resolves the current user's favorited tenderIds via
|
||||
* `TenderTriageService.favoriteIds(userId)` — derived from the auth
|
||||
* context, never from this DTO — and passes them into
|
||||
* `buildTenderWhere(dto, favIds)`. userId itself never appears here
|
||||
* (V4 / IDOR).
|
||||
*/
|
||||
@IsOptional()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
favOnly?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsBoolean, IsOptional, IsUUID } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Body DTO for PUT /modules/tender-radar/triage.
|
||||
*
|
||||
* Security (T-11-10 / V4 — IDOR): this DTO deliberately has NO userId or
|
||||
* tenantId field — both are always derived server-side from the auth
|
||||
* context (@CurrentUser-equivalent `req.user`/`req.tenantId`, same
|
||||
* FavoritesController.extractContext pattern) in TendersController, never
|
||||
* trusted from the request body.
|
||||
*/
|
||||
export class TenderTriageDto {
|
||||
/** Target tender's id — Tender.id is a `@default(uuid())` string. */
|
||||
@IsUUID()
|
||||
tenderId!: string;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
isRead?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
isFavorite?: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user