2025-02-01 21:48:13 +00:00
|
|
|
import { Result, UniqueID } from "@common/domain";
|
|
|
|
|
import { ITransactionManager } from "@common/infrastructure/database";
|
|
|
|
|
import {
|
2025-02-03 13:12:36 +00:00
|
|
|
AuthenticatedUser,
|
2025-02-01 21:48:13 +00:00
|
|
|
EmailAddress,
|
|
|
|
|
IAuthenticatedUserRepository,
|
|
|
|
|
PasswordHash,
|
|
|
|
|
Username,
|
|
|
|
|
} from "@contexts/auth/domain";
|
2025-02-03 13:12:36 +00:00
|
|
|
import { IAuthProvider } from "./auth-provider.interface";
|
2025-02-01 21:48:13 +00:00
|
|
|
import { IAuthService } from "./auth-service.interface";
|
|
|
|
|
|
|
|
|
|
export class AuthService implements IAuthService {
|
2025-02-03 18:03:23 +00:00
|
|
|
private readonly _respository!: IAuthenticatedUserRepository;
|
2025-02-01 21:48:13 +00:00
|
|
|
private readonly _transactionManager!: ITransactionManager;
|
2025-02-03 13:12:36 +00:00
|
|
|
private readonly _authProvider: IAuthProvider;
|
2025-02-01 21:48:13 +00:00
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
constructor(
|
|
|
|
|
repository: IAuthenticatedUserRepository,
|
|
|
|
|
transactionManager: ITransactionManager,
|
|
|
|
|
authProvider: IAuthProvider
|
|
|
|
|
) {
|
2025-02-01 21:48:13 +00:00
|
|
|
this._respository = repository;
|
|
|
|
|
this._transactionManager = transactionManager;
|
2025-02-03 13:12:36 +00:00
|
|
|
this._authProvider = authProvider;
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
2025-01-30 10:45:31 +00:00
|
|
|
|
|
|
|
|
/**
|
2025-02-03 21:54:51 +00:00
|
|
|
*
|
2025-01-30 10:45:31 +00:00
|
|
|
* Registra un nuevo usuario en la base de datos bajo transacción.
|
|
|
|
|
*/
|
2025-02-01 21:48:13 +00:00
|
|
|
async registerUser(params: {
|
|
|
|
|
username: Username;
|
|
|
|
|
email: EmailAddress;
|
2025-02-03 13:12:36 +00:00
|
|
|
passwordHash: PasswordHash;
|
|
|
|
|
}): Promise<Result<AuthenticatedUser, Error>> {
|
|
|
|
|
try {
|
|
|
|
|
return await this._transactionManager.complete(async (transaction) => {
|
|
|
|
|
const { username, email, passwordHash } = params;
|
2025-01-30 10:45:31 +00:00
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
// Verificar si el usuario ya existe
|
|
|
|
|
const userExists = await this._respository.findUserByEmail(email, transaction);
|
|
|
|
|
if (userExists.isSuccess && userExists.data) {
|
|
|
|
|
return Result.fail(new Error("Email is already registered"));
|
|
|
|
|
}
|
2025-01-30 10:45:31 +00:00
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
if (userExists.isFailure) {
|
|
|
|
|
return Result.fail(userExists.error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newUserId = UniqueID.generateNewID().data;
|
|
|
|
|
|
|
|
|
|
const userOrError = AuthenticatedUser.create(
|
|
|
|
|
{
|
|
|
|
|
username,
|
|
|
|
|
email,
|
|
|
|
|
passwordHash,
|
|
|
|
|
roles: ["USER"],
|
|
|
|
|
},
|
|
|
|
|
newUserId
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (userOrError.isFailure) {
|
|
|
|
|
return Result.fail(userOrError.error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const createdResult = await this._respository.createUser(userOrError.data, transaction);
|
|
|
|
|
|
|
|
|
|
if (createdResult.isFailure) {
|
|
|
|
|
return Result.fail(createdResult.error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result.ok(userOrError.data);
|
|
|
|
|
});
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
return Result.fail(error as Error);
|
|
|
|
|
}
|
2025-01-30 10:45:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-02-03 21:54:51 +00:00
|
|
|
*
|
|
|
|
|
* Autentica a un usuario validando su email y contraseña.
|
2025-01-30 10:45:31 +00:00
|
|
|
*/
|
2025-02-03 21:54:51 +00:00
|
|
|
async loginUser(params: {
|
|
|
|
|
email: EmailAddress;
|
|
|
|
|
passwordHash: PasswordHash;
|
|
|
|
|
}): Promise<Result<AuthenticatedUser, Error>> {
|
|
|
|
|
try {
|
|
|
|
|
return await this._transactionManager.complete(async (transaction) => {
|
|
|
|
|
const { email, passwordHash } = params;
|
|
|
|
|
|
|
|
|
|
// 🔹 Verificar si el usuario existe en la base de datos
|
|
|
|
|
const userResult = await this._respository.findUserByEmail(email, transaction);
|
|
|
|
|
if (userResult.isFailure) {
|
|
|
|
|
return Result.fail(new Error("Invalid email or password"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = userResult.data;
|
|
|
|
|
if (!user) {
|
|
|
|
|
return Result.fail(new Error("Invalid email or password"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 🔹 Verificar que la contraseña sea correcta
|
|
|
|
|
const isValidPassword = await user.comparePassword(passwordHash);
|
|
|
|
|
if (!isValidPassword) {
|
|
|
|
|
return Result.fail(new Error("Invalid email or password"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 🔹 Generar Access Token y Refresh Token
|
|
|
|
|
user.accessToken = this._authProvider.generateAccessToken({
|
|
|
|
|
userId: user.id.toString(),
|
|
|
|
|
email: email.toString(),
|
|
|
|
|
roles: ["USER"],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
user.refreshToken = this._authProvider.generateRefreshToken({
|
|
|
|
|
userId: user.id.toString(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Result.ok(user);
|
|
|
|
|
});
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
return Result.fail(error as Error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-30 10:45:31 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔹 `selectCompany`
|
|
|
|
|
* Permite a un usuario seleccionar una empresa activa en la sesión bajo transacción.
|
|
|
|
|
*/
|
2025-02-03 13:12:36 +00:00
|
|
|
/*static async selectCompany(
|
2025-01-30 10:45:31 +00:00
|
|
|
userId: string,
|
|
|
|
|
companyId: string
|
|
|
|
|
): Promise<Result<{ message: string }, Error>> {
|
|
|
|
|
return await authUserRepository.executeTransaction(async (transaction) => {
|
|
|
|
|
const user = await authUserRepository.findById(userId, transaction);
|
2025-02-03 13:12:36 +00:00
|
|
|
if (user.isFailure) {
|
2025-01-30 10:45:31 +00:00
|
|
|
return Result.fail(new Error("User not found"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isAssociated = await authUserRepository.isUserAssociatedWithCompany(
|
|
|
|
|
userId,
|
|
|
|
|
companyId,
|
|
|
|
|
transaction
|
|
|
|
|
);
|
|
|
|
|
if (!isAssociated) {
|
|
|
|
|
return Result.fail(new Error("User does not have access to this company"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result.ok({ message: "Company selected successfully" });
|
|
|
|
|
});
|
2025-02-03 13:12:36 +00:00
|
|
|
}*/
|
2025-01-30 10:45:31 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔹 `logout`
|
|
|
|
|
* Simula el cierre de sesión de un usuario. No requiere transacción.
|
|
|
|
|
*/
|
2025-02-03 13:12:36 +00:00
|
|
|
/*static logout(): Result<{ message: string }, never> {
|
2025-01-30 10:45:31 +00:00
|
|
|
return Result.ok({ message: "Logged out successfully" });
|
2025-02-03 13:12:36 +00:00
|
|
|
}*/
|
2025-01-30 10:45:31 +00:00
|
|
|
}
|