Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions semana22/revm5/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
build/
.env
.vscode
8 changes: 8 additions & 0 deletions semana22/revm5/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
roots: ["<rootDir>/tests"],
transform: {
"^.+\\.tsx?$": "ts-jest",
},
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};
32 changes: 32 additions & 0 deletions semana22/revm5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "revisao-modulo5",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "tsc && node ./build/src/index.js",
"test": "jest"
},
"author": "VINNIVSO",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"jest": "^26.0.1",
"knex": "^0.21.2",
"moment": "^2.27.0",
"mysql": "^2.18.1",
"uuid": "^8.2.0"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/express": "^4.17.7",
"@types/jest": "^25.2.3",
"@types/jsonwebtoken": "^8.5.0",
"@types/knex": "^0.16.1",
"@types/uuid": "^8.0.0",
"ts-jest": "^26.1.0",
"typescript": "^3.9.6"
}
}
59 changes: 59 additions & 0 deletions semana22/revm5/src/business/UserBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { UserInputDTO, LoginInputDTO } from "./entities/User";
import { UserDatabase } from "../data/UserDatabase";
import { IdGenerator } from "./services/IdGenerator";
import { HashManager } from "./services/HashManager";
import { Authenticator } from "./services/Authenticator";
import { CustomError } from "./error/CustomError";

export class UserBusiness {

constructor(
private idGenerator: IdGenerator,
private hashManager: HashManager,
private authenticator: Authenticator,
private userDatabase: UserDatabase,
) { }

async createUser(user: UserInputDTO) {

const id = this.idGenerator.generate();

const hashPassword = await this.hashManager.hash(user.password);

await this.userDatabase.createUser(
id,
user.email,
user.name,
hashPassword,
user.role
);

const accessToken = this.authenticator.generateToken({
id,
role: user.role
});

return accessToken;
}

async getUserByEmail(user: LoginInputDTO) {

const userFromDB = await this.userDatabase.getUserByEmail(user.email);

const passwordIsCorrect = await this.hashManager.compare(
user.password,
userFromDB.password
);

const accessToken = this.authenticator.generateToken({
id: userFromDB.id,
role: userFromDB.role
});

if (!passwordIsCorrect) {
throw new CustomError(401, "Invalid credentials!");
}

return accessToken;
}
}
45 changes: 45 additions & 0 deletions semana22/revm5/src/business/entities/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { CustomError } from "../error/CustomError";

export class User {
constructor(
public readonly id: string,
public readonly name: string,
public readonly email: string,
public readonly password: string,
public readonly role: UserRole
) { }


static stringToUserRole(input: string): UserRole {
switch (input) {
case "NORMAL":
return UserRole.NORMAL;
case "ADMIN":
return UserRole.ADMIN;
default:
throw new CustomError(422,"Invalid user role");
}
}
}

export interface UserInputDTO {
email: string;
password: string;
name: string;
role: string;
}

export interface LoginInputDTO {
email: string;
password: string;
}

export enum UserRole {
NORMAL = "NORMAL",
ADMIN = "ADMIN"
}

export interface AuthenticationData {
id: string;
role?: string;
}
8 changes: 8 additions & 0 deletions semana22/revm5/src/business/error/CustomError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class CustomError extends Error {
constructor(
public readonly statusCode: number,
message: string
) {
super(message);
}
}
38 changes: 38 additions & 0 deletions semana22/revm5/src/business/services/Authenticator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as jwt from "jsonwebtoken";
import { AuthenticationData } from "../entities/User";
import dotenv from "dotenv";

dotenv.config();

export class Authenticator {

public generateToken(
input: AuthenticationData,
expiresIn: string = process.env.JWT_EXPIRES_IN!
): string {
const token = jwt.sign(
input,
process.env.JWT_KEY as string,
{
expiresIn,
}
);
return token;
}

public getData(
token: string
): AuthenticationData {

const payload = jwt.verify(
token, process.env.JWT_KEY as string
) as AuthenticationData;

const result = {
id: payload.id,
role: payload.role
};

return result;
}
}
23 changes: 23 additions & 0 deletions semana22/revm5/src/business/services/HashManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as bcrypt from "bcryptjs";
import dotenv from "dotenv";

