import bCrypt from "bcryptjs"; import { AggregateRoot, Email, IDomainError, Name, Password, Result, UniqueID, handleDomainError, } from "@shared/contexts"; import { UserHasName } from "./User.specifications"; export interface IUserProps { name: Name; email: Email; password: Password; } //type ISecuredUserProps = Omit; export interface IUser { id: UniqueID; name: Name; email: Email; isUser: boolean; isAdmin: boolean; verifyPassword: (candidatePassword: string) => boolean; } export class User extends AggregateRoot implements IUser { static readonly ERROR_USER_WITHOUT_NAME = "ERROR_USER_WITHOUT_NAME"; public static create( props: IUserProps, id?: UniqueID, ): Result { const user = new User(props, id); // Reglas de negocio / validaciones const isValidUser = new UserHasName().isSatisfiedBy(user); if (!isValidUser) { return Result.fail(handleDomainError(User.ERROR_USER_WITHOUT_NAME)); } return Result.ok(user); } public static async hashPassword(password): Promise { return Password.hashPassword(password); } private _password: string; get name(): Name { return this.props.name; } get email(): Email { return this.props.email; } get hashed_password(): string { return this._password; } get isUser(): boolean { return true; } get isAdmin(): boolean { return true; } public verifyPassword(candidatePassword: string): boolean { return bCrypt.compareSync(candidatePassword, this._password!); } } async function genSalt(rounds = 10): Promise { return new Promise((resolve, reject) => { bCrypt.genSalt(rounds, function (err, salt) { if (err) return reject(err); return resolve(salt); }); }); } async function hashPassword(password: string, salt: string): Promise { return new Promise((resolve, reject) => { bCrypt.hash(password, salt, function (err, hash) { if (err) return reject(err); return resolve(hash); }); }); }