This commit is contained in:
David Arranz 2024-05-16 13:56:46 +02:00
parent 9264739af9
commit 173ec9e8d8
37 changed files with 366 additions and 485 deletions

View File

@ -79,7 +79,6 @@
"remove": "^0.1.5", "remove": "^0.1.5",
"response-time": "^2.3.2", "response-time": "^2.3.2",
"sequelize": "^6.37.3", "sequelize": "^6.37.3",
"sequelize-revision": "^6.0.0",
"sequelize-typescript": "^2.1.5", "sequelize-typescript": "^2.1.5",
"shallow-equal-object": "^1.1.1", "shallow-equal-object": "^1.1.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",

View File

@ -0,0 +1,88 @@
import {
IUseCase,
IUseCaseError,
IUseCaseRequest,
UseCaseError,
handleUseCaseError,
} from "@/contexts/common/application";
import { IRepositoryManager } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { Result, ensureUserEmailIsValid } from "@shared/contexts";
import { User } from "../domain";
import { findUserByEmail } from "./authServices";
export interface FindUserByEmailRequest extends IUseCaseRequest {
email: string;
}
export type FindUserByEmailResponseOrError =
| Result<never, IUseCaseError>
| Result<User, never>;
export class FindUserByEmailUseCase
implements
IUseCase<FindUserByEmailRequest, Promise<FindUserByEmailResponseOrError>>
{
private _adapter: ISequelizeAdapter;
private _repositoryManager: IRepositoryManager;
constructor(props: {
adapter: ISequelizeAdapter;
repositoryManager: IRepositoryManager;
}) {
this._adapter = props.adapter;
this._repositoryManager = props.repositoryManager;
}
private getRepositoryByName<T>(name: string) {
return this._repositoryManager.getRepository<T>(name);
}
async execute(
request: FindUserByEmailRequest,
): Promise<FindUserByEmailResponseOrError> {
const { email } = request;
// Validaciones de datos
const emailOrError = ensureUserEmailIsValid(email);
if (emailOrError.isFailure) {
return Result.fail(
handleUseCaseError(
UseCaseError.INVALID_INPUT_DATA,
"Email or password is not valid",
emailOrError.error,
),
);
}
// Crear auth
try {
const user = await findUserByEmail(
emailOrError.object,
this._adapter,
this.getRepositoryByName("Auth"),
);
if (user === null) {
return Result.fail(
handleUseCaseError(
UseCaseError.NOT_FOUND_ERROR,
`User with email ${email} not found`,
),
);
}
return Result.ok<User>(user);
} catch (error: unknown) {
const _error = error as IInfrastructureError;
return Result.fail(
handleUseCaseError(
UseCaseError.REPOSITORY_ERROR,
"Error al buscar el usuario",
_error,
),
);
}
}
}

View File

