Backend Complete: Interface Refactoring & Service Container Enhancements
Repository Interface Optimization: - Created IBaseRepository.ts and IPaginatedRepository.ts - Refactored all 7 repository interfaces to extend base interfaces - Eliminated ~200 lines of redundant code (70% reduction) - Improved type safety and maintainability Dependency Injection Improvements: - Added EmailService and GameTokenService to DIContainer - Updated CreateUserCommandHandler constructor for DI - Updated RequestPasswordResetCommandHandler constructor for DI - Enhanced testability and service consistency Environment Configuration: - Created comprehensive .env.example with 40+ variables - Organized into 12 logical sections (Database, Security, Email, etc.) - Added security guidelines and best practices - Documented all backend environment requirements Documentation: - Added comprehensive codebase review - Created refactoring summary report - Added frontend implementation guide Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { IUserRepository } from '../../../Domain/IRepository/IUserRepository';
|
||||
import { CreateUserCommand } from './CreateUserCommand';
|
||||
import { ShortUserDto } from '../../DTOs/UserDto';
|
||||
import { UserAggregate, UserState } from '../../../Domain/User/UserAggregate';
|
||||
import { UserMapper } from '../../DTOs/Mappers/UserMapper';
|
||||
import { PasswordService } from '../../Services/PasswordService';
|
||||
import { EmailService } from '../../Services/EmailService';
|
||||
import { TokenService } from '../../Services/TokenService';
|
||||
import { logDatabase, logError, logAuth, logWarning } from '../../Services/Logger';
|
||||
|
||||
export class CreateUserCommandHandler {
|
||||
constructor(
|
||||
private readonly userRepo: IUserRepository,
|
||||
private readonly emailService: EmailService
|
||||
) {}
|
||||
|
||||
async execute(cmd: CreateUserCommand): Promise<ShortUserDto> {
|
||||
try {
|
||||
// Validate password strength
|
||||
const passwordValidation = PasswordService.validatePasswordStrength(cmd.password);
|
||||
if (!passwordValidation.isValid) {
|
||||
throw new Error(`Password validation failed: ${passwordValidation.errors.join(', ')}`);
|
||||
}
|
||||
|
||||
const user = new UserAggregate();
|
||||
user.username = cmd.username;
|
||||
|
||||
// Hash the password before storing
|
||||
user.password = await PasswordService.hashPassword(cmd.password);
|
||||
|
||||
// Generate verification token
|
||||
const verificationTokenData = TokenService.generateVerificationToken();
|
||||
user.token = await TokenService.hashToken(verificationTokenData.token);
|
||||
user.TokenExpires = verificationTokenData.expiresAt;
|
||||
|
||||
user.email = cmd.email;
|
||||
user.fname = cmd.fname;
|
||||
user.lname = cmd.lname;
|
||||
user.orgid = cmd.orgid || null;
|
||||
user.phone = cmd.phone || null;
|
||||
user.state = UserState.REGISTERED_NOT_VERIFIED;
|
||||
|
||||
const created = await this.userRepo.create(user);
|
||||
|
||||
// Send verification email (non-blocking)
|
||||
this.sendVerificationEmailAsync(created, verificationTokenData.token);
|
||||
|
||||
return UserMapper.toShortDto(created);
|
||||
} catch (error) {
|
||||
// Only log the error once here, don't log again in router
|
||||
const errorMessage = (error as Error).message;
|
||||
|
||||
// Re-throw validation errors as-is (don't log as these are user input errors)
|
||||
if (errorMessage.includes('Password validation failed')) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Handle database constraint errors
|
||||
if (errorMessage.includes('duplicate') || errorMessage.includes('unique') ||
|
||||
errorMessage.includes('UNIQUE constraint') || errorMessage.includes('already exists')) {
|
||||
throw new Error('User with this username or email already exists');
|
||||
}
|
||||
|
||||
// Log database/system errors but throw user-friendly message
|
||||
logError('CreateUserCommandHandler error', error as Error);
|
||||
throw new Error('Failed to create user');
|
||||
}
|
||||
}
|
||||
|
||||
private async sendVerificationEmailAsync(user: UserAggregate, token: string): Promise<void> {
|
||||
try {
|
||||
const baseUrl = process.env.APP_BASE_URL || 'http://localhost:3000';
|
||||
const verificationUrl = TokenService.generateVerificationUrl(baseUrl, token);
|
||||
|
||||
const emailSent = await this.emailService.sendVerificationEmail(
|
||||
user.email,
|
||||
`${user.fname} ${user.lname}`,
|
||||
token,
|
||||
verificationUrl
|
||||
);
|
||||
|
||||
if (!emailSent) {
|
||||
logWarning('Failed to send verification email', { email: user.email, userId: user.id });
|
||||
} else {
|
||||
logAuth('Verification email sent successfully', user.id, { email: user.email });
|
||||
}
|
||||
} catch (emailError) {
|
||||
logError('Error sending verification email', emailError as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user