refactor(quick-260921-m34): Aufgabe 2a - gemeinsamer Aufrufer-Typ, aus den Signierstellen abgeleitet

apps/api/src/auth/types/auth-user.ts angelegt: AuthUser, AuthenticatedRequest,
LocalAuthenticatedRequest, LoginUser, JwtPayload, UploadedFileLike. Jedes Feld
traegt seine Herkunft als Kommentar.

tenantId ist string, hergeleitet und nicht gewaehlt: die Spalte User.tenantId
ist in schema.prisma Pflicht, beide Signierstellen schreiben genau sie, und
der Bestand beschreibt dasselbe Objekt in SessionUser schon so. Der
SUPER_ADMIN-Zweig in TenantGuard spricht nicht dagegen - der Waechter liest
AuthUser gar nicht, und dass es den Zweig gibt, steht als null in
AuthenticatedRequest.tenantId weiter im Typsystem. tenant.guard.ts bleibt
unberuehrt.

role ist die Aufzaehlung Role: schema.prisma deklariert die Spalte so, die
SQL-Funktion auth_lookup_user_by_username gibt sie als "Role" zurueck. Die
Handannotation role: string in AuthLookupUserByUsernameRow war eine zweite
Fassung desselben Wertes und faellt damit weg.

SessionUser und UploadedPng in bug-reports.service.ts sind jetzt Pick<> der
neuen Typen statt eigener Beschreibungen.

Fixtures in auth.controller.spec.ts ergaenzt: sie uebergaben einen Aufrufer
ohne username und ohne mustChangePassword - eine Form, die JwtStrategy nie
erzeugt. Testzahlen unveraendert.

noExplicitAny in apps/api/src: 149 -> 137. type-check 4/4, lint 5/5,
apps/api 72/1143, apps/web 73/531.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 17:03:24 +02:00
parent b188946e31
commit f2fc39f51c
10 changed files with 212 additions and 38 deletions
+7 -6
View File
@@ -12,7 +12,7 @@ import {
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Role } from '@prisma/client';
import { Request, Response } from 'express';
import { Response } from 'express';
import { UserService } from '../user/user.service';
import { AuthService } from './auth.service';
import { CurrentUser } from './decorators/current-user.decorator';
@@ -22,6 +22,7 @@ import { AdminResetPasswordDto } from './dto/admin-reset-password.dto';
import { ChangePasswordDto } from './dto/change-password.dto';
import { RequestResetDto, ResetPasswordDto } from './dto/reset-password.dto';
import { RolesGuard } from './guards/roles.guard';
import type { AuthUser, LocalAuthenticatedRequest } from './types/auth-user';
@Controller('auth')
export class AuthController {
@@ -42,7 +43,7 @@ export class AuthController {
* Mandantenpruefung warf, damit ein API-Aufrufer denselben Statuscode
* sieht wie vor dieser Umstellung.
*/
private async resolveTargetTenantId(currentUser: any, userId: string): Promise<string> {
private async resolveTargetTenantId(currentUser: AuthUser, userId: string): Promise<string> {
if (currentUser.role === Role.SUPER_ADMIN) {
const target = await this.userService.findByIdForPlatformAdmin(userId);
if (!target) {
@@ -62,7 +63,7 @@ export class AuthController {
@Post('login')
@HttpCode(200)
async login(
@Req() req: Request,
@Req() req: LocalAuthenticatedRequest,
@Res({ passthrough: true }) res: Response,
) {
return this.authService.login(req.user, res);
@@ -91,7 +92,7 @@ export class AuthController {
* Befund C).
*/
@Get('me')
async me(@CurrentUser() user: any) {
async me(@CurrentUser() user: AuthUser) {
return this.authService.getMe(user.tenantId, user.id);
}
@@ -130,7 +131,7 @@ export class AuthController {
@Post('change-password')
@HttpCode(200)
async changePassword(
@CurrentUser() user: any,
@CurrentUser() user: AuthUser,
@Body() dto: ChangePasswordDto,
@Res({ passthrough: true }) res: Response,
) {
@@ -163,7 +164,7 @@ export class AuthController {
async adminResetPassword(
@Param('userId') userId: string,
@Body() dto: AdminResetPasswordDto,
@CurrentUser() currentUser: any,
@CurrentUser() currentUser: AuthUser,
) {
const tenantId = await this.resolveTargetTenantId(currentUser, userId);
await this.authService.adminResetPassword(