@ -7,14 +7,9 @@ import {
import { IRepositoryManager } from "@/contexts/common/domain"; import { IRepositoryManager } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure"; import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize"; import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { import { ILogin_DTO, Result, ensureUserEmailIsValid } from "@shared/contexts";
Email,
ILogin_DTO,
Result,
ensureUserEmailIsValid,
} from "@shared/contexts";
import { User } from "../domain"; import { User } from "../domain";
import { IAuthRepository } from "../domain/repository"; import { findUserByEmail } from "./authServices";
export type LoginResponseOrError = export type LoginResponseOrError =
| Result<never, IUseCaseError> | Result<never, IUseCaseError>
@ -56,7 +51,11 @@ export class LoginUseCase
// Crear auth // Crear auth
try { try {
const user = await this.findUserEmail(emailOrError.object); const user = await findUserByEmail(
emailOrError.object,
this._adapter,
this.getRepositoryByName("Auth"),
);
if (user === null || !user.verifyPassword(password)) { if (user === null || !user.verifyPassword(password)) {
return Result.fail( return Result.fail(
handleUseCaseError( handleUseCaseError(
@ -77,18 +76,4 @@ export class LoginUseCase
); );
} }
} }
private async findUserEmail(email: Email): Promise<User | null> {
const transaction = this._adapter.startTransaction();
const authRepoBuilder = this.getRepositoryByName<IAuthRepository>("Auth");
let user: User | null = null;
await transaction.complete(async (t) => {
const authRepo = authRepoBuilder({ transaction: t });
user = await authRepo.findByEmail(email);
});
return user;
}
} }

View File

@ -0,0 +1,18 @@
import { IAdapter, RepositoryBuilder } from "@/contexts/common/domain";
import { Email } from "@shared/contexts";
import { User } from "../domain";
import { IAuthRepository } from "../domain/repository";
export const findUserByEmail = async (
email: Email,
adapter: IAdapter,
repository: RepositoryBuilder<IAuthRepository>,
): Promise<User | null> => {
const user = await adapter
.startTransaction()
.complete(async (t) =>
repository({ transaction: t }).findUserByEmail(email),
);
return user;
};

View File

@ -1 +1,2 @@
export * from "./authServices"; export * from "./FindUserByEmail.useCase";
export * from "./Login.useCase";

View File

@ -4,11 +4,13 @@ import {
AggregateRoot, AggregateRoot,
Email, Email,
IDomainError, IDomainError,
Name,
Result, Result,
UniqueID, UniqueID,
} from "@shared/contexts"; } from "@shared/contexts";
export interface IUserProps { export interface IUserProps {
name: Name;
email: Email; email: Email;
password?: string; password?: string;
hashed_password?: string; hashed_password?: string;
@ -16,6 +18,7 @@ export interface IUserProps {
export interface IUser { export interface IUser {
id: UniqueID; id: UniqueID;
name: Name;
email: Email; email: Email;
hashed_password: string; hashed_password: string;
@ -51,6 +54,10 @@ export class User extends AggregateRoot<IUserProps> implements IUser {
this._protectPassword(props); this._protectPassword(props);
} }
get name(): Name {
return this.props.name;
}
get email(): Email { get email(): Email {
return this.props.email; return this.props.email;
} }

View File

@ -1,8 +1,7 @@
import { IRepository } from "@/contexts/common/domain"; import { IRepository } from "@/contexts/common/domain";
import { Email, UniqueID } from "@shared/contexts"; import { Email } from "@shared/contexts";
import { User } from "../entities"; import { User } from "../entities";
export interface IAuthRepository extends IRepository<any> { export interface IAuthRepository extends IRepository<any> {
getById(id: UniqueID): Promise<User | null>; findUserByEmail(email: Email): Promise<User | null>;
findByEmail(email: Email): Promise<User | null>;
} }

View File

@ -41,7 +41,7 @@ export class AuthRepository
return this.mapper.mapToDomain(rawUser); return this.mapper.mapToDomain(rawUser);
} }
public async findByEmail(email: Email): Promise<User | null> { public async findUserByEmail(email: Email): Promise<User | null> {
const rawUser: any = await this._getBy( const rawUser: any = await this._getBy(
"User_Model", "User_Model",
"email", "email",

View File

@ -1,4 +1,5 @@
// Import the necessary packages and modules // Import the necessary packages and modules
import { User } from "@/contexts/auth/domain";
import { IServerError } from "@/contexts/common/domain/errors"; import { IServerError } from "@/contexts/common/domain/errors";
import { ExpressController } from "@/contexts/common/infrastructure/express"; import { ExpressController } from "@/contexts/common/infrastructure/express";
import passport from "passport"; import passport from "passport";
@ -41,7 +42,7 @@ export class AuthenticateController extends ExpressController {
{ session: false }, { session: false },
( (
err: any, err: any,
user?: Express.User | false | null, user?: User | false | null,
info?: object | string | Array<string | undefined>, info?: object | string | Array<string | undefined>,
status?: number | Array<number | undefined>, status?: number | Array<number | undefined>,
) => { ) => {

View File

@ -1,108 +0,0 @@
import { IUseCaseError, UseCaseError } from "@/contexts/common/application";
import { IServerError } from "@/contexts/common/domain/errors";
import {
IInfrastructureError,
InfrastructureError,
handleInfrastructureError,
} from "@/contexts/common/infrastructure";
import { ExpressController } from "@/contexts/common/infrastructure/express";
import { ILogin_DTO, ensureLogin_DTOIsValid } from "@shared/contexts";
export class LoginController extends ExpressController {
private useCase: LoginUseCase;
private presenter: ILoginPresenter;
private context: IAuthContext;
constructor(
props: {
useCase: LoginUseCase;
presenter: ILoginPresenter;
},
context: IAuthContext,
) {
super();
const { useCase, presenter } = props;
this.useCase = useCase;
this.presenter = presenter;
this.context = context;
}
async executeImpl() {
try {
const loginDTO: ILogin_DTO = this.req.body;
// Validaciones de DTO
const loginDTOOrError = ensureLogin_DTOIsValid(loginDTO);
if (loginDTOOrError.isFailure) {
const errorMessage = "Login data not valid";
const infraError = handleInfrastructureError(
InfrastructureError.INVALID_INPUT_DATA,
errorMessage,
loginDTOOrError.error,
);
return this.invalidInputError(errorMessage, infraError);
}
const result = await this.useCase.execute();
if (result.isFailure) {
return this._handleExecuteError(result.error);
}
console.log("login OK => generate token JWT");
const customer = <Customer>result.object;
return this.created<ICreateCustomer_Response_DTO>(
this.presenter.map(customer, this.context),
);
} catch (e: unknown) {
return this.fail(e as IServerError);
}
}
private _handleExecuteError(error: IUseCaseError) {
let errorMessage: string;
let infraError: IInfrastructureError;
switch (error.code) {
case UseCaseError.INVALID_INPUT_DATA:
errorMessage = "Login data not valid";
infraError = handleInfrastructureError(
InfrastructureError.INVALID_INPUT_DATA,
errorMessage,
error,
);
return this.invalidInputError(errorMessage, infraError);
break;
case UseCaseError.NOT_FOUND_ERROR:
errorMessage = "User not found";
infraError = handleInfrastructureError(
InfrastructureError.INVALID_INPUT_DATA,
errorMessage,
error,
);
return this.conflictError(error.message, error);
break;
case UseCaseError.UNEXCEPTED_ERROR:
errorMessage = error.message;
infraError = handleInfrastructureError(
InfrastructureError.UNEXCEPTED_ERROR,
errorMessage,
error,
);
return this.internalServerError(errorMessage, infraError);
break;
default:
errorMessage = error.message;
return this.clientError(errorMessage);
}
}
}

View File

@ -1,20 +1 @@
import { AuthenticateController } from "./AuthenticateController"; export * from "./login";
const authenticate = (context: any) => {
//const adapter = context.adapter;
//const repoManager = context.repositoryManager;
/*repoManager.registerRepository("Auth", (params = { transaction: null }) => {
const { transaction } = params;
return new AuthRepository({
transaction,
adapter,
mapper: createAuthMapper(context),
});
});*/
//const listArticlesUseCase = new ListArticlesUseCase(context);
return new AuthenticateController();
};

View File

@ -0,0 +1,71 @@
import { config } from "@/config";
import { User } from "@/contexts/auth/domain";
import { IServerError } from "@/contexts/common/domain/errors";
import {
InfrastructureError,
handleInfrastructureError,
} from "@/contexts/common/infrastructure";
import { ExpressController } from "@/contexts/common/infrastructure/express";
import { ILogin_Response_DTO } from "@shared/contexts";
import JWT from "jsonwebtoken";
import { IAuthContext } from "../../../Auth.context";
import { ILoginPresenter, ILoginUser } from "./presenter";
export class LoginController extends ExpressController {
private presenter: ILoginPresenter;
private context: IAuthContext;
constructor(
props: {
presenter: ILoginPresenter;
},
context: IAuthContext,
) {
super();
const { presenter } = props;
this.presenter = presenter;
this.context = context;
}
async executeImpl() {
try {
const user = <User>this.req.user;
if (!user) {
const errorMessage = "Unexpected missing user data";
const infraError = handleInfrastructureError(
InfrastructureError.UNEXCEPTED_ERROR,
errorMessage,
);
return this.internalServerError(errorMessage, infraError);
}
const loginUser: ILoginUser = {
user,
token: this._generateUserToken(user),
refreshToken: this._generateUserRefreshToken(user),
};
return this.ok<ILogin_Response_DTO>(
this.presenter.map(loginUser, this.context),
);
} catch (e: unknown) {
return this.fail(e as IServerError);
}
}
private _generateUserToken(user: User) {
return JWT.sign({ email: user.email.toString() }, config.jwt.secret_key, {
expiresIn: config.jwt.token_expiration,
});
}
private _generateUserRefreshToken(user: User) {
return JWT.sign(
{ email: user.email.toString() },
config.jwt.refresh_secret_key,
{ expiresIn: config.jwt.refresh_token_expiration },
);
}
}

View File

@ -0,0 +1,13 @@
import { IAuthContext } from "../../../Auth.context";
import { LoginController } from "./Login.controller";
import { loginPresenter } from "./presenter";
export const createLoginController = (context: IAuthContext) => {
return new LoginController(
{
//useCase: listArticlesUseCase,
presenter: loginPresenter,
},
context,
);
};

View File

@ -0,0 +1,29 @@
import { IUser } from "@/contexts/auth/domain";
import { IAuthContext } from "@/contexts/auth/infrastructure/Auth.context";
import { ILogin_Response_DTO } from "@shared/contexts";
export interface ILoginUser {
user: IUser;
token: string;
refreshToken: string;
}
export interface ILoginPresenter {
map: (user: ILoginUser, context: IAuthContext) => ILogin_Response_DTO;
}
export const loginPresenter: ILoginPresenter = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
map: (loginUser: ILoginUser, context: IAuthContext): ILogin_Response_DTO => {
const { user, token, refreshToken } = loginUser;
return {
id: user.id.toString(),
name: user.name.toString(),
email: user.email.toString(),
roles: ["ROLE_USER"],
token,
refresh_token: refreshToken,
};
},
};

View File

@ -0,0 +1 @@
export * from "./Login.presenter";

View File

@ -1,9 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import Express from "express";
import passport from "passport"; import passport from "passport";
export const authenticate = ( export const isLoggedUser = passport.authenticate("local-jwt", {
session: false,
});
/*export const authenticate = (
req: Express.Request, req: Express.Request,
res: Express.Response, res: Express.Response,
next: Express.NextFunction, next: Express.NextFunction,
@ -14,7 +17,7 @@ export const authenticate = (
{ session: false }, { session: false },
( (
err: any, err: any,
user?: Express.User | false | null, user?: User | false | null,
info?: object | string | Array<string | undefined>, info?: object | string | Array<string | undefined>,
status?: number | Array<number | undefined>, status?: number | Array<number | undefined>,
) => { ) => {
@ -32,3 +35,6 @@ export const authenticate = (
}, },
)(req, res, next); )(req, res, next);
}; };
*/

View File

@ -1,11 +1,10 @@
import { PassportStatic } from "passport"; import { PassportStatic } from "passport";
import { AuthContext } from "../../Auth.context"; import { AuthContext } from "../../Auth.context";
import { initEmailStrategy } from "./emailStrategy"; import { initEmailStrategy } from "./emailStrategy";
import { jwtStrategy } from "./jwtStrategy"; import { initJWTStrategy } from "./jwtStrategy";
// Export a function that will be used to configure Passport authentication // Export a function that will be used to configure Passport authentication
export const configurePassportAuth = (passport: PassportStatic) => { export const configurePassportAuth = (passport: PassportStatic) => {
console.log("passport: configuring strategies !!!!!!!!!!!!!!!!!!");
passport.use("local-email", initEmailStrategy(AuthContext.getInstance())); passport.use("local-email", initEmailStrategy(AuthContext.getInstance()));
passport.use("local-jwt", jwtStrategy); passport.use("local-jwt", initJWTStrategy(AuthContext.getInstance()));
}; };

View File

@ -1,9 +1,10 @@
import { LoginUseCase } from "@/contexts/auth/application/LoginUseCase";
import { IServerError } from "@/contexts/common/domain/errors"; import { IServerError } from "@/contexts/common/domain/errors";
import { PassportStrategyController } from "@/contexts/common/infrastructure/express"; import { PassportStrategyController } from "@/contexts/common/infrastructure/express";
import { ensureLogin_DTOIsValid } from "@shared/contexts"; import { ensureLogin_DTOIsValid } from "@shared/contexts";
import { Strategy as EmailStrategy, IVerifyOptions } from "passport-local"; import { Strategy as EmailStrategy, IVerifyOptions } from "passport-local";
import { LoginUseCase } from "@/contexts/auth/application";
import { User } from "@/contexts/auth/domain";
import { IAuthContext } from "../../Auth.context"; import { IAuthContext } from "../../Auth.context";
import { registerAuthRepository } from "../../Auth.repository"; import { registerAuthRepository } from "../../Auth.repository";
@ -32,11 +33,7 @@ class EmailStrategyController extends PassportStrategyController {
public async verifyStrategy( public async verifyStrategy(
email: string, email: string,
password: string, password: string,
done: ( done: (error: any, user?: User | false, options?: IVerifyOptions) => void,
error: any,
user?: Express.User | false,
options?: IVerifyOptions,
) => void,
) { ) {
const loginDTOOrError = ensureLogin_DTOIsValid({ email, password }); const loginDTOOrError = ensureLogin_DTOIsValid({ email, password });

View File

@ -1,2 +1,2 @@
export * from "./authenticate"; export * from "./authMiddleware";
export * from "./configurePassportAuth"; export * from "./configurePassportAuth";

View File

@ -1,27 +1,60 @@
import { config } from "@/config"; import { config } from "@/config";
import { ExtractJwt, Strategy as JWTStrategy } from "passport-jwt"; import { FindUserByEmailUseCase } from "@/contexts/auth/application/FindUserByEmail.useCase";
import { IServerError } from "@/contexts/common/domain/errors";
import { PassportStrategyController } from "@/contexts/common/infrastructure/express";
import {
ExtractJwt,
Strategy as JWTStrategy,
VerifiedCallback,
} from "passport-jwt";
import { IAuthContext } from "../../Auth.context";
import { registerAuthRepository } from "../../Auth.repository";
const strategyOpts = { const strategyOpts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // Extract the JWT from the Authorization header jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // Extract the JWT from the Authorization header
secretOrKey: config.jwt.secret_key, secretOrKey: config.jwt.secret_key,
}; };
export const jwtStrategy = new JWTStrategy( class JWTStrategyController extends PassportStrategyController {
strategyOpts, private useCase: FindUserByEmailUseCase;
async (jwt_payload, done) => { private context: IAuthContext;
console.log(
"PASSPORT USE LOCAL-JWT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
);
console.log(jwt_payload);
/* constructor(
const user = await authenticateUserByEmail("assa@asds.com", "password"); props: {
if (user) { useCase: FindUserByEmailUseCase;
return done(null, user); },
} else { context: any,
return done(null, false); ) {
super();
const { useCase } = props;
this.useCase = useCase;
this.context = context;
}
public async verifyStrategy(payload: any, done: VerifiedCallback) {
const { email } = payload;
try {
const result = await this.useCase.execute({ email });
if (result.isFailure) {
return done(null, false);
}
return done(null, result.object);
} catch (e: unknown) {
return done(e as IServerError);
} }
*/ }
return done(null, { id: "xzxxxxx", email: "assa@asds.com" }); }
},
); export const initJWTStrategy = (context: IAuthContext) =>
new JWTStrategy(strategyOpts, async (...params) => {
registerAuthRepository(context);
return new JWTStrategyController(
{
useCase: new FindUserByEmailUseCase(context),
},
context,
).verifyStrategy(...params);
});

View File

@ -1,184 +0,0 @@
import {
InfrastructureError,
handleInfrastructureError,
} from "@/contexts/common/infrastructure";
import { ensureLogin_DTOIsValid } from "@shared/contexts";
import passport from "passport";
import { ExtractJwt, Strategy as JwtStrategy } from "passport-jwt";
import { Strategy as LocalStrategy } from "passport-local";
/*declare global {
namespace Express {
interface User {
id?: string;
email: string;
password: string;
}
}
}*/
//import { authenticateUserByEmail, deserializeUserById } from './../../../services';
const authenticateUserByEmail = async (
email: string,
password: string,
): Promise<Express.User | Error> => {
// Simulación de búsqueda del usuario en la base de datos
const user = {
id: "1",
email: "usuario@example.com",
password: "$2a$10$EdfkgQ7vMTGvXq1qI7qJQ.g3WiYGqCSCdzhZ/LG20YKwA1YjgLJnO",
}; // Contraseña hasheada: "password"
return user;
/*const res = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
if (res.rows.length) {
const user = res.rows[0];
const match = await bcrypt.compare(password, user.password);
if (match) {
return user;
} else {
throw new Error('Incorrect email and/or password');
}
} else {
throw new Error('User not found');
}*/
};
const deserializeUserById = async (
id: number,
): Promise<Express.User | Error> => {
/*const res = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
if (res.rows.length) {
return res.rows[0];
} else {
throw new Error('User was not found');
}*/
const user = {
id: "1",
email: "usuario@example.com",
password: "$2a$10$EdfkgQ7vMTGvXq1qI7qJQ.g3WiYGqCSCdzhZ/LG20YKwA1YjgLJnO",
}; // Contraseña hasheada: "password"
return user;
};
// Configurar la estrategia de autenticación local de Passport.js
passport.use(
"local-email",
new LocalStrategy(
{
usernameField: "email", // Campo utilizado para el email en el formulario
passwordField: "password",
},
async (email, password, done) => {
console.log("local-email");
const loginDTO = { email, password };
const loginDTOOrError = ensureLogin_DTOIsValid(loginDTO);
if (loginDTOOrError.isFailure) {
const errorMessage = "Login data not valid";
const infraError = handleInfrastructureError(
InfrastructureError.INVALID_INPUT_DATA,
errorMessage,
loginDTOOrError.error,
);
done(infraError);
}
const result = await this.useCase.execute();
try {
// Buscar el usuario en la base de datos por email
const user = await authenticateUserByEmail(email, password);
return done(null, user as Express.User);
} catch (error) {
return done(error);
}
},
),
);
passport.use(
"local-jwt",
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: "secret",
},
async (jwt_payload, done) => {
console.log(jwt_payload);
const user = await authenticateUserByEmail("assa@asds.com", "password");
return done(null, user);
},
),
);
/*passport.use(
"jwt2",
new CustomStrategy(async (req, done) => {
const token =
req && req.headers && req.headers["x-access-token"]
? req.headers["x-access-token"]
: null;
const appVersion =
req && req.headers && req.headers["accept-version"]
? req.headers["accept-version"]
: null;
console.log("appVersion: ", appVersion);
if (!token) {
console.error("Unauthorized. Token missing.");
return done(null, false, { message: "Unauthorized. Token missing." });
}
const result = securityHelper.verify(token);
//console.log('token result => ', result);
if (result && result.id) {
//recuperamos el usuario de la petición
let user = await authService.extraMethods.findUser({ id: result.id });
if (user) {
user = user.toJSON();
userService._updateLastLoginAndVersionUser(user.id, appVersion);
user.app_version = appVersion;
user.token = token;
delete user.password;
console.log("Logged in Successfully");
console.log(user);
return done(null, user, { message: "Logged in Successfully" });
} else {
console.error("Unauthorized. User not found.");
return done(null, false, { message: "Unauthorized. User not found." });
}
} else {
//console.log('Token no válido');
console.error("Unauthorized. Invalid token.");
return done(null, false, { message: "Unauthorized. Invalid token." });
}
}),
);*/
// Serializar y deserializar el usuario para almacenar y recuperar la sesión
passport.serializeUser((user: Express.User, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id: number, done) => {
try {
const user = await deserializeUserById(id);
done(null, user as Express.User);
} catch (error) {
done(error, null);
}
});
export { passport };

View File

@ -1,8 +1,8 @@
import { config } from "@/config"; import { registerMiddleware } from "@/contexts/common/infrastructure/express";
import Express from "express"; import Express from "express";
import JWT from "jsonwebtoken";
import passport from "passport"; import passport from "passport";
import { User } from "../../domain"; import { createLoginController } from "./controllers";
import { isLoggedUser } from "./passport";
/*authRoutes.post( /*authRoutes.post(
"/login", "/login",
@ -40,48 +40,13 @@ authRoutes.get(
export const AuthRouter = (appRouter: Express.Router) => { export const AuthRouter = (appRouter: Express.Router) => {
const authRoutes: Express.Router = Express.Router({ mergeParams: true }); const authRoutes: Express.Router = Express.Router({ mergeParams: true });
//appRouter.use(registerMiddleware("authenticate", authenticate)); appRouter.use(registerMiddleware("isLoggedUser", isLoggedUser));
authRoutes.post( authRoutes.post(
"/login", "/login",
passport.authenticate("local-email", { session: false }), passport.authenticate("local-email", { session: false }),
(req, res, next) => {
if (req.isAuthenticated()) {
const user: User = req.user;
const accessToken = JWT.sign(
{ id: user.id, email: user.email },
config.jwt.secret_key,
{ expiresIn: config.jwt.token_expiration },
);
const refreshToken = JWT.sign(
{ id: user.id, email: user.email },
config.jwt.refresh_secret_key,
{ expiresIn: config.jwt.refresh_token_expiration },
);
//refreshTokens.push(refreshToken);
return res.json({ accessToken, refreshToken });
}
return res.status(401).json({});
},
);
authRoutes.post(
"/login2",
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => (req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
passport.authenticate( createLoginController(res.locals["context"]).execute(req, res, next),
"local-email",
{ session: false },
(err, user, info) => {
console.log(err, user, info);
next(err);
},
)(req, res, next),
(req, res, next) => {
res.status(200).json({});
},
); );
appRouter.use("/auth", authRoutes); appRouter.use("/auth", authRoutes);

View File

@ -2,7 +2,7 @@ import {
ISequelizeMapper, ISequelizeMapper,
SequelizeMapper, SequelizeMapper,
} from "@/contexts/common/infrastructure"; } from "@/contexts/common/infrastructure";
import { Email, UniqueID } from "@shared/contexts"; import { Email, Name, UniqueID } from "@shared/contexts";
import { IUserProps, User } from "../../domain/entities"; import { IUserProps, User } from "../../domain/entities";
import { IAuthContext } from "../Auth.context"; import { IAuthContext } from "../Auth.context";
import { TCreationUser_Attributes, User_Model } from "../sequelize/user.model"; import { TCreationUser_Attributes, User_Model } from "../sequelize/user.model";
@ -20,6 +20,7 @@ class UserMapper
protected toDomainMappingImpl(source: User_Model, params: any): User { protected toDomainMappingImpl(source: User_Model, params: any): User {
const props: IUserProps = { const props: IUserProps = {
name: this.mapsValue(source, "name", Name.create),
email: this.mapsValue(source, "email", Email.create), email: this.mapsValue(source, "email", Email.create),
hashed_password: source.password, hashed_password: source.password,
}; };
@ -40,6 +41,7 @@ class UserMapper
) { ) {
return { return {
id: source.id.toPrimitive(), id: source.id.toPrimitive(),
name: source.name.toPrimitive(),
email: source.email.toPrimitive(), email: source.email.toPrimitive(),
password: source.hashed_password, password: source.hashed_password,
}; };

View File

@ -20,6 +20,7 @@ export class User_Model extends Model<
static associate(connection: Sequelize) {} static associate(connection: Sequelize) {}
declare id: string; declare id: string;
declare name: string;
declare email: string; declare email: string;
declare password: string; declare password: string;
} }
@ -32,10 +33,15 @@ export default (sequelize: Sequelize) => {
primaryKey: true, primaryKey: true,
}, },
name: {
type: DataTypes.STRING,
},
email: { email: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
password: { password: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
@ -45,15 +51,15 @@ export default (sequelize: Sequelize) => {
sequelize, sequelize,
tableName: "users", tableName: "users",
//paranoid: true, // softs deletes paranoid: true, // softs deletes
//timestamps: true, timestamps: true,
//version: true, //version: true,
//createdAt: "created_at", createdAt: "created_at",
//updatedAt: "updated_at", updatedAt: "updated_at",
//deletedAt: "deleted_at", deletedAt: "deleted_at",
indexes: [{ name: "email_idx", unique: true, fields: ["email"] }], indexes: [{ name: "email_idx", fields: ["email"] }],
}, },
); );

View File

@ -13,7 +13,7 @@ export const CatalogRouter = (appRouter: Express.Router) => {
catalogRoutes.get( catalogRoutes.get(
"/", "/",
applyMiddleware("authenticate"), applyMiddleware("isLoggedUser"),
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => (req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
createListArticlesController(res.locals["context"]).execute( createListArticlesController(res.locals["context"]).execute(
req, req,

View File

@ -1,4 +1,4 @@
import { TBusinessTransaction } from "./BusinessTransaction.interface"; import { TBusinessTransaction } from "./BusinessTransaction";
export interface IAdapter { export interface IAdapter {
startTransaction: () => TBusinessTransaction; startTransaction: () => TBusinessTransaction;

View File

@ -1,5 +0,0 @@
type TUnitOfWork = {
start(): unknown;
};
export type TBusinessTransaction = TUnitOfWork;

View File

@ -0,0 +1,4 @@
export type TBusinessTransaction = {
start(): void;
complete<T>(work: (...params: any[]) => Promise<T>): Promise<T>;
};

View File

@ -1,5 +1,5 @@
export * from "./Adapter.interface"; export * from "./Adapter.interface";
export * from "./BusinessTransaction.interface"; export * from "./BusinessTransaction";
export * from "./Repository.interface"; export * from "./Repository.interface";
export * from "./RepositoryBuilder"; export * from "./RepositoryBuilder";
export * from "./RepositoryManager"; export * from "./RepositoryManager";

View File

@ -18,7 +18,7 @@ export class InfrastructureError
public static create( public static create(
code: string, code: string,
message: string, message: string,
payload?: Record<string, any> payload?: Record<string, any>,
): InfrastructureError { ): InfrastructureError {
return new InfrastructureError(code, message, payload); return new InfrastructureError(code, message, payload);
} }
@ -31,16 +31,17 @@ function _isJoiError(error: Error) {
export function handleInfrastructureError( export function handleInfrastructureError(
code: string, code: string,
message: string, message: string,
error: Error // UseCaseError | ValidationError error?: Error, // UseCaseError | ValidationError
): IInfrastructureError { ): IInfrastructureError {
let payload = {}; let payload = {};
if (_isJoiError(error)) { if (error) {
//Joi => error.details if (_isJoiError(error)) {
payload = (<ValidationError>error).details; //Joi => error.details
} else { payload = (<ValidationError>error).details;
// UseCaseError } else {
/*const useCaseError = <UseCaseError>error; // UseCaseError
/*const useCaseError = <UseCaseError>error;
if (useCaseError.payload.path) { if (useCaseError.payload.path) {
const errorItem = {}; const errorItem = {};
errorItem[`${useCaseError.payload.path}`] = useCaseError.message; errorItem[`${useCaseError.payload.path}`] = useCaseError.message;
@ -48,7 +49,8 @@ export function handleInfrastructureError(
errors: [errorItem], errors: [errorItem],
}; };
}*/ }*/
payload = (<UseCaseError>error).payload; payload = (<UseCaseError>error).payload;
}
} }
console.log(payload); console.log(payload);

View File

@ -40,7 +40,7 @@ export class FirebirdAdapter implements IFirebirdAdapter {
protected constructor( protected constructor(
connection: Firebird.ConnectionPool, connection: Firebird.ConnectionPool,
queryBuilder: IRepositoryQueryBuilder queryBuilder: IRepositoryQueryBuilder,
) { ) {
this._connection = connection; this._connection = connection;
this._queryBuilder = queryBuilder; this._queryBuilder = queryBuilder;

View File

@ -1,11 +1,11 @@
import Firebird from "node-firebird"; import Firebird from "node-firebird";
import { TBusinessTransaction } from "../../domain/repositories"; import { IBusinessTransaction } from "../../domain/repositories";
import { InfrastructureError } from "../InfrastructureError"; import { InfrastructureError } from "../InfrastructureError";
export type FirebirdBusinessTransactionType = TBusinessTransaction & { export type FirebirdBusinessTransactionType = IBusinessTransaction & {
start: (a: unknown) => unknown; start: (a: unknown) => unknown;
complete( complete(
work: (t: Firebird.Transaction, db: Firebird.Database) => unknown work: (t: Firebird.Transaction, db: Firebird.Database) => unknown,
): void; ): void;
}; };
@ -23,7 +23,7 @@ export class FirebirdBusinessTransaction
} }
public complete( public complete(
work: (t: Firebird.Transaction, db?: Firebird.Database) => unknown work: (t: Firebird.Transaction, db?: Firebird.Database) => unknown,
): void { ): void {
this._connection.get((err, db: Firebird.Database) => { this._connection.get((err, db: Firebird.Database) => {
if (err) { if (err) {
@ -36,7 +36,7 @@ export class FirebirdBusinessTransaction
if (err) { if (err) {
InfrastructureError.create( InfrastructureError.create(
InfrastructureError.UNEXCEPTED_ERROR, InfrastructureError.UNEXCEPTED_ERROR,
err err,
); );
} }
@ -48,7 +48,7 @@ export class FirebirdBusinessTransaction
} finally { } finally {
db.detach(); db.detach();
} }
} },
); );
}); });
} }

View File

@ -1,7 +1,6 @@
import { config } from "@/config"; import { config } from "@/config";
import rTracer from "cls-rtracer"; import rTracer from "cls-rtracer";
import { DataTypes, Sequelize } from "sequelize"; import { DataTypes, Sequelize } from "sequelize";
import { SequelizeRevision } from "sequelize-revision";
import { initLogger } from "@/infrastructure/logger"; import { initLogger } from "@/infrastructure/logger";
import * as glob from "glob"; import * as glob from "glob";
@ -43,38 +42,23 @@ export class SequelizeAdapter implements ISequelizeAdapter {
const { queryBuilder } = params; const { queryBuilder } = params;
const connection = initConnection(); const connection = initConnection();
const sequelizeRevision = new SequelizeRevision(connection, { const models = registerModels(connection);
UUID: true,
tableName: "revisions",
//changeTableName: "",
underscored: true,
underscoredAttributes: true,
});
const models = registerModels(connection, sequelizeRevision);
return new SequelizeAdapter( return new SequelizeAdapter(connection, models, queryBuilder);
connection,
models,
queryBuilder,
sequelizeRevision
);
} }
private _connection: Sequelize; private _connection: Sequelize;
private _models: ISequelizeModels; private _models: ISequelizeModels;
private _queryBuilder: ISequelizeQueryBuilder; private _queryBuilder: ISequelizeQueryBuilder;
private _revisions: SequelizeRevision<any>;
protected constructor( protected constructor(
connection: Sequelize, connection: Sequelize,
models: ISequelizeModels, models: ISequelizeModels,
queryBuilder: ISequelizeQueryBuilder, queryBuilder: ISequelizeQueryBuilder,
revisions: SequelizeRevision<any>
) { ) {
this._connection = connection; this._connection = connection;
this._models = models; this._models = models;
this._queryBuilder = queryBuilder; this._queryBuilder = queryBuilder;
this._revisions = revisions;
} }
get queryBuilder(): ISequelizeQueryBuilder { get queryBuilder(): ISequelizeQueryBuilder {
@ -95,7 +79,7 @@ export class SequelizeAdapter implements ISequelizeAdapter {
} }
throw InfrastructureError.create( throw InfrastructureError.create(
InfrastructureError.RESOURCE_NOT_FOUND_ERROR, InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
`[SequelizeAdapter] ${modelName} sequelize model not exists!` `[SequelizeAdapter] ${modelName} sequelize model not exists!`,
); );
} }
@ -139,10 +123,7 @@ function initConnection(): Sequelize {
}); });
} }
function registerModels( function registerModels(connection: Sequelize): ISequelizeModels {
connection: Sequelize,
sequelizeRevision: SequelizeRevision<any>
): ISequelizeModels {
const cwd = path.resolve(`${__dirname}/../../../`); const cwd = path.resolve(`${__dirname}/../../../`);
const models: ISequelizeModels = {}; const models: ISequelizeModels = {};
@ -164,18 +145,9 @@ function registerModels(
if (model) models[model.name] = model; if (model) models[model.name] = model;
}); });
// Register revisions models
const [Revision, RevisionChanges] = sequelizeRevision.defineModels();
//models[Revision.name] = Revision;
//models[RevisionChanges.name] = RevisionChanges;
for (const modelName in models) { for (const modelName in models) {
const model = models[modelName]; const model = models[modelName];
if (model.trackRevision) {
model.trackRevision(connection, sequelizeRevision);
}
if (model.associate) { if (model.associate) {
model.associate(connection, models); model.associate(connection, models);
} }

View File

@ -1,9 +1,8 @@
import { Sequelize, Transaction } from "sequelize"; import { Sequelize, Transaction } from "sequelize";
import { TBusinessTransaction } from "../../domain/repositories"; import { TBusinessTransaction } from "../../domain";
import { InfrastructureError } from "../InfrastructureError"; import { InfrastructureError } from "../InfrastructureError";
export type SequelizeBusinessTransactionType = TBusinessTransaction & { export type SequelizeBusinessTransactionType = TBusinessTransaction & {
start(): void;
complete<T>(work: (t: Transaction) => Promise<T>): Promise<T>; complete<T>(work: (t: Transaction) => Promise<T>): Promise<T>;
}; };
@ -57,7 +56,7 @@ export class SequelizeBusinessTransaction
throw InfrastructureError.create( throw InfrastructureError.create(
InfrastructureError.UNEXCEPTED_ERROR, InfrastructureError.UNEXCEPTED_ERROR,
(error as Error).message (error as Error).message,
); );
} }
} }

