import { EmailAddress } from '../../domain/value-objects/EmailAddress'; import { UserId } from '../../domain/value-objects/UserId'; import { User } from '../../domain/entities/User'; import { IAuthRepository } from '../../domain/repositories/IAuthRepository'; import { IPasswordHashingService } from '../../domain/services/PasswordHashingService'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { UseCaseOutputPort, Logger, UseCase } from '@core/shared/application'; export type SignupInput = { email: string; password: string; displayName: string; }; export type SignupResult = { user: User; }; export type SignupErrorCode = 'USER_ALREADY_EXISTS' | 'WEAK_PASSWORD' | 'INVALID_DISPLAY_NAME' | 'REPOSITORY_ERROR'; export type SignupApplicationError = ApplicationErrorCode; /** * Application Use Case: SignupUseCase * * Handles user registration. */ export class SignupUseCase implements UseCase { constructor( private readonly authRepo: IAuthRepository, private readonly passwordService: IPasswordHashingService, private readonly logger: Logger, private readonly output: UseCaseOutputPort, ) {} async execute(input: SignupInput): Promise> { try { // Validate email format const emailVO = EmailAddress.create(input.email); // Validate password strength if (!this.isPasswordStrong(input.password)) { return Result.err({ code: 'WEAK_PASSWORD', details: { message: 'Password must be at least 8 characters and contain uppercase, lowercase, and number' }, }); } // Check if user exists const existingUser = await this.authRepo.findByEmail(emailVO); if (existingUser) { return Result.err({ code: 'USER_ALREADY_EXISTS', details: { message: 'User already exists' }, }); } // Hash password const hashedPassword = await this.passwordService.hash(input.password); const passwordHashModule = await import('../../domain/value-objects/PasswordHash'); const passwordHash = passwordHashModule.PasswordHash.fromHash(hashedPassword); // Create user (displayName validation happens in User entity constructor) const userId = UserId.create(); const user = User.create({ id: userId, displayName: input.displayName, email: emailVO.value, passwordHash, }); await this.authRepo.save(user); this.output.present({ user }); return Result.ok(undefined); } catch (error) { // Handle specific validation errors from User entity if (error instanceof Error) { if (error.message.includes('Name must be at least') || error.message.includes('Name can only contain') || error.message.includes('Please use your real name')) { return Result.err({ code: 'INVALID_DISPLAY_NAME', details: { message: error.message }, }); } } const message = error instanceof Error && error.message ? error.message : 'Failed to execute SignupUseCase'; this.logger.error('SignupUseCase.execute failed', error instanceof Error ? error : undefined, { input, }); return Result.err({ code: 'REPOSITORY_ERROR', details: { message }, }); } } private isPasswordStrong(password: string): boolean { if (password.length < 8) return false; if (!/[a-z]/.test(password)) return false; if (!/[A-Z]/.test(password)) return false; if (!/\d/.test(password)) return false; return true; } }