feat: add MFA/TOTP auth, PII encryption, agents/leads/inquiries modules, and comprehensive tests
- Add TOTP-based MFA with setup, verify, disable, backup codes, and challenge flow - Add PII field encryption middleware with AES-256-GCM and deterministic search hashes - Add agents, inquiries, and leads domain modules with entities, events, value objects - Add web dashboard pages for inquiries and leads with detail dialogs - Add 30+ component tests (valuation, charts, listings, search, providers, UI) - Add Prisma migrations for encryption hash columns and MFA TOTP support - Fix all ESLint errors (unused imports, duplicate imports, lint auto-fixes) - Update dependencies and lock file - Clean up obsolete exploration/QA docs, add audit documentation Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { Inject, InternalServerErrorException } from '@nestjs/common';
|
||||
import { type IQueryHandler, QueryHandler } from '@nestjs/cqrs';
|
||||
import { DomainException, type LoggerService, ValidationException } from '@modules/shared';
|
||||
import { USER_REPOSITORY, type IUserRepository } from '../../../domain/repositories/user.repository';
|
||||
import { GetMfaStatusQuery } from './get-mfa-status.query';
|
||||
|
||||
export interface MfaStatusDto {
|
||||
enabled: boolean;
|
||||
enabledAt: string | null;
|
||||
backupCodesRemaining: number;
|
||||
}
|
||||
|
||||
@QueryHandler(GetMfaStatusQuery)
|
||||
export class GetMfaStatusHandler implements IQueryHandler<GetMfaStatusQuery> {
|
||||
constructor(
|
||||
@Inject(USER_REPOSITORY) private readonly userRepo: IUserRepository,
|
||||
private readonly logger: LoggerService,
|
||||
) {}
|
||||
|
||||
async execute(query: GetMfaStatusQuery): Promise<MfaStatusDto> {
|
||||
try {
|
||||
const user = await this.userRepo.findById(query.userId);
|
||||
if (!user) {
|
||||
throw new ValidationException('Người dùng không tồn tại');
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: user.totpEnabled,
|
||||
enabledAt: user.totpEnabledAt?.toISOString() ?? null,
|
||||
backupCodesRemaining: user.totpBackupCodes.length,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof DomainException) throw error;
|
||||
this.logger.error(
|
||||
`Failed to get MFA status: ${error instanceof Error ? error.message : error}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
this.constructor.name,
|
||||
);
|
||||
throw new InternalServerErrorException('Không thể lấy trạng thái MFA');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export class GetMfaStatusQuery {
|
||||
constructor(public readonly userId: string) {}
|
||||
}
|
||||
Reference in New Issue
Block a user