View File

@ -1,5 +1,4 @@
import { Model, Sequelize } from "sequelize"; import { Model, Sequelize } from "sequelize";
import { SequelizeRevision } from "sequelize-revision";
interface ISequelizeModel extends Model {} interface ISequelizeModel extends Model {}
@ -9,10 +8,6 @@ interface ISequelizeModels {
interface ISequelizeModel extends Model { interface ISequelizeModel extends Model {
associate?: (connection: Sequelize, models?: ISequelizeModels) => void; associate?: (connection: Sequelize, models?: ISequelizeModels) => void;
hooks?: (connection: Sequelize) => void; hooks?: (connection: Sequelize) => void;
trackRevision?: (
connection: Sequelize,
sequelizeRevision: SequelizeRevision<any>
) => void;
} }
export { ISequelizeModel, ISequelizeModels }; export { ISequelizeModel, ISequelizeModels };

View File

@ -1,3 +1,8 @@
export interface ILogin_Response_DTO { export interface ILogin_Response_DTO {
id: string;
name: string;
email: string;
roles: string[];
token: string; token: string;
refresh_token: string;
} }

View File

@ -33,7 +33,7 @@ export class QuickSearchCriteria
} }
} }
return Result.ok(String(searchString)); return Result.ok(searchString);
} }
public static create(value: UndefinedOr<string>) { public static create(value: UndefinedOr<string>) {
@ -49,7 +49,7 @@ export class QuickSearchCriteria
} }
private static sanitize(searchTerm: UndefinedOr<string>): string { private static sanitize(searchTerm: UndefinedOr<string>): string {
return String(Joi.string().trim().validate(searchTerm).value); return String(Joi.string().default("").trim().validate(searchTerm).value);
} }
get searchTerm(): string { get searchTerm(): string {