This commit is contained in:
David Arranz 2025-02-06 12:55:47 +01:00
parent 36788f34e8
commit e8d606714a
16 changed files with 224 additions and 200 deletions

View File

@ -9,6 +9,11 @@
"date": 1738768744297, "date": 1738768744297,
"name": "debug-2025-02-05.log", "name": "debug-2025-02-05.log",
"hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256" "hash": "35182b14bda063a4b734238473f84cfa66a0362a76d8f12f0c20277df81c7256"
},
{
"date": 1738834648631,
"name": "debug-2025-02-06.log",
"hash": "fbca91e028d38f5065d0e5bdbbfdbf49aadd06feddd074c6a1419b7c9e6b4fd7"
} }
], ],
"hashType": "sha256" "hashType": "sha256"

View File

@ -9,6 +9,11 @@
"date": 1738768744292, "date": 1738768744292,
"name": "error-2025-02-05.log", "name": "error-2025-02-05.log",
"hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8" "hash": "c32d976d68382b2ba2ddec8c907c30547ec9fda2bb31180bfdbeb685964810a8"
},
{
"date": 1738834648629,
"name": "error-2025-02-06.log",
"hash": "17c45ac8b0d8d1bf22f601247a7f1384f004d06420d39e78eb65cb0a26815e13"
} }
], ],
"hashType": "sha256" "hashType": "sha256"

View File

