import { Body, Controller, Get, HttpCode, HttpStatus, NotFoundException, Post, Req, UseGuards } from "@nestjs/common"; import type { FastifyRequest } from "fastify"; import { getPrismaClient } from "@b2bcall/database"; import { changePassword, isPlatformUser, listUserTenants, login, logout, refreshSession, setActiveTenant, type AccessTokenClaims, } from "@b2bcall/auth"; import { JwtAuthGuard } from "../common/guards/jwt-auth.guard"; import { CurrentUser } from "../common/decorators/current-user.decorator"; import { LoginDto } from "./dto/login.dto"; import { RefreshDto } from "./dto/refresh.dto"; import { SelectTenantDto } from "./dto/select-tenant.dto"; import { ChangePasswordDto } from "./dto/change-password.dto"; import { LoginRateLimitGuard } from "./login-rate-limit.guard"; @Controller("auth") export class AuthController { @UseGuards(LoginRateLimitGuard) @Post("login") @HttpCode(HttpStatus.OK) async login(@Body() dto: LoginDto, @Req() req: FastifyRequest) { return login({ email: dto.email, password: dto.password, ipAddress: req.ip, userAgent: req.headers["user-agent"], }); } @Post("refresh") @HttpCode(HttpStatus.OK) async refresh(@Body() dto: RefreshDto) { return refreshSession(dto.refreshToken); } @UseGuards(JwtAuthGuard) @Post("logout") @HttpCode(HttpStatus.NO_CONTENT) async logout(@CurrentUser() user: AccessTokenClaims) { await logout(user.sessionId); } @UseGuards(JwtAuthGuard) @Get("me") async me(@CurrentUser() user: AccessTokenClaims) { const prisma = getPrismaClient(); const dbUser = await prisma.user.findUnique({ where: { id: user.sub }, select: { id: true, email: true, name: true }, }); if (!dbUser) throw new NotFoundException(); // Nome do tenant ativo pra topbar (frontend, PHASE 23) — `tenants` não // tem RLS (tabela raiz, ver docs/TENANT_ISOLATION.md), leitura direta // sem `withTenantContext` é segura aqui. const tenant = user.tenantId ? await prisma.tenant.findUnique({ where: { id: user.tenantId }, select: { id: true, code: true, tradeName: true, legalName: true }, }) : null; return { ...dbUser, isPlatformUser: await isPlatformUser(user.sub), tenant: tenant ? { id: tenant.id, code: tenant.code, name: tenant.tradeName ?? tenant.legalName } : null, }; } @UseGuards(JwtAuthGuard) @Get("tenants") async tenants(@CurrentUser() user: AccessTokenClaims) { const memberships = await listUserTenants(user.sub); return memberships.map((m) => ({ tenantId: m.tenant.id, code: m.tenant.code, name: m.tenant.tradeName ?? m.tenant.legalName, status: m.tenant.status, })); } @UseGuards(JwtAuthGuard) @Post("select-tenant") @HttpCode(HttpStatus.OK) async selectTenant(@CurrentUser() user: AccessTokenClaims, @Body() dto: SelectTenantDto) { const accessToken = await setActiveTenant(user.sessionId, user.sub, dto.tenantId); return { accessToken }; } @UseGuards(JwtAuthGuard) @Post("change-password") @HttpCode(HttpStatus.NO_CONTENT) async changePassword(@CurrentUser() user: AccessTokenClaims, @Body() dto: ChangePasswordDto) { await changePassword(user.sub, user.sessionId, dto.currentPassword, dto.newPassword); } }