dotenv.config();

export class HashManager {

private rounds:number = Number(process.env.BCRYPT_COST)

public async hash(text: string): Promise<string> {
const salt = await bcrypt.genSalt(this.rounds);
const result = await bcrypt.hash(text, salt);
return result;
}

public async compare(
text: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(text, hash);
}

}
8 changes: 8 additions & 0 deletions semana22/revm5/src/business/services/IdGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { v4 } from "uuid";

export class IdGenerator{

generate(): string{
return v4();
}
}
58 changes: 58 additions & 0 deletions semana22/revm5/src/controller/UserController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Request, Response } from "express";
import { UserInputDTO, LoginInputDTO } from "../business/entities/User";
import { Authenticator } from "../business/services/Authenticator";
import { HashManager } from "../business/services/HashManager";
import { IdGenerator } from "../business/services/IdGenerator";
import { UserBusiness } from "../business/UserBusiness";
import { UserDatabase } from "../data/UserDatabase";

const userBusiness = new UserBusiness(
new IdGenerator(),
new HashManager,
new Authenticator(),
new UserDatabase()
);

export class UserController {
async signup(req: Request, res: Response) {
try {

const input: UserInputDTO = {
email: req.body.email,
name: req.body.name,
password: req.body.password,
role: req.body.role
}

const token = await userBusiness.createUser(input);

res.status(200).send({ token });

} catch (error:any) {
res
.status(error.statusCode || 400)
.send({ error: error.message });
}
}

async login(req: Request, res: Response) {

try {

const loginData: LoginInputDTO = {
email: req.body.email,
password: req.body.password
};

const token = await userBusiness.getUserByEmail(loginData);

res.status(200).send({ token });

} catch (error: any) {
res
.status(error.statusCode || 400)
.send({ error: error.message });
}
}

}
10 changes: 10 additions & 0 deletions semana22/revm5/src/controller/routes/userRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import express from "express";
import { UserController } from "../UserController";


export const userRouter = express.Router();

const userController = new UserController();

userRouter.post("/signup", userController.signup);
userRouter.post("/login", userController.login);
19 changes: 19 additions & 0 deletions semana22/revm5/src/data/BaseDatabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import knex from "knex";
import Knex from "knex";
import dotenv from "dotenv";

dotenv.config();

export class BaseDatabase {

protected static connection: Knex = knex({
client: "mysql",
connection: {
host: process.env.DB_HOST,
port: 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
});
}
53 changes: 53 additions & 0 deletions semana22/revm5/src/data/UserDatabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { BaseDatabase } from "./BaseDatabase";
import { User } from "../business/entities/User";
import { CustomError } from "../business/error/CustomError";

export class UserDatabase extends BaseDatabase {

private static TABLE_NAME = "user";

private static toUserModel(user: any): User {
return new User(
user.id,
user.name,
user.email,
user.password,
User.stringToUserRole(user.role)
);
}

public async createUser(
id: string,
email: string,
name: string,
password: string,
role: string
): Promise<void> {
try {
await BaseDatabase.connection
.insert({
id,
email,
name,
password,
role
})
.into(UserDatabase.TABLE_NAME);
} catch (error) {
throw new CustomError(500, "An unexpected error ocurred");
}
}

public async getUserByEmail(email: string): Promise<User> {
try {
const result = await BaseDatabase.connection
.select("*")
.from(UserDatabase.TABLE_NAME)
.where({ email });

return UserDatabase.toUserModel(result[0]);
} catch (error) {
throw new CustomError(500, "An unexpected error ocurred");
}
}
}
Loading