feat(api): implement GDPR-compliant user data deletion

- Add deletedAt/deletionScheduledAt fields to User model with indexes
- Implement 5 CQRS command handlers:
  - RequestUserDeletion: 30-day soft-delete grace period
  - CancelUserDeletion: restore within grace period
  - ForceDeleteUser: admin immediate deletion with PII anonymization
  - ProcessScheduledDeletions: cron-ready batch processor
  - ExportUserData: GDPR Article 20 data portability
- Cascade strategy: anonymize PII, expire listings, cancel subscriptions,
  delete reviews/inquiries/searches/notifications, preserve payments for audit
- Add UserDataController with DELETE /users/me, POST /users/me/cancel-deletion,
  GET /users/me/export, DELETE /users/:id/force (admin)
- 22 unit tests covering all handlers (160 files, 853 tests passing)
- Migration: 20260410000000_add_user_soft_delete_fields

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-10 05:43:54 +07:00
parent 34202f2527
commit e03c4699d0
21 changed files with 914 additions and 4 deletions

View File

@@ -2,9 +2,14 @@ import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { CancelUserDeletionHandler } from './application/commands/cancel-user-deletion/cancel-user-deletion.handler';
import { ExportUserDataHandler } from './application/commands/export-user-data/export-user-data.handler';
import { ForceDeleteUserHandler } from './application/commands/force-delete-user/force-delete-user.handler';
import { LoginUserHandler } from './application/commands/login-user/login-user.handler';
import { ProcessScheduledDeletionsHandler } from './application/commands/process-scheduled-deletions/process-scheduled-deletions.handler';
import { RefreshTokenHandler } from './application/commands/refresh-token/refresh-token.handler';
import { RegisterUserHandler } from './application/commands/register-user/register-user.handler';
import { RequestUserDeletionHandler } from './application/commands/request-user-deletion/request-user-deletion.handler';
import { VerifyKycHandler } from './application/commands/verify-kyc/verify-kyc.handler';
import { GetAgentByUserIdHandler } from './application/queries/get-agent-by-user-id/get-agent-by-user-id.handler';
import { GetProfileHandler } from './application/queries/get-profile/get-profile.handler';
@@ -20,12 +25,18 @@ import { LocalStrategy } from './infrastructure/strategies/local.strategy';
import { ZaloOAuthStrategy } from './infrastructure/strategies/zalo-oauth.strategy';
import { AuthController } from './presentation/controllers/auth.controller';
import { OAuthController } from './presentation/controllers/oauth.controller';
import { UserDataController } from './presentation/controllers/user-data.controller';
const CommandHandlers = [
RegisterUserHandler,
LoginUserHandler,
RefreshTokenHandler,
VerifyKycHandler,
RequestUserDeletionHandler,
CancelUserDeletionHandler,
ForceDeleteUserHandler,
ProcessScheduledDeletionsHandler,
ExportUserDataHandler,
];
const QueryHandlers = [GetProfileHandler, GetAgentByUserIdHandler];
@@ -47,7 +58,7 @@ const QueryHandlers = [GetProfileHandler, GetAgentByUserIdHandler];
},
}),
],
controllers: [AuthController, OAuthController],
controllers: [AuthController, OAuthController, UserDataController],
providers: [
// Repositories
{ provide: USER_REPOSITORY, useClass: PrismaUserRepository },