@ -14,6 +14,7 @@ const initLogger = () => {
format.timestamp(), format.timestamp(),
format.align(), format.align(),
format.splat(), format.splat(),
format.errors({ stack: !isProduction }),
format.printf((info) => { format.printf((info) => {
const rid = rTracer.id(); const rid = rTracer.id();

View File

@ -1,11 +0,0 @@
import { AuthenticatedUser } from "../domain";
export interface IAuthProvider {
/* JWT Strategy */
generateAccessToken(payload: object): string;
generateRefreshToken(payload: object): string;
verifyToken(token: string): Promise<AuthenticatedUser | null>;
/* LocalStrategy */
//_verifyUser(email: string, password: string): Promise<AuthenticatedUser | null>;
}

View File

@ -2,6 +2,10 @@ import { Result, UniqueID } from "@common/domain";
import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain"; import { AuthenticatedUser, EmailAddress, HashPassword, PlainPassword, Username } from "../domain";
export interface IAuthService { export interface IAuthService {
generateAccessToken(payload: object): string;
generateRefreshToken(payload: object): string;
verifyToken(token: string): Promise<AuthenticatedUser | null>;
registerUser(params: { registerUser(params: {
username: Username; username: Username;
email: EmailAddress; email: EmailAddress;
@ -24,4 +28,11 @@ export interface IAuthService {
Error Error
> >
>; >;
logoutUser(params: { email: EmailAddress; tabId: UniqueID }): Promise<Result<void, Error>>;
verifyUser(params: {
email: EmailAddress;
plainPassword: PlainPassword;
}): Promise<Result<AuthenticatedUser, Error>>;
} }

View File

@ -1,33 +1,47 @@
import { Result, UniqueID } from "@common/domain"; import { Result, UniqueID } from "@common/domain";
import { ITransactionManager } from "@common/infrastructure/database"; import { ITransactionManager } from "@common/infrastructure/database";
import jwt from "jsonwebtoken";
import { import {
AuthenticatedUser, AuthenticatedUser,
EmailAddress, EmailAddress,
HashPassword, HashPassword,
IAuthenticatedUserRepository, IAuthenticatedUserRepository,
PlainPassword,
TabContext, TabContext,
Username, Username,
} from "../domain"; } from "../domain";
import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface"; import { ITabContextRepository } from "../domain/repositories/tab-context-repository.interface";
import { IAuthProvider } from "./auth-provider.interface";
import { IAuthService } from "./auth-service.interface"; import { IAuthService } from "./auth-service.interface";
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
const ACCESS_EXPIRATION = process.env.JWT_ACCESS_EXPIRATION || "1h";
const REFRESH_EXPIRATION = process.env.JWT_REFRESH_EXPIRATION || "7d";
export class AuthService implements IAuthService { export class AuthService implements IAuthService {
private readonly _userRepo!: IAuthenticatedUserRepository; private readonly _userRepo!: IAuthenticatedUserRepository;
private readonly _tabContactRepo!: ITabContextRepository; private readonly _tabContactRepo!: ITabContextRepository;
private readonly _transactionManager!: ITransactionManager; private readonly _transactionManager!: ITransactionManager;
private readonly _authProvider: IAuthProvider;
constructor( constructor(
userRepo: IAuthenticatedUserRepository, userRepo: IAuthenticatedUserRepository,
tabContextRepo: ITabContextRepository, tabContextRepo: ITabContextRepository,
transactionManager: ITransactionManager, transactionManager: ITransactionManager
authProvider: IAuthProvider
) { ) {
this._userRepo = userRepo; this._userRepo = userRepo;
this._tabContactRepo = tabContextRepo; this._tabContactRepo = tabContextRepo;
this._transactionManager = transactionManager; this._transactionManager = transactionManager;
this._authProvider = authProvider; }
generateAccessToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: ACCESS_EXPIRATION });
}
generateRefreshToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: REFRESH_EXPIRATION });
}
verifyToken(token: string): any {
return jwt.verify(token, SECRET_KEY);
} }
/** /**
@ -125,25 +139,23 @@ export class AuthService implements IAuthService {
const contextOrError = TabContext.create({ const contextOrError = TabContext.create({
userId: user.id, userId: user.id,
tabId: tabId, tabId: tabId,
companyId: UniqueID.generateUndefinedID().data,
branchId: UniqueID.generateUndefinedID().data,
}); });
if (contextOrError.isFailure) { if (contextOrError.isFailure) {
return Result.fail(new Error("Error creating user context")); return Result.fail(new Error("Error creating user context"));
} }
await this._tabContactRepo.registerContext(contextOrError.data, transaction); await this._tabContactRepo.registerContextByTabId(contextOrError.data, transaction);
// 🔹 Generar Access Token y Refresh Token // 🔹 Generar Access Token y Refresh Token
const accessToken = this._authProvider.generateAccessToken({ const accessToken = this.generateAccessToken({
userId: user.id.toString(), userId: user.id.toString(),
email: email.toString(), email: email.toString(),
tabId: tabId.toString(), tabId: tabId.toString(),
roles: ["USER"], roles: ["USER"],
}); });
const refreshToken = this._authProvider.generateRefreshToken({ const refreshToken = this.generateRefreshToken({
userId: user.id.toString(), userId: user.id.toString(),
}); });
@ -159,4 +171,72 @@ export class AuthService implements IAuthService {
return Result.fail(error as Error); return Result.fail(error as Error);
} }
} }
/**
*
* Autentica a un usuario validando su email y contraseña.
*/
async logoutUser(params: { email: EmailAddress; tabId: UniqueID }): Promise<Result<void, Error>> {
try {
return await this._transactionManager.complete(async (transaction) => {
const { email, tabId } = params;
// Verificar que el tab ID está definido
if (!tabId.isDefined()) {
return Result.fail(new Error("Invalid tab id"));
}
// 🔹 Verificar si el usuario existe en la base de datos
const userResult = await this._userRepo.getUserByEmail(email, transaction);
if (userResult.isFailure) {
return Result.fail(new Error("Invalid email or password"));
}
const user = userResult.data;
const contextOrError = TabContext.create({
userId: user.id,
tabId: tabId,
});
if (contextOrError.isFailure) {
return Result.fail(new Error("Error creating user context"));
}
// Desregistrar el contexto de ese tab ID
await this._tabContactRepo.unregisterContextByTabId(contextOrError.data, transaction);
return Result.ok();
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
async verifyUser(params: {
email: EmailAddress;
plainPassword: PlainPassword;
}): Promise<Result<AuthenticatedUser, Error>> {
try {
return await this._transactionManager.complete(async (transaction) => {
const { email, plainPassword } = params;
const userResult = await this._userRepo.getUserByEmail(email, transaction);
if (userResult.isFailure || !userResult.data) {
return Result.fail(new Error("Invalid email or password"));
}
const user = userResult.data;
const isValidPassword = await user.verifyPassword(plainPassword);
if (!isValidPassword) {
return Result.fail(new Error("Invalid email or password"));
}
return Result.ok(user);
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
} }

View File

@ -1,14 +1,11 @@
import { createSequelizeTransactionManager } from "@common/infrastructure"; import { createSequelizeTransactionManager } from "@common/infrastructure";
import { createAuthenticatedUserRepository, createTabContextRepository } from "../infraestructure"; import { createAuthenticatedUserRepository, createTabContextRepository } from "../infraestructure";
import { createPassportAuthProvider } from "../infraestructure/passport/passport-auth-provider";
import { IAuthProvider } from "./auth-provider.interface";
import { IAuthService } from "./auth-service.interface"; import { IAuthService } from "./auth-service.interface";
import { AuthService } from "./auth.service"; import { AuthService } from "./auth.service";
import { ITabContextService } from "./tab-context-service.interface"; import { ITabContextService } from "./tab-context-service.interface";
import { TabContextService } from "./tab-context.service"; import { TabContextService } from "./tab-context.service";
export * from "./auth-provider.interface";
export * from "./auth-service.interface"; export * from "./auth-service.interface";
export const createAuthService = (): IAuthService => { export const createAuthService = (): IAuthService => {
@ -16,16 +13,7 @@ export const createAuthService = (): IAuthService => {
const authenticatedUserRepository = createAuthenticatedUserRepository(); const authenticatedUserRepository = createAuthenticatedUserRepository();
const tabContextRepository = createTabContextRepository(); const tabContextRepository = createTabContextRepository();
const authProvider: IAuthProvider = createPassportAuthProvider( return new AuthService(authenticatedUserRepository, tabContextRepository, transactionManager);
authenticatedUserRepository,
transactionManager
);
return new AuthService(
authenticatedUserRepository,
tabContextRepository,
transactionManager,
authProvider
);
}; };
export const createTabContextService = (): ITabContextService => { export const createTabContextService = (): ITabContextService => {

View File

@ -3,12 +3,6 @@ import { TabContext } from "../domain";
export interface ITabContextService { export interface ITabContextService {
getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>; getContextByTabId(tabId: UniqueID): Promise<Result<TabContext, Error>>;
createContext(params: { createContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<TabContext, Error>>;
tabId: UniqueID; removeContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<void, Error>>;
userId: UniqueID;
companyId: UniqueID;
branchId: UniqueID;
}): Promise<Result<TabContext, Error>>;
assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>>;
removeContext(tabId: UniqueID): Promise<Result<void, Error>>;
} }

View File

@ -42,10 +42,8 @@ export class TabContextService implements ITabContextService {
async createContext(params: { async createContext(params: {
tabId: UniqueID; tabId: UniqueID;
userId: UniqueID; userId: UniqueID;
companyId: UniqueID;
branchId: UniqueID;
}): Promise<Result<TabContext, Error>> { }): Promise<Result<TabContext, Error>> {
const { tabId, userId, companyId, branchId } = params; const { tabId, userId } = params;
if (!userId || !tabId) { if (!userId || !tabId) {
return Result.fail(new Error("User ID and Tab ID are required")); return Result.fail(new Error("User ID and Tab ID are required"));
@ -57,8 +55,6 @@ export class TabContextService implements ITabContextService {
{ {
userId, userId,
tabId, tabId,
companyId,
branchId,
}, },
UniqueID.generateNewID().data UniqueID.generateNewID().data
); );
@ -67,7 +63,7 @@ export class TabContextService implements ITabContextService {
return Result.fail(contextOrError.error); return Result.fail(contextOrError.error);
} }
await this._respository.registerContext(contextOrError.data, transaction); await this._respository.registerContextByTabId(contextOrError.data, transaction);
return Result.ok(contextOrError.data); return Result.ok(contextOrError.data);
}); });
@ -76,39 +72,31 @@ export class TabContextService implements ITabContextService {
} }
} }
/**
* Asigna una empresa activa a un contexto de pestaña
*/
async assignCompany(tabId: UniqueID, companyId: UniqueID): Promise<Result<void, Error>> {
if (!companyId || !tabId) {
return Result.fail(new Error("Tab ID and Company ID are required"));
}
try {
return await this._transactionManager.complete(async (transaction) => {
// Verificar si la pestaña existe
const tabContextOrError = await this._respository.contextExists(tabId, transaction);
if (tabContextOrError.isFailure || !tabContextOrError.data) {
return Result.fail(new Error("Invalid or expired Tab ID"));
}
return await this._respository.updateCompanyByTabId(tabId, companyId, transaction);
});
} catch (error: unknown) {
return Result.fail(error as Error);
}
}
/** /**
* Elimina un contexto de pestaña por su ID * Elimina un contexto de pestaña por su ID
*/ */
async removeContext(tabId: UniqueID): Promise<Result<void, Error>> { async removeContext(params: { tabId: UniqueID; userId: UniqueID }): Promise<Result<void, Error>> {
if (!tabId) { const { tabId, userId } = params;
return Result.fail(new Error("Tab ID is required"));
if (!userId || !tabId) {
return Result.fail(new Error("User ID and Tab ID are required"));
} }
try { try {
return await this._transactionManager.complete(async (transaction) => { return await this._transactionManager.complete(async (transaction) => {
return await this._respository.deleteContextByTabId(tabId, transaction); const contextOrError = TabContext.create(
{
userId,
tabId,
},
UniqueID.generateNewID().data
);
if (contextOrError.isFailure) {
return Result.fail(contextOrError.error);
}
return await this._respository.unregisterContextByTabId(contextOrError.data, transaction);
}); });
} catch (error: unknown) { } catch (error: unknown) {
return Result.fail(error as Error); return Result.fail(error as Error);

View File

@ -3,18 +3,11 @@ import { DomainEntity, Result, UniqueID } from "@common/domain";
export interface ITabContextProps { export interface ITabContextProps {
tabId: UniqueID; tabId: UniqueID;
userId: UniqueID; userId: UniqueID;
companyId: UniqueID;
branchId: UniqueID;
} }
export interface ITabContext { export interface ITabContext {
tabId: UniqueID; tabId: UniqueID;
userId: UniqueID; userId: UniqueID;
companyId: UniqueID;
branchId: UniqueID;
hasCompanyAssigned(): boolean;
hasBranchAssigned(): boolean;
toPersistenceData(): any; toPersistenceData(): any;
} }
@ -32,22 +25,6 @@ export class TabContext extends DomainEntity<ITabContextProps> implements ITabCo
return this._props.userId; return this._props.userId;
} }
get companyId(): UniqueID {
return this._props.companyId;
}
get branchId(): UniqueID {
return this._props.branchId;
}
hasCompanyAssigned(): boolean {
return this._props.companyId.isDefined();
}
hasBranchAssigned(): boolean {
return this._props.branchId.isDefined();
}
/** /**
* 🔹 Devuelve una representación lista para persistencia * 🔹 Devuelve una representación lista para persistencia
*/ */
@ -56,8 +33,6 @@ export class TabContext extends DomainEntity<ITabContextProps> implements ITabCo
id: this._id.toString(), id: this._id.toString(),
tab_id: this.tabId.toString(), tab_id: this.tabId.toString(),
user_id: this.userId.toString(), user_id: this.userId.toString(),
company_id: this.companyId.toString(),
branch_id: this.branchId.toString(),
}; };
} }
} }

View File

@ -4,12 +4,16 @@ import { TabContext } from "../entities";
export interface ITabContextRepository { export interface ITabContextRepository {
getContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<TabContext, Error>>; getContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<TabContext, Error>>;
registerContext(context: TabContext, transaction?: Transaction): Promise<Result<void, Error>>;
contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>>; contextExistsByTabId(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
updateCompanyByTabId(
tabId: UniqueID, registerContextByTabId(
companyId: UniqueID, context: TabContext,
transaction?: Transaction
): Promise<Result<void, Error>>;
unregisterContextByTabId(
context: TabContext,
transaction?: Transaction transaction?: Transaction
): Promise<Result<void, Error>>; ): Promise<Result<void, Error>>;
deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>>;
} }

View File

@ -1 +1,9 @@
export * from "./passport-auth-provider"; import { createAuthService } from "@contexts/auth/application";
import { PassportAuthProvider } from "./passport-auth-provider";
export const createPassportAuthProvider = () => {
const _authService = createAuthService();
return new PassportAuthProvider(_authService);
};
export const initializePassportAuthProvide = () => createPassportAuthProvider();

View File

@ -1,38 +1,32 @@
import { createSequelizeTransactionManager } from "@common/infrastructure"; import { IAuthService } from "@contexts/auth/application";
import { ITransactionManager } from "@common/infrastructure/database"; import { AuthenticatedUser, EmailAddress, PlainPassword } from "@contexts/auth/domain";
import { IAuthProvider } from "@contexts/auth/application";
import {
AuthenticatedUser,
EmailAddress,
IAuthenticatedUserRepository,
PlainPassword,
} from "@contexts/auth/domain";
import jwt from "jsonwebtoken";
import passport from "passport"; import passport from "passport";
import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt"; import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt";
import { Strategy as LocalStrategy } from "passport-local"; import { Strategy as LocalStrategy } from "passport-local";
import { createAuthenticatedUserRepository } from "../sequelize";
const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey"; const SECRET_KEY = process.env.JWT_SECRET || "supersecretkey";
const ACCESS_EXPIRATION = process.env.JWT_ACCESS_EXPIRATION || "1h";
const REFRESH_EXPIRATION = process.env.JWT_REFRESH_EXPIRATION || "7d";
export class PassportAuthProvider implements IAuthProvider { export class PassportAuthProvider {
private readonly _repository: IAuthenticatedUserRepository; private readonly _authService: IAuthService;
private readonly _transactionManager!: ITransactionManager;
private async _verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> { private async _verifyUser(email: string, password: string): Promise<AuthenticatedUser | null> {
const emailVO = EmailAddress.create(email); const emailVO = EmailAddress.create(email);
if (emailVO.isFailure) return Promise.resolve(null); if (emailVO.isFailure) return Promise.resolve(null);
const passwordVO = PlainPassword.create(password); const plainPasswordVO = PlainPassword.create(password);
if (passwordVO.isFailure) return Promise.resolve(null); if (plainPasswordVO.isFailure) return Promise.resolve(null);
const userResult = await this._repository.getUserByEmail(emailVO.data); const userResult = await this._authService.verifyUser({
if (userResult.isFailure || !userResult.data) return Promise.resolve(null); email: emailVO.data,
plainPassword: plainPasswordVO.data,
});
if (userResult.isFailure || !userResult.data) {
return Promise.resolve(null);
}
const user = userResult.data; const user = userResult.data;
const isValidPassword = await user.verifyPassword(passwordVO.data); const isValidPassword = await user.verifyPassword(plainPasswordVO.data);
return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user); return !isValidPassword ? Promise.resolve(null) : Promise.resolve(user);
} }
@ -50,6 +44,7 @@ export class PassportAuthProvider implements IAuthProvider {
"jwt", "jwt",
new JwtStrategy(jwtOptions, (tokenPayload, done) => { new JwtStrategy(jwtOptions, (tokenPayload, done) => {
try { try {
console.log(tokenPayload);
return done(null, tokenPayload); return done(null, tokenPayload);
} catch (error) { } catch (error) {
return done(error, false); return done(error, false);
@ -77,32 +72,8 @@ export class PassportAuthProvider implements IAuthProvider {
passport.initialize(); passport.initialize();
} }
constructor(repository: IAuthenticatedUserRepository, transactionManager: ITransactionManager) { constructor(authService: IAuthService) {
this._repository = repository; this._authService = authService;
this._transactionManager = transactionManager;
this.initializePassport(); this.initializePassport();
} }
generateAccessToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: String(ACCESS_EXPIRATION) });
}
generateRefreshToken(payload: object): string {
return jwt.sign(payload, SECRET_KEY, { expiresIn: REFRESH_EXPIRATION });
}
verifyToken(token: string): any {
return jwt.verify(token, SECRET_KEY);
}
} }
export const createPassportAuthProvider = (
repository?: IAuthenticatedUserRepository,
transactionManager?: ITransactionManager
) => {
const _transactionManager = transactionManager || createSequelizeTransactionManager();
const _repository = repository || createAuthenticatedUserRepository();
return new PassportAuthProvider(_repository, _transactionManager);
};
export const initializePassportAuthProvide = () => createPassportAuthProvider();

View File

@ -2,7 +2,7 @@ import { Result, UniqueID } from "@common/domain";
import { SequelizeRepository } from "@common/infrastructure"; import { SequelizeRepository } from "@common/infrastructure";
import { TabContext } from "@contexts/auth/domain/"; import { TabContext } from "@contexts/auth/domain/";
import { ITabContextRepository } from "@contexts/auth/domain/repositories/tab-context-repository.interface"; import { ITabContextRepository } from "@contexts/auth/domain/repositories/tab-context-repository.interface";
import { Transaction } from "sequelize"; import { Op, Transaction } from "sequelize";
import { createTabContextMapper, ITabContextMapper } from "../mappers"; import { createTabContextMapper, ITabContextMapper } from "../mappers";
import { TabContextModel } from "./tab-context.model"; import { TabContextModel } from "./tab-context.model";
@ -51,7 +51,7 @@ export class TabContextRepository
} }
} }
async contextExists(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> { async contextExistsByTabId(tabId: UniqueID, transaction?: any): Promise<Result<boolean, Error>> {
try { try {
const result: any = await this._exists( const result: any = await this._exists(
TabContextModel, TabContextModel,
@ -67,57 +67,57 @@ export class TabContextRepository
} }
/** /**
* Crea un contexto para un tab id o actualiza si ya existe * Registra un contexto para un tab id o actualiza si ya existe
* @param context * @param context
* @param transaction * @param transaction
* @returns * @returns
*/ */
async registerContext( async registerContextByTabId(
context: TabContext, context: TabContext,
transaction?: Transaction transaction?: Transaction
): Promise<Result<void, Error>> { ): Promise<Result<void, Error>> {
try { try {
const { id } = context; const { userId, tabId } = context;
const persistenceData = this._mapper.toPersistence(context); const data = this._mapper.toPersistence(context);
// Si existe el contexto de ese tabId, lo actualizo.
if (await this._exists(TabContextModel, "tab_id", tabId.toString())) {
await TabContextModel.update(data, {
where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
transaction,
});
} else {
await TabContextModel.create(data, {
include: [{ all: true }],
transaction,
});
}
await this._save(TabContextModel, id, persistenceData, {}, transaction);
return Result.ok(); return Result.ok();
} catch (error: any) { } catch (error: any) {
return this._handleDatabaseError(error, this._customErrorMapper); return this._handleDatabaseError(error, this._customErrorMapper);
} }
} }
async updateCompanyByTabId( /**
tabId: UniqueID, * Desregistra un contexto para un tab id o actualiza si ya existe
companyId: UniqueID, * @param context
* @param transaction
* @returns
*/
async unregisterContextByTabId(
context: TabContext,
transaction?: Transaction transaction?: Transaction
): Promise<Result<void, Error>> { ): Promise<Result<void, Error>> {
try { try {
await TabContextModel.update( const { userId, tabId } = context;
{ company_id: companyId.toString() },
{
where: {
tab_id: tabId.toString(),
},
transaction,
}
);
return Result.ok();
} catch (error: any) {
return this._handleDatabaseError(error, this._customErrorMapper);
}
}
async deleteContextByTabId(tabId: UniqueID, transaction?: any): Promise<Result<void, Error>> {
try {
await TabContextModel.destroy({ await TabContextModel.destroy({
where: { where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
tab_id: tabId.toString(),
},
transaction, transaction,
force: false,
}); });
return Result.ok(); return Result.ok();
} catch (error: any) { } catch (error: any) {

View File

@ -1,6 +1,7 @@
import { UniqueID } from "@common/domain"; import { Result, UniqueID } from "@common/domain";
import { ExpressController } from "@common/presentation"; import { ExpressController } from "@common/presentation";
import { createAuthService, IAuthService } from "@contexts/auth/application"; import { createAuthService, IAuthService } from "@contexts/auth/application";
import { EmailAddress } from "@contexts/auth/domain";
class LogoutController extends ExpressController { class LogoutController extends ExpressController {
private readonly _authService!: IAuthService; private readonly _authService!: IAuthService;
@ -12,22 +13,21 @@ class LogoutController extends ExpressController {
async executeImpl() { async executeImpl() {
const tabId = this.req.headers["x-tab-id"]; const tabId = this.req.headers["x-tab-id"];
const emailVO = EmailAddress.create(this.req.body.email);
const tabIdVO = UniqueID.create(String(tabId)); const tabIdVO = UniqueID.create(String(tabId));
if (tabIdVO.isFailure) { const resultValidation = Result.combine([emailVO, tabIdVO]);
return this.clientError("Invalid tab id", [tabIdVO.error]);
if (resultValidation.isFailure) {
return this.clientError("Invalid input data", resultValidation.error);
} }
const userOrError = await this._authService.logoutUser({ await this._authService.logoutUser({
email: emailVO.data, email: emailVO.data,
plainPassword: plainPasswordVO.data,
tabId: tabIdVO.data, tabId: tabIdVO.data,
}); });
if (userOrError.isFailure) {
return this.unauthorizedError(userOrError.error.message);
}
return this.ok(); return this.ok();
} }
} }

View File

@ -1,9 +1,10 @@
import { validateRequest } from "@common/presentation"; import { validateRequest } from "@common/presentation";
import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation"; import { validateTabContextHeader, validateUser } from "@contexts/auth/presentation";
import { createLoginController } from "@contexts/auth/presentation/controllers"; import { createLoginController } from "@contexts/auth/presentation/controllers";
import { createLogoutController } from "@contexts/auth/presentation/controllers/logout/logout.controller";
import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller"; import { createRegisterController } from "@contexts/auth/presentation/controllers/register/register.controller";
import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto"; import { LoginUserSchema, RegisterUserSchema } from "@contexts/auth/presentation/dto";
import { Router } from "express"; import { NextFunction, Request, Response, Router } from "express";
export const authRouter = (appRouter: Router) => { export const authRouter = (appRouter: Router) => {
const authRoutes: Router = Router({ mergeParams: true }); const authRoutes: Router = Router({ mergeParams: true });
@ -45,7 +46,7 @@ export const authRouter = (appRouter: Router) => {
"/login", "/login",
validateRequest(LoginUserSchema), validateRequest(LoginUserSchema),
validateTabContextHeader, validateTabContextHeader,
(req, res, next) => { (req: Request, res: Response, next: NextFunction) => {
createLoginController().execute(req, res, next); createLoginController().execute(req, res, next);
} }
); );
@ -61,10 +62,14 @@ export const authRouter = (appRouter: Router) => {
* *
* @apiSuccess (200) {String} message Success message. * @apiSuccess (200) {String} message Success message.
*/ */
authRoutes.post("/logout", validateUser, validateTabContextHeader, (req, res, next) => { authRoutes.post(
res.sendStatus(200); "/logout",
//createLogoutController().execute(req, res, next); validateUser,
}); validateTabContextHeader,
(req: Request, res: Response, next: NextFunction) => {
createLogoutController().execute(req, res, next);
}
);
appRouter.use("/auth", authRoutes); appRouter.use("/auth", authRoutes);
}; };