refactor(quick-260921-m34): Aufgabe 2b - getypte Anfrage in elf Controllern, zwei Befunde gemeldet
(req as any) und @Req() req: any durch AuthenticatedRequest ersetzt in
dashboard, favorites, calendar, groups, module-grants, module-registry,
tenders, dkv, ldap, settings; @CurrentUser() in user.controller auf AuthUser.
Die abwehrenden Pruefungen ("No tenant context", "No user context") bleiben
lebendig, weil user auf dem Anfragetyp wahlfrei ist - genau das beschreibt
den Zustand auf oeffentlichen Wegen.
Nebengewinn ohne neue Zusicherungen: req.tenantId as string | undefined
(dkv, settings), file.buffer as Buffer und file.mimetype as string
(dkv, user) sind weggefallen, weil der Typ sie jetzt traegt.
BEFUND 1 (D-03, gemeldet) dashboard.controller.ts:74 alt: der Handler las
req.user?.role NACH extractContext und gab sie an getWidgets(role: Role)
weiter, das eine Rolle zwingend verlangt. Die Annahme "hier gibt es immer
einen Aufrufer" stimmt - die Pruefung "No user context" erzwingt sie -, aber
sie stand in einer anderen Methode, wo der Compiler sie nicht sehen konnte.
extractContext gibt die Rolle jetzt mit zurueck: keine neue Pruefung, kein
erfundener Wert, gleiche Reihenfolge, gleiche Meldungen.
BEFUND 2 (D-03, gemeldet) tenders.controller.ts:142: resolveRequestingTenantId
erklaerte string | undefined, liest aber req.tenantId, das TenantGuard fuer
einen SUPER_ADMIN ohne Mandanten auf null setzt. Die Erklaerung war also nie
vollstaendig. Erweitert auf string | null | undefined, und buildTenderWhere
nimmt string | null - beides nur Erklaerung, kein Verhalten: die Funktion
entscheidet seit jeher ueber Wahrheitswert und faellt bei beiden zu
(nur global sichtbare Ausschreibungen).
Fixtures in user.controller.spec.ts ergaenzt (username, mustChangePassword,
originalname, size). Testzahlen unveraendert.
noExplicitAny in apps/api/src: 137 -> 66. type-check 4/4, lint 5/5,
apps/api 72/1143, apps/web 73/531, tenant.guard.ts unveraendert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
Put,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import type { AuthenticatedRequest } from '../auth/types/auth-user';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
import { CreateSearchProviderDto } from './dto/create-search-provider.dto';
|
||||
import { CreateWidgetDto } from './dto/create-widget.dto';
|
||||
@@ -39,42 +39,53 @@ import { UpdateWidgetConfigDto } from './dto/update-widget-config.dto';
|
||||
export class DashboardController {
|
||||
constructor(private readonly dashboardService: DashboardService) {}
|
||||
|
||||
private extractContext(req: Request) {
|
||||
const userId = (req as any).user?.id;
|
||||
/**
|
||||
* BEFUND quick-260921-m34 (D-03, gemeldet nicht repariert): `getWidgets`
|
||||
* las die Rolle vorher als `req.user?.role` NACH dieser Pruefung und gab
|
||||
* sie an `DashboardService.getWidgets(role: Role)` weiter, das eine Rolle
|
||||
* zwingend verlangt. Der Bestandscode nahm also an, dass an dieser Stelle
|
||||
* immer ein Aufrufer vorliegt. Die Annahme stimmt — die Pruefung "No user
|
||||
* context" direkt darunter erzwingt sie seit jeher —, aber der Compiler
|
||||
* konnte die beiden Stellen nicht verbinden, weil sie in zwei Methoden
|
||||
* standen. Deshalb gibt diese Methode die Rolle jetzt MIT zurueck: keine
|
||||
* neue Pruefung, kein erfundener Wert, gleiche Reihenfolge, gleiche
|
||||
* Meldungen, gleiches Verhalten — nur sichtbar statt angenommen.
|
||||
*/
|
||||
private extractContext(req: AuthenticatedRequest) {
|
||||
const user = req.user;
|
||||
const tenantId =
|
||||
(req as any).tenantId ?? (req as any).user?.tenantId;
|
||||
req.tenantId ?? user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
throw new ForbiddenException('No tenant context');
|
||||
}
|
||||
if (!userId) {
|
||||
if (!user?.id) {
|
||||
throw new ForbiddenException('No user context');
|
||||
}
|
||||
|
||||
return { userId, tenantId };
|
||||
return { userId: user.id, tenantId, role: user.role };
|
||||
}
|
||||
|
||||
@Get('layout')
|
||||
async getLayout(@Req() req: Request) {
|
||||
async getLayout(@Req() req: AuthenticatedRequest) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.getLayout(userId, tenantId);
|
||||
}
|
||||
|
||||
@Put('layout')
|
||||
async saveLayout(@Req() req: Request, @Body() dto: SaveLayoutDto) {
|
||||
async saveLayout(@Req() req: AuthenticatedRequest, @Body() dto: SaveLayoutDto) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.saveLayout(userId, tenantId, dto);
|
||||
}
|
||||
|
||||
@Get('widgets')
|
||||
async getWidgets(@Req() req: Request) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
const role = (req as any).user?.role;
|
||||
async getWidgets(@Req() req: AuthenticatedRequest) {
|
||||
const { userId, tenantId, role } = this.extractContext(req);
|
||||
return this.dashboardService.getWidgets(userId, tenantId, role);
|
||||
}
|
||||
|
||||
@Post('widgets')
|
||||
async addWidget(@Req() req: Request, @Body() dto: CreateWidgetDto) {
|
||||
async addWidget(@Req() req: AuthenticatedRequest, @Body() dto: CreateWidgetDto) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.addWidget(userId, tenantId, dto);
|
||||
}
|
||||
@@ -82,7 +93,7 @@ export class DashboardController {
|
||||
@Patch('widgets/:id/config')
|
||||
async updateWidgetConfig(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Body() dto: UpdateWidgetConfigDto,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
@@ -92,7 +103,7 @@ export class DashboardController {
|
||||
@Delete('widgets/:id')
|
||||
async removeWidget(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.removeWidget(id, userId, tenantId);
|
||||
@@ -101,14 +112,14 @@ export class DashboardController {
|
||||
// --- Search Providers (05-02, D-15) ---
|
||||
|
||||
@Get('search-providers')
|
||||
async getSearchProviders(@Req() req: Request) {
|
||||
async getSearchProviders(@Req() req: AuthenticatedRequest) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.getSearchProviders(userId, tenantId);
|
||||
}
|
||||
|
||||
@Post('search-providers')
|
||||
async addSearchProvider(
|
||||
@Req() req: Request,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Body() dto: CreateSearchProviderDto,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
@@ -118,7 +129,7 @@ export class DashboardController {
|
||||
@Delete('search-providers/:id')
|
||||
async removeSearchProvider(
|
||||
@Param('id') id: string,
|
||||
@Req() req: Request,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const { userId, tenantId } = this.extractContext(req);
|
||||
return this.dashboardService.removeSearchProvider(id, userId, tenantId);
|
||||
|
||||
Reference in New Issue
Block a user