- payments: real Stripe Checkout Session (redirect flow), confirmBySession, webhook handler - notifications: Zeptomail REST email, wa.me WhatsApp links, IntegrationsService injection - auth: changePassword, updateProfile, disableMfa (TOTP-verified) endpoints - packages: tryLinkPreAlert() auto-links on create (non-blocking) - integrations: catalog updated (zeptomail, stripe_webhook_secret; removed sendgrid/whatsapp-api) - web/portal/perfil: 3-tab layout (datos / contraseña / MFA) - web/portal/pago: Stripe redirect + ?success=1&session_id= callback, cancelled banner - web/admin/b2b: fix enum values (EN_COTIZACION, ACEPTADO) - web/carga-pesada: rich marketing landing (FCL/LCL, 7-step process, sectors, INEN callout) - tests: fix payments.service.spec (IntegrationsService+ConfigService mocks) - tests: fix notifications.service.spec (IntegrationsService mock, phone in mockUser) - all 104 tests passing, API + web builds clean
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import {
|
|
Controller, Post, Get, Patch, Body, Req, UseGuards, HttpCode, HttpStatus,
|
|
} from "@nestjs/common";
|
|
import { AuthService } from "./auth.service";
|
|
import { RegisterDto, LoginDto, RefreshDto, SetupMfaDto, ChangePasswordDto, UpdateProfileDto } from "./dto/auth.dto";
|
|
import { JwtAuthGuard } from "./guards/auth.guard";
|
|
import { CurrentUser } from "./decorators/current-user.decorator";
|
|
|
|
@Controller("auth")
|
|
export class AuthController {
|
|
constructor(private auth: AuthService) {}
|
|
|
|
/** POST /api/auth/register — Registro público (doc §09 paso 1) */
|
|
@Post("register")
|
|
register(@Body() dto: RegisterDto) {
|
|
return this.auth.register(dto);
|
|
}
|
|
|
|
/** POST /api/auth/login — Login con JWT + MFA opcional */
|
|
@Post("login")
|
|
@HttpCode(HttpStatus.OK)
|
|
login(@Body() dto: LoginDto, @Req() req: any) {
|
|
return this.auth.login(dto, req.ip);
|
|
}
|
|
|
|
/** POST /api/auth/refresh — Rotar refresh token */
|
|
@Post("refresh")
|
|
@HttpCode(HttpStatus.OK)
|
|
refresh(@Body() dto: RefreshDto) {
|
|
return this.auth.refresh(dto.refreshToken);
|
|
}
|
|
|
|
/** POST /api/auth/logout */
|
|
@Post("logout")
|
|
@UseGuards(JwtAuthGuard)
|
|
@HttpCode(HttpStatus.OK)
|
|
logout(@Body() dto: RefreshDto, @CurrentUser() user: any) {
|
|
return this.auth.logout(dto.refreshToken, user.id);
|
|
}
|
|
|
|
/** GET /api/auth/me — Perfil del usuario autenticado */
|
|
@Get("me")
|
|
@UseGuards(JwtAuthGuard)
|
|
me(@CurrentUser() user: any) {
|
|
return this.auth.getProfile(user.id);
|
|
}
|
|
|
|
/** PATCH /api/auth/me — Actualizar nombre / teléfono */
|
|
@Patch("me")
|
|
@UseGuards(JwtAuthGuard)
|
|
updateProfile(@Body() dto: UpdateProfileDto, @CurrentUser() user: any) {
|
|
return this.auth.updateProfile(user.id, dto);
|
|
}
|
|
|
|
/** PATCH /api/auth/password — Cambiar contraseña */
|
|
@Patch("password")
|
|
@UseGuards(JwtAuthGuard)
|
|
@HttpCode(HttpStatus.OK)
|
|
async changePassword(@Body() dto: ChangePasswordDto, @CurrentUser() user: any) {
|
|
await this.auth.changePassword(user.id, dto.oldPassword, dto.newPassword);
|
|
return { message: "Contraseña actualizada correctamente." };
|
|
}
|
|
|
|
/** POST /api/auth/mfa/setup — Genera QR para TOTP */
|
|
@Post("mfa/setup")
|
|
@UseGuards(JwtAuthGuard)
|
|
setupMfa(@CurrentUser() user: any) {
|
|
return this.auth.setupMfa(user.id);
|
|
}
|
|
|
|
/** POST /api/auth/mfa/verify — Activa MFA con primer código TOTP */
|
|
@Post("mfa/verify")
|
|
@UseGuards(JwtAuthGuard)
|
|
verifyMfa(@Body() dto: SetupMfaDto, @CurrentUser() user: any) {
|
|
return this.auth.verifyMfa(user.id, dto.totpCode);
|
|
}
|
|
|
|
/** POST /api/auth/mfa/disable — Desactiva MFA (requiere código TOTP) */
|
|
@Post("mfa/disable")
|
|
@UseGuards(JwtAuthGuard)
|
|
disableMfa(@Body() dto: SetupMfaDto, @CurrentUser() user: any) {
|
|
return this.auth.disableMfa(user.id, dto.totpCode);
|
|
}
|
|
}
|
|
|