feat(02-03): password reset flow, force-change interceptor, MailModule

- MailModule with SMTP transport configured from ENV variables
- MailService for password reset and welcome emails (plain text, i18n)
- Password reset flow: request-reset (public), reset-password (token-based)
- Change password for logged-in users with current password verification
- Admin reset password endpoint (ADMIN/SUPER_ADMIN only, D-03)
- ForcePasswordChangeInterceptor blocks all routes except change-password,
  logout, me when mustChangePassword=true (D-06, Pitfall 5)
- Frontend: reset-password request page, token reset page, change-password page
- Forgot password link added to login page
- MailHog service added to docker-compose.dev.yml for dev email testing
- SMTP env vars added to docker-compose.yml (defaults to MailHog)
- Complete DE/EN i18n coverage for reset and change password flows
- SUS packages installed: @nestjs-modules/mailer, nodemailer, ldapts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 13:48:23 +02:00
parent eaaa9adfa5
commit ac617f4fe5
20 changed files with 4216 additions and 12 deletions
+9 -1
View File
@@ -1,10 +1,12 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { AuthModule } from './auth/auth.module';
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
import { RolesGuard } from './auth/guards/roles.guard';
import { ForcePasswordChangeInterceptor } from './auth/interceptors/force-password-change.interceptor';
import { HealthModule } from './health/health.module';
import { MailModule } from './mail/mail.module';
import { PrismaModule } from './prisma/prisma.module';
import { TenantMiddleware } from './tenant/tenant.middleware';
import { TenantModule } from './tenant/tenant.module';
@@ -18,6 +20,7 @@ import { UserModule } from './user/user.module';
UserModule,
TenantModule,
HealthModule,
MailModule,
],
providers: [
// Global JWT guard: all routes require auth unless @Public()
@@ -30,6 +33,11 @@ import { UserModule } from './user/user.module';
provide: APP_GUARD,
useClass: RolesGuard,
},
// Global interceptor: forces password change if mustChangePassword=true (D-06 / Pitfall 5)
{
provide: APP_INTERCEPTOR,
useClass: ForcePasswordChangeInterceptor,
},
],
})
export class AppModule implements NestModule {