refactor to adapters
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import type { EmailValidationResult } from '../types/EmailAddress';
|
||||
import { validateEmail } from '../types/EmailAddress';
|
||||
import { UserId } from '../value-objects/UserId';
|
||||
import { PasswordHash } from '../value-objects/PasswordHash';
|
||||
import { StoredUser } from '../repositories/IUserRepository';
|
||||
|
||||
export interface UserProps {
|
||||
id: UserId;
|
||||
displayName: string;
|
||||
email?: string;
|
||||
passwordHash?: PasswordHash;
|
||||
iracingCustomerId?: string;
|
||||
primaryDriverId?: string;
|
||||
avatarUrl?: string;
|
||||
@@ -15,6 +18,7 @@ export class User {
|
||||
private readonly id: UserId;
|
||||
private displayName: string;
|
||||
private email: string | undefined;
|
||||
private passwordHash: PasswordHash | undefined;
|
||||
private iracingCustomerId: string | undefined;
|
||||
private primaryDriverId: string | undefined;
|
||||
private avatarUrl: string | undefined;
|
||||
@@ -47,6 +51,22 @@ export class User {
|
||||
return new User(props);
|
||||
}
|
||||
|
||||
public static fromStored(stored: StoredUser): User {
|
||||
const passwordHash = stored.passwordHash ? PasswordHash.fromHash(stored.passwordHash) : undefined;
|
||||
const userProps: any = {
|
||||
id: UserId.fromString(stored.id),
|
||||
displayName: stored.displayName,
|
||||
email: stored.email,
|
||||
};
|
||||
if (passwordHash) {
|
||||
userProps.passwordHash = passwordHash;
|
||||
}
|
||||
if (stored.primaryDriverId) {
|
||||
userProps.primaryDriverId = stored.primaryDriverId;
|
||||
}
|
||||
return new User(userProps);
|
||||
}
|
||||
|
||||
public getId(): UserId {
|
||||
return this.id;
|
||||
}
|
||||
@@ -59,6 +79,10 @@ export class User {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
public getPasswordHash(): PasswordHash | undefined {
|
||||
return this.passwordHash;
|
||||
}
|
||||
|
||||
public getIracingCustomerId(): string | undefined {
|
||||
return this.iracingCustomerId;
|
||||
}
|
||||
|
||||
19
core/identity/domain/repositories/IAuthRepository.ts
Normal file
19
core/identity/domain/repositories/IAuthRepository.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { EmailAddress } from '../value-objects/EmailAddress';
|
||||
import { User } from '../entities/User';
|
||||
|
||||
/**
|
||||
* Domain Repository: IAuthRepository
|
||||
*
|
||||
* Repository interface for authentication operations.
|
||||
*/
|
||||
export interface IAuthRepository {
|
||||
/**
|
||||
* Find user by email
|
||||
*/
|
||||
findByEmail(email: EmailAddress): Promise<User | null>;
|
||||
|
||||
/**
|
||||
* Save a user
|
||||
*/
|
||||
save(user: User): Promise<void>;
|
||||
}
|
||||
26
core/identity/domain/services/PasswordHashingService.ts
Normal file
26
core/identity/domain/services/PasswordHashingService.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { PasswordHash } from '../value-objects/PasswordHash';
|
||||
|
||||
/**
|
||||
* Domain Service: PasswordHashingService
|
||||
*
|
||||
* Service for password hashing and verification.
|
||||
*/
|
||||
export interface IPasswordHashingService {
|
||||
hash(plain: string): Promise<string>;
|
||||
verify(plain: string, hash: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation using bcrypt via PasswordHash VO.
|
||||
*/
|
||||
export class PasswordHashingService implements IPasswordHashingService {
|
||||
async hash(plain: string): Promise<string> {
|
||||
const passwordHash = await PasswordHash.create(plain);
|
||||
return passwordHash.value;
|
||||
}
|
||||
|
||||
async verify(plain: string, hash: string): Promise<boolean> {
|
||||
const passwordHash = PasswordHash.fromHash(hash);
|
||||
return passwordHash.verify(plain);
|
||||
}
|
||||
}
|
||||
41
core/identity/domain/value-objects/PasswordHash.ts
Normal file
41
core/identity/domain/value-objects/PasswordHash.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import type { IValueObject } from '@gridpilot/shared/domain';
|
||||
|
||||
export interface PasswordHashProps {
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value Object: PasswordHash
|
||||
*
|
||||
* Wraps a bcrypt-hashed password string and provides verification.
|
||||
*/
|
||||
export class PasswordHash implements IValueObject<PasswordHashProps> {
|
||||
public readonly props: PasswordHashProps;
|
||||
|
||||
private constructor(value: string) {
|
||||
this.props = { value };
|
||||
}
|
||||
|
||||
static async create(plain: string): Promise<PasswordHash> {
|
||||
const saltRounds = 12;
|
||||
const hash = await bcrypt.hash(plain, saltRounds);
|
||||
return new PasswordHash(hash);
|
||||
}
|
||||
|
||||
static fromHash(hash: string): PasswordHash {
|
||||
return new PasswordHash(hash);
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value;
|
||||
}
|
||||
|
||||
async verify(plain: string): Promise<boolean> {
|
||||
return bcrypt.compare(plain, this.props.value);
|
||||
}
|
||||
|
||||
equals(other: IValueObject<PasswordHashProps>): boolean {
|
||||
return this.props.value === other.props.value;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import type { IValueObject } from '@gridpilot/shared/domain';
|
||||
|
||||
export interface UserIdProps {
|
||||
@@ -14,6 +15,10 @@ export class UserId implements IValueObject<UserIdProps> {
|
||||
this.props = { value };
|
||||
}
|
||||
|
||||
public static create(): UserId {
|
||||
return new UserId(uuidv4());
|
||||
}
|
||||
|
||||
public static fromString(value: string): UserId {
|
||||
return new UserId(value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user