import { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository'; import { IUserRepository, StoredUser } from '@core/identity/domain/repositories/IUserRepository'; import { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService'; import { User } from '@core/identity/domain/entities/User'; import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress'; import { randomUUID } from 'crypto'; import { Logger } from '@core/shared/application'; export class InMemoryAuthRepository implements IAuthRepository { constructor( private readonly userRepository: IUserRepository, private readonly passwordHashingService: IPasswordHashingService, private readonly logger: Logger, ) {} async findByEmail(email: EmailAddress): Promise { this.logger.debug(`[InMemoryAuthRepository] Finding user by email: ${email.value}`); const storedUser = await this.userRepository.findByEmail(email.value); if (!storedUser) { return null; } return User.fromStored(storedUser); } async save(user: User): Promise { this.logger.debug(`[InMemoryAuthRepository] Saving user with email: ${user.getEmail()}`); const userId = user.getId().value; const userEmail = user.getEmail() ?? ''; const userDisplayName = user.getDisplayName(); const userPasswordHash = user.getPasswordHash()?.value ?? ''; const storedUser: StoredUser = { id: userId, email: userEmail, passwordHash: userPasswordHash, displayName: userDisplayName, salt: '', // In-memory doesn't use actual salt, but Stub has to provide. primaryDriverId: user.getPrimaryDriverId() ?? undefined, createdAt: new Date(), }; // Check if user exists to decide between create or update const existingStoredUser = await this.userRepository.findById(userId); if (existingStoredUser) { // For update, ensure createdAt is preserved from existing user const updatedStoredUser: StoredUser = { ...storedUser, createdAt: existingStoredUser.createdAt, }; await this.userRepository.update(updatedStoredUser); } else { await this.userRepository.create(storedUser); } } async create(user: User, passwordPlain: string): Promise { this.logger.debug(`[InMemoryAuthRepository] Creating user with email: ${user.getEmail()}`); const passwordHashValue = await this.passwordHashingService.hash(passwordPlain); // Ensure email is not undefined before using const userEmail = user.getEmail() ?? ''; const userDisplayName = user.getDisplayName(); const userId = user.getId().value; const storedUser: StoredUser = { id: userId, email: userEmail, passwordHash: passwordHashValue, displayName: userDisplayName, salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it. primaryDriverId: user.getPrimaryDriverId() ?? undefined, createdAt: new Date(), }; const createdStoredUser = await this.userRepository.create(storedUser); return User.fromStored(createdStoredUser); } async update(user: User): Promise { this.logger.debug(`[InMemoryAuthRepository] Updating user with email: ${user.getEmail()}`); // Ensure values are not undefined before using const userId = user.getId().value; const userEmail = user.getEmail() ?? ''; const userPasswordHash = user.getPasswordHash()?.value ?? ''; const userDisplayName = user.getDisplayName(); const storedUser: StoredUser = { id: userId, email: userEmail, passwordHash: userPasswordHash, displayName: userDisplayName, salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it. primaryDriverId: user.getPrimaryDriverId() ?? undefined, createdAt: new Date(), // Assuming creation date is maintained or updated within UserRepository }; const updatedStoredUser = await this.userRepository.update(storedUser); return User.fromStored(updatedStoredUser); } async delete(userId: string): Promise { // Deletion needs to be implemented in InMemoryUserRepository and exposed via IUserRepository // For now, it's not supported. this.logger.warn(`[InMemoryAuthRepository] Delete operation not implemented for user: ${userId}`); return false; } async userExists(email: string): Promise { this.logger.debug(`[InMemoryAuthRepository] Checking if user exists for email: ${email}`); return this.userRepository.emailExists(email); } async verifyPassword(email: string, passwordPlain: string): Promise { this.logger.debug(`[InMemoryAuthRepository] Verifying password for user with email: ${email}`); const user = await this.findByEmail(EmailAddress.create(email)); // Use EmailAddress value object if (!user) { this.logger.warn(`[InMemoryAuthRepository] User not found for email: ${email}`); return null; } const isPasswordValid = await this.passwordHashingService.verify( passwordPlain, user.getPasswordHash()?.value ?? '', // Access value and provide fallback ); if (!isPasswordValid) { this.logger.warn(`[InMemoryAuthRepository] Invalid password for user with email: ${email}`); return null; } return user; } }