├── .gitignore ├── src ├── validation │ ├── protocols │ │ ├── index.ts │ │ └── email-validator.ts │ └── validators │ │ ├── index.ts │ │ ├── required-field-validation.ts │ │ ├── validation-composite.ts │ │ ├── compare-fields-validation.ts │ │ └── email-validation.ts ├── presentation │ ├── protocols │ │ ├── index.ts │ │ ├── validation.ts │ │ ├── controller.ts │ │ └── http.ts │ ├── errors │ │ ├── not-found-error.ts │ │ ├── unauthorized-error.ts │ │ ├── index.ts │ │ ├── invalid-param-error.ts │ │ ├── missing-param-error.ts │ │ └── server-error.ts │ ├── controllers │ │ ├── get-all │ │ │ ├── get-all-controller-protocols.ts │ │ │ └── get-all-controller.ts │ │ ├── login │ │ │ ├── login-controller-protocols.ts │ │ │ └── login-controller.ts │ │ ├── delete │ │ │ ├── delete-controller-protocols.ts │ │ │ └── delete-controller.ts │ │ ├── signup │ │ │ ├── signup-controller-protocols.ts │ │ │ └── signup-controller.ts │ │ ├── update │ │ │ ├── update-controller-protocols.ts │ │ │ └── update-controller.ts │ │ └── get-by-id │ │ │ ├── get-by-id-controller-protocols.ts │ │ │ └── get-by-id-controller.ts │ └── helpers │ │ └── http │ │ └── http-helper.ts ├── main │ ├── middlewares │ │ ├── body-parser.ts │ │ ├── index.ts │ │ ├── content-type.ts │ │ └── cors.ts │ ├── config │ │ ├── env.ts │ │ ├── app.ts │ │ ├── middlewares.ts │ │ └── routes.ts │ ├── server.ts │ ├── factories │ │ ├── controllers │ │ │ └── user │ │ │ │ ├── get-all │ │ │ │ └── get-all-controller-factory.ts │ │ │ │ ├── delete │ │ │ │ ├── delete-validation-factory.ts │ │ │ │ └── delete-controller-factory.ts │ │ │ │ ├── get-by-id │ │ │ │ ├── get-by-id-validation-factory.ts │ │ │ │ └── get-by-id-controller-factory.ts │ │ │ │ ├── login │ │ │ │ ├── login-controller-factory.ts │ │ │ │ └── login-validation-factory.ts │ │ │ │ ├── update │ │ │ │ ├── update-controller-factory.ts │ │ │ │ └── update-validation-factory.ts │ │ │ │ └── signup │ │ │ │ ├── signup-controller-factory.ts │ │ │ │ └── signup-validation-factory.ts │ │ └── usecases │ │ │ ├── get-all-account │ │ │ └── db-get-all-account-factory.ts │ │ │ ├── delete-account │ │ │ └── db-delete-account-factory.ts │ │ │ ├── get-account-by-id │ │ │ └── db-get-account-by-id-factory.ts │ │ │ ├── create-account │ │ │ └── db-create-account-factory.ts │ │ │ ├── update-account │ │ │ └── db-update-account-factory.ts │ │ │ └── authentication │ │ │ └── db-authentication-factory.ts │ ├── adapters │ │ └── express │ │ │ └── express-route-adapter.ts │ └── routes │ │ └── account-routes.ts ├── data │ ├── protocols │ │ ├── criptography │ │ │ ├── encrypter.ts │ │ │ ├── hasher.ts │ │ │ ├── decrypter.ts │ │ │ ├── hash-comparer.ts │ │ │ └── index.ts │ │ └── db │ │ │ ├── account │ │ │ ├── update-access-token-repository.ts │ │ │ ├── get-account-by-id-repository.ts │ │ │ ├── check-account-by-id-repository.ts │ │ │ ├── check-account-by-email-repository.ts │ │ │ ├── delete-account-repository.ts │ │ │ ├── create-account-repository.ts │ │ │ ├── update-account-repository.ts │ │ │ └── get-all-account-repository.ts │ │ │ └── index.ts │ └── usecases │ │ ├── authentication │ │ ├── db-authentication-protocols.ts │ │ └── db-authentication.ts │ │ ├── get-all-account │ │ ├── db-get-all-account-protocols.ts │ │ └── db-get-all-account.ts │ │ ├── delete-account │ │ ├── db-delete-account-protocols.ts │ │ └── db-delete-account.ts │ │ ├── get-account-by-id │ │ ├── db-get-account-by-id-protocols.ts │ │ └── db-get-account-by-id.ts │ │ ├── create-account │ │ ├── db-create-account-protocols.ts │ │ └── db-create-account.ts │ │ └── update-account │ │ ├── db-update-account-protocols.ts │ │ └── db-update-account.ts ├── domain │ ├── models │ │ └── account.ts │ └── usecases │ │ └── user │ │ ├── delete-account.ts │ │ ├── authentication.ts │ │ ├── index.ts │ │ ├── create-account.ts │ │ ├── get-all-account.ts │ │ ├── get-account-by-id.ts │ │ └── update-account.ts └── infra │ ├── db │ └── mssqldb │ │ ├── helpers │ │ └── connect.ts │ │ ├── database │ │ └── migrations │ │ │ └── 20211202200348_create_table_users.ts │ │ └── models │ │ └── account-repository │ │ └── account-repository.ts │ ├── validators │ └── email-validator-adapter.ts │ └── criptography │ ├── jwt-adapter.ts │ └── bcrypt-adapter.ts ├── knexfile.ts ├── seguir.txt ├── tsconfig.json ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /src/validation/protocols/index.ts: -------------------------------------------------------------------------------- 1 | export * from './email-validator' 2 | -------------------------------------------------------------------------------- /src/presentation/protocols/index.ts: -------------------------------------------------------------------------------- 1 | export * from './http' 2 | export * from './controller' -------------------------------------------------------------------------------- /src/main/middlewares/body-parser.ts: -------------------------------------------------------------------------------- 1 | import { json } from 'express' 2 | 3 | export const bodyParser = json() 4 | -------------------------------------------------------------------------------- /src/presentation/protocols/validation.ts: -------------------------------------------------------------------------------- 1 | export interface Validation { 2 | validate(input: any): Error 3 | } -------------------------------------------------------------------------------- /src/main/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body-parser' 2 | export * from './content-type' 3 | export * from './cors' -------------------------------------------------------------------------------- /src/data/protocols/criptography/encrypter.ts: -------------------------------------------------------------------------------- 1 | export interface Encrypter { 2 | encrypt(value: number): Promise 3 | } -------------------------------------------------------------------------------- /src/data/protocols/criptography/hasher.ts: -------------------------------------------------------------------------------- 1 | export interface Hasher { 2 | hash(plaintext: string): Promise 3 | } 4 | -------------------------------------------------------------------------------- /src/validation/protocols/email-validator.ts: -------------------------------------------------------------------------------- 1 | export interface EmailValidator { 2 | isValid: (email: string) => boolean 3 | } 4 | -------------------------------------------------------------------------------- /src/data/protocols/criptography/decrypter.ts: -------------------------------------------------------------------------------- 1 | export interface Decrypter { 2 | decrypt(ciphertext: string): Promise 3 | } 4 | -------------------------------------------------------------------------------- /src/main/config/env.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | port: process.env.PORT || 8080, 3 | jwtSecret: process.env.JWT_SECRET || 'batata7711=@@' 4 | } -------------------------------------------------------------------------------- /src/data/protocols/criptography/hash-comparer.ts: -------------------------------------------------------------------------------- 1 | export interface HashComparer { 2 | compare(value: string, hash: string): Promise 3 | } -------------------------------------------------------------------------------- /src/domain/models/account.ts: -------------------------------------------------------------------------------- 1 | export interface AccountModel { 2 | id: number 3 | name: string 4 | email: string 5 | password: string 6 | } -------------------------------------------------------------------------------- /src/presentation/errors/not-found-error.ts: -------------------------------------------------------------------------------- 1 | export class NotFoundError extends Error { 2 | constructor() { 3 | super('NotFound') 4 | this.name = 'NotFoundError' 5 | } 6 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/update-access-token-repository.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateAccessTokenRepository { 2 | updateAccessToken(id: number, token: string): Promise 3 | } 4 | -------------------------------------------------------------------------------- /src/presentation/protocols/controller.ts: -------------------------------------------------------------------------------- 1 | import { HttpRequest, HttpResponse } from './' 2 | 3 | export interface Controller { 4 | handle(httpRequest: HttpRequest): Promise 5 | } -------------------------------------------------------------------------------- /src/presentation/errors/unauthorized-error.ts: -------------------------------------------------------------------------------- 1 | export class UnauthorizedError extends Error { 2 | constructor() { 3 | super('Unauthorized') 4 | this.name = 'UnauthorizedError' 5 | } 6 | } -------------------------------------------------------------------------------- /src/presentation/protocols/http.ts: -------------------------------------------------------------------------------- 1 | export interface HttpResponse { 2 | statusCode: number 3 | body: any 4 | } 5 | 6 | export interface HttpRequest { 7 | body?: any 8 | params?: any 9 | } -------------------------------------------------------------------------------- /src/data/protocols/criptography/index.ts: -------------------------------------------------------------------------------- 1 | export * from './decrypter' 2 | export * from './encrypter' 3 | export * from './hash-comparer' 4 | export * from './hasher' 5 | export * from './encrypter' 6 | -------------------------------------------------------------------------------- /src/presentation/controllers/get-all/get-all-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../../domain/usecases/user' 3 | export * from '../../../domain/models/account' 4 | -------------------------------------------------------------------------------- /src/presentation/controllers/login/login-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../protocols/validation' 3 | export * from '../../../domain/usecases/user/authentication' -------------------------------------------------------------------------------- /src/validation/validators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './compare-fields-validation' 2 | export * from './email-validation' 3 | export * from './required-field-validation' 4 | export * from './validation-composite' 5 | -------------------------------------------------------------------------------- /src/domain/usecases/user/delete-account.ts: -------------------------------------------------------------------------------- 1 | export interface DeleteAccountModel { 2 | email: string 3 | } 4 | 5 | export interface DeleteAccount { 6 | delete(deleteAccountModel: DeleteAccountModel): Promise 7 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/get-account-by-id-repository.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../../../domain/models/account' 2 | 3 | export interface GetAccountByIdRepository { 4 | getById(id: number): Promise 5 | } -------------------------------------------------------------------------------- /src/main/server.ts: -------------------------------------------------------------------------------- 1 | import app from './config/app' 2 | import connection from '../infra/db/mssqldb/helpers/connect' 3 | 4 | connection() 5 | app.listen(8080, () => console.log(`🔥 Server running at http://localhost:8080 🔥`)) 6 | -------------------------------------------------------------------------------- /src/presentation/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './invalid-param-error' 2 | export * from './missing-param-error' 3 | export * from './server-error' 4 | export * from './unauthorized-error' 5 | export * from './not-found-error' 6 | -------------------------------------------------------------------------------- /src/presentation/errors/invalid-param-error.ts: -------------------------------------------------------------------------------- 1 | export class InvalidParamError extends Error { 2 | constructor(paramName: string) { 3 | super(`Invalid param: ${paramName}`); 4 | this.name = 'InvalidParamError' 5 | } 6 | } -------------------------------------------------------------------------------- /src/presentation/errors/missing-param-error.ts: -------------------------------------------------------------------------------- 1 | export class MissingParamError extends Error { 2 | constructor(paramName: string) { 3 | super(`Missing param: ${paramName}`) 4 | this.name = 'MissingParamError' 5 | } 6 | } -------------------------------------------------------------------------------- /src/presentation/errors/server-error.ts: -------------------------------------------------------------------------------- 1 | export class ServerError extends Error { 2 | constructor(stack?: string) { 3 | super('Internal server error') 4 | this.name = 'ServerError' 5 | this.stack = stack 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/middlewares/content-type.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express' 2 | 3 | export const contentType = (req: Request, res: Response, next: NextFunction): void => { 4 | res.type('json') 5 | next() 6 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/check-account-by-id-repository.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../../../domain/models/account' 2 | 3 | export interface CheckAccountByIdRepository { 4 | checkById(id: number): Promise 5 | } -------------------------------------------------------------------------------- /src/domain/usecases/user/authentication.ts: -------------------------------------------------------------------------------- 1 | export interface AuthenticationModel { 2 | email: string 3 | password: string 4 | } 5 | 6 | export interface Authentication { 7 | auth(authentication: AuthenticationModel): Promise 8 | } -------------------------------------------------------------------------------- /src/main/config/app.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import setupMiddlewares from "./middlewares" 3 | import setupRoutes from "./routes" 4 | 5 | const app = express() 6 | setupMiddlewares(app) 7 | setupRoutes(app) 8 | export default app -------------------------------------------------------------------------------- /src/infra/db/mssqldb/helpers/connect.ts: -------------------------------------------------------------------------------- 1 | import knex from 'knex' 2 | import knexConfig from '../../../../../knexfile' 3 | 4 | console.log("🔥 Successfully created connection with database 🔥") 5 | export default knex(knexConfig['development']) 6 | -------------------------------------------------------------------------------- /src/data/protocols/db/account/check-account-by-email-repository.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../../../domain/models/account' 2 | 3 | export interface CheckAccountByEmailRepository { 4 | checkByEmail(email: string): Promise 5 | } -------------------------------------------------------------------------------- /src/presentation/controllers/delete/delete-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../../domain/usecases/user' 3 | export * from '../../../domain/models/account' 4 | export * from '../../protocols/validation' 5 | -------------------------------------------------------------------------------- /src/presentation/controllers/signup/signup-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../../domain/usecases/user' 3 | export * from '../../../domain/models/account' 4 | export * from '../../protocols/validation' 5 | -------------------------------------------------------------------------------- /src/presentation/controllers/update/update-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../../domain/usecases/user' 3 | export * from '../../../domain/models/account' 4 | export * from '../../protocols/validation' 5 | -------------------------------------------------------------------------------- /src/data/usecases/authentication/db-authentication-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols/db' 2 | export * from '../../../domain/usecases/user/authentication' 3 | export * from '../../protocols/criptography' 4 | export * from '../../protocols/db' 5 | -------------------------------------------------------------------------------- /src/data/usecases/get-all-account/db-get-all-account-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../../domain/usecases/user/get-account-by-id' 2 | export * from '../../../domain/models/account' 3 | export * from '../../protocols/db/account/get-all-account-repository' 4 | -------------------------------------------------------------------------------- /src/presentation/controllers/get-by-id/get-by-id-controller-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../protocols' 2 | export * from '../../../domain/usecases/user' 3 | export * from '../../../domain/models/account' 4 | export * from '../../protocols/validation' 5 | -------------------------------------------------------------------------------- /src/domain/usecases/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-account' 2 | export * from './authentication' 3 | export * from './update-account' 4 | export * from './delete-account' 5 | export * from './get-account-by-id' 6 | export * from './get-all-account' 7 | -------------------------------------------------------------------------------- /src/main/config/middlewares.ts: -------------------------------------------------------------------------------- 1 | import { Express } from 'express'; 2 | import { bodyParser, cors, contentType } from '../middlewares/' 3 | 4 | export default (app: Express): void => { 5 | app.use(bodyParser) 6 | app.use(cors) 7 | app.use(contentType) 8 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/delete-account-repository.ts: -------------------------------------------------------------------------------- 1 | import { DeleteAccountModel } from '../../../../domain/usecases/user/delete-account' 2 | 3 | export interface DeleteAccountRepository { 4 | delete(deleteAccountModel: DeleteAccountModel): Promise 5 | } -------------------------------------------------------------------------------- /src/infra/validators/email-validator-adapter.ts: -------------------------------------------------------------------------------- 1 | import { EmailValidator } from "../../validation/protocols"; 2 | import validator from 'validator'; 3 | 4 | export class EmailValidatorAdapter implements EmailValidator { 5 | isValid(email: string): boolean { 6 | return validator.isEmail(email) 7 | } 8 | } -------------------------------------------------------------------------------- /src/data/usecases/delete-account/db-delete-account-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../../domain/usecases/user/delete-account' 2 | export * from '../../../domain/models/account' 3 | export * from '../../protocols/db/account/delete-account-repository' 4 | export * from '../../protocols/db/account/check-account-by-email-repository' -------------------------------------------------------------------------------- /src/domain/usecases/user/create-account.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../models/account' 2 | 3 | export interface CreateAccountModel { 4 | name: string 5 | email: string 6 | password: string 7 | } 8 | 9 | export interface CreateAccount { 10 | create(account: CreateAccountModel): Promise 11 | } -------------------------------------------------------------------------------- /src/domain/usecases/user/get-all-account.ts: -------------------------------------------------------------------------------- 1 | export interface GetAllAccountModel { 2 | getAll(): GetAllAccountModel.Result 3 | } 4 | 5 | export namespace GetAllAccountModel { 6 | export type Result = Promise<{ 7 | id: number 8 | name: string 9 | email: string 10 | accessToken: string 11 | }[]> 12 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/create-account-repository.ts: -------------------------------------------------------------------------------- 1 | import { CreateAccountModel } from '../../../../domain/usecases/user/create-account' 2 | import { AccountModel } from '../../../../domain/models/account' 3 | 4 | export interface CreateAccountRepository { 5 | create(accountData: CreateAccountModel): Promise 6 | } -------------------------------------------------------------------------------- /src/data/usecases/get-account-by-id/db-get-account-by-id-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../../domain/usecases/user/get-account-by-id' 2 | export * from '../../../domain/models/account' 3 | export * from '../../protocols/db/account/get-account-by-id-repository' 4 | export * from '../../protocols/db/account/check-account-by-id-repository' 5 | -------------------------------------------------------------------------------- /src/domain/usecases/user/get-account-by-id.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../models/account' 2 | 3 | export interface GetAccountByIdModel { 4 | id: number 5 | name: string 6 | email: string 7 | accessToken: string 8 | } 9 | 10 | export interface GetAccountById { 11 | getById(id: number): Promise 12 | } -------------------------------------------------------------------------------- /src/domain/usecases/user/update-account.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../models/account' 2 | 3 | export interface UpdateAccountModel { 4 | name: string 5 | email: string 6 | password: string 7 | } 8 | 9 | export interface UpdateAccount { 10 | update(id: number, account: UpdateAccountModel): Promise 11 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/update-account-repository.ts: -------------------------------------------------------------------------------- 1 | import { AccountModel } from '../../../../domain/models/account' 2 | import { UpdateAccountModel } from '../../../../domain/usecases/user/update-account' 3 | 4 | export interface UpdateAccountRepository { 5 | update(id: number, accountData: UpdateAccountModel): Promise 6 | } -------------------------------------------------------------------------------- /src/main/middlewares/cors.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express' 2 | 3 | export const cors = (req: Request, res: Response, next: NextFunction): void => { 4 | res.set('access-control-allow-origin', '*') 5 | res.set('access-control-allow-methods', '*') 6 | res.set('access-control-allow-headers', '*') 7 | next() 8 | } -------------------------------------------------------------------------------- /src/data/protocols/db/account/get-all-account-repository.ts: -------------------------------------------------------------------------------- 1 | export interface GetAllAccountRepository { 2 | getAll(): GetAllAccountRepository.Result 3 | } 4 | 5 | export namespace GetAllAccountRepository { 6 | export type Result = Promise<{ 7 | id: number 8 | name: string 9 | email: string 10 | accessToken: string 11 | }[]> 12 | } -------------------------------------------------------------------------------- /src/data/usecases/create-account/db-create-account-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../../domain/usecases/user/create-account' 2 | export * from '../../../domain/models/account' 3 | export * from '../../protocols/criptography/hasher' 4 | export * from '../../protocols/db/account/create-account-repository' 5 | export * from '../../protocols/db/account/check-account-by-email-repository' -------------------------------------------------------------------------------- /src/data/usecases/update-account/db-update-account-protocols.ts: -------------------------------------------------------------------------------- 1 | export * from '../../../domain/usecases/user/update-account' 2 | export * from '../../../domain/models/account' 3 | export * from '../../protocols/criptography/hasher' 4 | export * from '../../protocols/db/account/update-account-repository' 5 | export * from '../../protocols/db/account/check-account-by-email-repository' -------------------------------------------------------------------------------- /src/main/factories/controllers/user/get-all/get-all-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "../../../../../presentation/protocols" 2 | import { GetAllController } from '../../../../../presentation/controllers/get-all/get-all-controller' 3 | import { makeDbGetAllAccount } from "../../../usecases/get-all-account/db-get-all-account-factory" 4 | 5 | export const makeGetAllController = (): Controller => { 6 | return new GetAllController(makeDbGetAllAccount()) 7 | } 8 | -------------------------------------------------------------------------------- /src/validation/validators/required-field-validation.ts: -------------------------------------------------------------------------------- 1 | import { Validation } from "../../presentation/protocols/validation" 2 | import { MissingParamError } from '../../presentation/errors' 3 | 4 | export class RequiredFieldValidation implements Validation { 5 | constructor( 6 | private readonly fieldName: string 7 | ) { } 8 | 9 | validate(input: any): Error { 10 | if (!input[this.fieldName]) return new MissingParamError(this.fieldName) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/validation/validators/validation-composite.ts: -------------------------------------------------------------------------------- 1 | import { Validation } from "../../presentation/protocols/validation" 2 | 3 | export class ValidationComposite implements Validation { 4 | 5 | constructor( 6 | private readonly validator: Validation[] 7 | ) { } 8 | 9 | validate(input: any): Error { 10 | for (const validation of this.validator) { 11 | const error = validation.validate(input) 12 | if (error) return error 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /knexfile.ts: -------------------------------------------------------------------------------- 1 | const knexConfig = { 2 | 3 | development: { 4 | client: 'mssql', 5 | connection: { 6 | database: "Estagio", 7 | host: "192.168.100.8", 8 | port: 49273, 9 | user: "matheus.willian", 10 | password: "@mw1q2w3e4r@" 11 | }, 12 | migrations: { 13 | tableName: 'knex_migrations', 14 | directory: `${__dirname}/src/infra/db/mssqldb/database/migrations` 15 | }, 16 | }, 17 | 18 | } 19 | 20 | export default knexConfig -------------------------------------------------------------------------------- /seguir.txt: -------------------------------------------------------------------------------- 1 | arrumar a validação de email no update 2 | 3 | 1º -> Criar no domain a interface a se usar 4 | 2º -> Criar no infra e no db 5 | 6 | data -> protocols -> db -> account 7 | data -> protocols -> db -> usecases 8 | domain -> usecases 9 | infra -> db -> mssql -> models 10 | main -> factories -> controllers -> user ?? 11 | main -> routes 12 | presentation -> controllers -------------------------------------------------------------------------------- /src/data/protocols/db/index.ts: -------------------------------------------------------------------------------- 1 | export * from './account/create-account-repository' 2 | export * from './account/check-account-by-email-repository' 3 | export * from './account/update-access-token-repository' 4 | export * from './account/update-account-repository' 5 | export * from './account/delete-account-repository' 6 | export * from './account/get-account-by-id-repository' 7 | export * from './account/get-all-account-repository' 8 | export * from './account/check-account-by-id-repository' 9 | -------------------------------------------------------------------------------- /src/data/usecases/get-all-account/db-get-all-account.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GetAllAccountRepository 3 | } from './db-get-all-account-protocols' 4 | 5 | export class DbGetAllAccount implements GetAllAccountRepository { 6 | 7 | constructor( 8 | private readonly getAllAccountRepository: GetAllAccountRepository, 9 | ) { } 10 | 11 | async getAll(): GetAllAccountRepository.Result { 12 | const allAccount = await this.getAllAccountRepository.getAll() 13 | return allAccount 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/delete/delete-validation-factory.ts: -------------------------------------------------------------------------------- 1 | import { ValidationComposite, RequiredFieldValidation } from '../../../../../validation/validators' 2 | import { Validation } from '../../../../../presentation/protocols/validation' 3 | 4 | export const makeDeleteValidation = (): ValidationComposite => { 5 | const validations: Validation[] = [] 6 | for (const field of ['email']) { 7 | validations.push(new RequiredFieldValidation(field)) 8 | } 9 | return new ValidationComposite(validations) 10 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/get-by-id/get-by-id-validation-factory.ts: -------------------------------------------------------------------------------- 1 | import { ValidationComposite, RequiredFieldValidation } from '../../../../../validation/validators' 2 | import { Validation } from '../../../../../presentation/protocols/validation' 3 | 4 | export const makeGetByIdValidation = (): ValidationComposite => { 5 | const validations: Validation[] = [] 6 | for (const field of ['id']) { 7 | validations.push(new RequiredFieldValidation(field)) 8 | } 9 | return new ValidationComposite(validations) 10 | } -------------------------------------------------------------------------------- /src/main/config/routes.ts: -------------------------------------------------------------------------------- 1 | import { Express, Router } from 'express'; 2 | // import fg from 'fast-glob' 3 | import { readdirSync } from 'fs' 4 | 5 | export default (app: Express): void => { 6 | const router = Router() 7 | app.use('/api', router) 8 | // fg.sync('**/src/main/routes/**routes.ts').map(async file => (await import(`../../../${file}`)).default(router)) 9 | readdirSync(`${__dirname}/../routes`).map(async file => { 10 | if (!file.includes('.test.')) (await import(`../routes/${file}`)).default(router) 11 | }) 12 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // "outDir": "./dist/", 4 | "allowJs": true, 5 | "target": "es2021", 6 | "module": "commonjs", 7 | // "module": "commonjs", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | // "strict": true, 11 | // "strictPropertyInitialization": false, 12 | "skipLibCheck": true, 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "baseUrl": "./src/", 16 | "moduleResolution": "node", 17 | } 18 | } -------------------------------------------------------------------------------- /src/infra/criptography/jwt-adapter.ts: -------------------------------------------------------------------------------- 1 | import { Encrypter, Decrypter } from '../../data/protocols/criptography' 2 | import jwt from 'jsonwebtoken' 3 | 4 | export class JwtAdapter implements Encrypter, Decrypter { 5 | 6 | constructor( 7 | private readonly secret: string 8 | ) { } 9 | 10 | async encrypt(value: number): Promise { 11 | return jwt.sign({ id: value }, this.secret) 12 | } 13 | async decrypt(ciphertext: string): Promise { 14 | return jwt.verify(ciphertext, this.secret) as any 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/factories/usecases/get-all-account/db-get-all-account-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { DbGetAllAccount } from '../../../../data/usecases/get-all-account/db-get-all-account' 3 | import { GetAllAccountModel } from '../../../../domain/usecases/user/get-all-account' 4 | 5 | export const makeDbGetAllAccount = (): GetAllAccountModel => { 6 | const accountMssqlRepository = new AccountMssqlRepository() 7 | return new DbGetAllAccount(accountMssqlRepository) 8 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/delete/delete-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "../../../../../presentation/protocols" 2 | import { DeleteController } from '../../../../../presentation/controllers/delete/delete-controller' 3 | import { makeDeleteValidation } from "./delete-validation-factory" 4 | import { makeDbDeleteAccount } from "../../../usecases/delete-account/db-delete-account-factory" 5 | 6 | export const makeDeleteController = (): Controller => { 7 | 8 | return new DeleteController(makeDbDeleteAccount(), makeDeleteValidation()) 9 | } 10 | -------------------------------------------------------------------------------- /src/main/factories/usecases/delete-account/db-delete-account-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { DbDeleteAccount } from '../../../../data/usecases/delete-account/db-delete-account' 3 | import { DeleteAccount } from '../../../../domain/usecases/user/delete-account' 4 | 5 | export const makeDbDeleteAccount = (): DeleteAccount => { 6 | const accountMssqlRepository = new AccountMssqlRepository() 7 | return new DbDeleteAccount(accountMssqlRepository, accountMssqlRepository) 8 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/get-by-id/get-by-id-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "../../../../../presentation/protocols" 2 | import { GetByIdController } from '../../../../../presentation/controllers/get-by-id/get-by-id-controller' 3 | import { makeDbGetAccountById } from "../../../usecases/get-account-by-id/db-get-account-by-id-factory" 4 | import { makeGetByIdValidation } from "./get-by-id-validation-factory" 5 | 6 | export const makeGetByIdController = (): Controller => { 7 | return new GetByIdController(makeDbGetAccountById(), makeGetByIdValidation()) 8 | } 9 | -------------------------------------------------------------------------------- /src/main/factories/controllers/user/login/login-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import env from '../../../../config/env' 2 | import { Controller } from '../../../../../presentation/protocols' 3 | import { LoginController } from '../../../../../presentation/controllers/login/login-controller' 4 | import { makeLoginUpValidation } from './login-validation-factory' 5 | import { makeDbAuthentication } from '../../../usecases/authentication/db-authentication-factory' 6 | 7 | export const makeLoginController = (): Controller => { 8 | return new LoginController(makeDbAuthentication(), makeLoginUpValidation()) 9 | } -------------------------------------------------------------------------------- /src/main/factories/usecases/get-account-by-id/db-get-account-by-id-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { DbGetAccountById } from '../../../../data/usecases/get-account-by-id/db-get-account-by-id' 3 | import { GetAccountById } from '../../../../domain/usecases/user/get-account-by-id' 4 | 5 | export const makeDbGetAccountById = (): GetAccountById => { 6 | const accountMssqlRepository = new AccountMssqlRepository() 7 | return new DbGetAccountById(accountMssqlRepository, accountMssqlRepository) 8 | } -------------------------------------------------------------------------------- /src/validation/validators/compare-fields-validation.ts: -------------------------------------------------------------------------------- 1 | import { Validation } from "../../presentation/protocols/validation" 2 | import { InvalidParamError } from '../../presentation/errors' 3 | 4 | export class CompareFieldsValidation implements Validation { 5 | constructor( 6 | private readonly fieldName: string, 7 | private readonly fieldToCompareName: string 8 | ) { } 9 | 10 | validate(input: any): Error { 11 | if (input[this.fieldName] !== input[this.fieldToCompareName]) { 12 | return new InvalidParamError(this.fieldToCompareName) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/infra/criptography/bcrypt-adapter.ts: -------------------------------------------------------------------------------- 1 | import { Hasher, HashComparer } from '../../data/protocols/criptography' 2 | import bcrypt from 'bcrypt' 3 | 4 | export class BcryptAdapter implements Hasher, HashComparer { 5 | constructor( 6 | private readonly salt: number 7 | ) { } 8 | 9 | async hash(value: string): Promise { 10 | const hash = await bcrypt.hash(value, this.salt) 11 | return hash 12 | } 13 | 14 | async compare(value: string, hash: string): Promise { 15 | const isValid = await bcrypt.compare(value, hash) 16 | return isValid 17 | } 18 | } -------------------------------------------------------------------------------- /src/validation/validators/email-validation.ts: -------------------------------------------------------------------------------- 1 | import { EmailValidator } from '../protocols/email-validator' 2 | import { Validation } from "../../presentation/protocols/validation" 3 | import { InvalidParamError } from '../../presentation/errors' 4 | 5 | export class EmailValidation implements Validation { 6 | constructor( 7 | private readonly fieldName: string, 8 | private readonly emailValidator: EmailValidator 9 | ) { } 10 | 11 | validate(input: any): Error { 12 | const isValid = this.emailValidator.isValid(input[this.fieldName]) 13 | if (!isValid) return new InvalidParamError(this.fieldName) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/factories/controllers/user/update/update-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "../../../../../presentation/protocols" 2 | import { UpdateController } from '../../../../../presentation/controllers/update/update-controller' 3 | import { makeSignUpValidation } from "./update-validation-factory" 4 | import { makeDbAuthentication } from '../../../usecases/authentication/db-authentication-factory' 5 | import { makeDbUpdateAccount } from "../../../usecases/update-account/db-update-account-factory" 6 | 7 | export const makeUpdateController = (): Controller => { 8 | return new UpdateController(makeDbUpdateAccount(), makeSignUpValidation(), makeDbAuthentication()) 9 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/signup/signup-controller-factory.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "../../../../../presentation/protocols" 2 | import { SignUpController } from '../../../../../presentation/controllers/signup/signup-controller' 3 | import { makeSignUpValidation } from "./signup-validation-factory" 4 | import { makeDbAuthentication } from '../../../usecases/authentication/db-authentication-factory' 5 | import { makeDbCreateAccount } from "../../../usecases/create-account/db-create-account-factory" 6 | 7 | export const makeSignUpController = (): Controller => { 8 | return new SignUpController(makeDbCreateAccount(), makeSignUpValidation(), makeDbAuthentication()) 9 | } 10 | -------------------------------------------------------------------------------- /src/presentation/controllers/get-all/get-all-controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpRequest, 3 | HttpResponse, 4 | Controller, 5 | GetAllAccountModel, 6 | } from './get-all-controller-protocols' 7 | import { serverError, ok } from '../../helpers/http/http-helper' 8 | 9 | export class GetAllController implements Controller { 10 | 11 | constructor( 12 | private readonly getAllAccount: GetAllAccountModel 13 | ) { } 14 | 15 | async handle(httpRequest: HttpRequest): Promise { 16 | 17 | try { 18 | 19 | const account = await this.getAllAccount.getAll() 20 | 21 | return ok(account) 22 | } catch (error) { 23 | return serverError() 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/main/adapters/express/express-route-adapter.ts: -------------------------------------------------------------------------------- 1 | import { Controller, HttpRequest } from '../../../presentation/protocols' 2 | import { Request, Response } from 'express' 3 | 4 | export const adaptRoute = (controller: Controller) => { 5 | return async (req: Request, res: Response) => { 6 | const httpRequest: HttpRequest = { 7 | body: req.body, 8 | params: req.params 9 | } 10 | const httpResponse = await controller.handle(httpRequest) 11 | if (httpResponse.statusCode === 200) { 12 | res.status(httpResponse.statusCode).json(httpResponse.body) 13 | } else { 14 | res.status(httpResponse.statusCode).json({ 15 | error: httpResponse.body.message 16 | }) 17 | } 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/factories/usecases/create-account/db-create-account-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { BcryptAdapter } from "../../../../infra/criptography/bcrypt-adapter" 3 | import { DbCreateAccount } from '../../../../data/usecases/create-account/db-create-account' 4 | import { CreateAccount } from '../../../../domain/usecases/user/create-account' 5 | 6 | export const makeDbCreateAccount = (): CreateAccount => { 7 | const salt = 12 8 | const bcryptAdapter = new BcryptAdapter(salt) 9 | const accountMssqlRepository = new AccountMssqlRepository() 10 | return new DbCreateAccount(bcryptAdapter, accountMssqlRepository, accountMssqlRepository) 11 | } -------------------------------------------------------------------------------- /src/main/factories/usecases/update-account/db-update-account-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { BcryptAdapter } from "../../../../infra/criptography/bcrypt-adapter" 3 | import { DbUpdateAccount } from '../../../../data/usecases/update-account/db-update-account' 4 | import { UpdateAccount } from '../../../../domain/usecases/user/update-account' 5 | 6 | export const makeDbUpdateAccount = (): UpdateAccount => { 7 | const salt = 12 8 | const bcryptAdapter = new BcryptAdapter(salt) 9 | const accountMssqlRepository = new AccountMssqlRepository() 10 | return new DbUpdateAccount(bcryptAdapter, accountMssqlRepository, accountMssqlRepository) 11 | } -------------------------------------------------------------------------------- /src/infra/db/mssqldb/database/migrations/20211202200348_create_table_users.ts: -------------------------------------------------------------------------------- 1 | import { Knex } from "knex" 2 | 3 | 4 | export async function up(knex: Knex): Promise { 5 | return await knex.schema.createTable('matheus.users', (table) => { 6 | table.increments('id').unique() 7 | table.string('name').notNullable() 8 | table.string('email').notNullable().unique() 9 | table.string('password').notNullable() 10 | table.string('accessToken') 11 | 12 | // table.timestamp('created_at').defaultTo(knex.fn.now()) 13 | // table.timestamp('updated_at').defaultTo(knex.fn.now()) 14 | }) 15 | } 16 | 17 | export async function down(knex: Knex): Promise { 18 | return await knex.schema.dropTable('matheus.users') 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/main/factories/controllers/user/login/login-validation-factory.ts: -------------------------------------------------------------------------------- 1 | import { ValidationComposite, RequiredFieldValidation, CompareFieldsValidation, EmailValidation } from '../../../../../validation/validators' 2 | import { Validation } from '../../../../../presentation/protocols/validation' 3 | import { EmailValidatorAdapter } from '../../../../../infra/validators/email-validator-adapter' 4 | 5 | export const makeLoginUpValidation = (): ValidationComposite => { 6 | const validations: Validation[] = [] 7 | for (const field of ['email', 'password',]) { 8 | validations.push(new RequiredFieldValidation(field)) 9 | } 10 | validations.push(new EmailValidation('email', new EmailValidatorAdapter())) 11 | return new ValidationComposite(validations) 12 | } -------------------------------------------------------------------------------- /src/data/usecases/get-account-by-id/db-get-account-by-id.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GetAccountByIdRepository, 3 | AccountModel, 4 | CheckAccountByIdRepository 5 | } from './db-get-account-by-id-protocols' 6 | 7 | export class DbGetAccountById implements GetAccountByIdRepository { 8 | 9 | constructor( 10 | private readonly getAccountByIdRepository: GetAccountByIdRepository, 11 | private readonly checkAccountByIdRepository: CheckAccountByIdRepository 12 | ) { } 13 | 14 | async getById(id: number): Promise { 15 | const account = await this.checkAccountByIdRepository.checkById(id) 16 | 17 | if (!account) throw new Error('ID_NOT_EXISTING') 18 | 19 | const result = await this.getAccountByIdRepository.getById(id) 20 | return result 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crud-basico-clean", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --exec ts-node src/main/server.ts" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.0.1", 14 | "express": "^4.17.1", 15 | "jsonwebtoken": "^8.5.1", 16 | "knex": "^0.95.14", 17 | "mssql": "^7.3.0", 18 | "typescript": "^4.5.2", 19 | "validator": "^13.7.0" 20 | }, 21 | "devDependencies": { 22 | "@types/bcrypt": "^5.0.0", 23 | "@types/express": "^4.17.13", 24 | "@types/jsonwebtoken": "^8.5.6", 25 | "@types/node": "^16.11.11", 26 | "@types/typescript": "^2.0.0", 27 | "@types/validator": "^13.7.0", 28 | "nodemon": "^2.0.15", 29 | "ts-node": "^10.4.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/data/usecases/delete-account/db-delete-account.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CheckAccountByEmailRepository, 3 | DeleteAccount, 4 | DeleteAccountModel, 5 | DeleteAccountRepository 6 | } from './db-delete-account-protocols' 7 | 8 | export class DbDeleteAccount implements DeleteAccount { 9 | 10 | constructor( 11 | private readonly deleteAccountRepository: DeleteAccountRepository, 12 | private readonly checkAccountByEmailRepository: CheckAccountByEmailRepository 13 | ) { } 14 | 15 | async delete(deleteAccountModel: DeleteAccountModel): Promise { 16 | const account = await this.checkAccountByEmailRepository.checkByEmail(deleteAccountModel.email) 17 | 18 | if (!account) throw new Error('ACCOUNT_EMAIL_NOT_EXISTING') 19 | 20 | const result = await this.deleteAccountRepository.delete(deleteAccountModel) 21 | return result 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/signup/signup-validation-factory.ts: -------------------------------------------------------------------------------- 1 | import { ValidationComposite, RequiredFieldValidation, CompareFieldsValidation, EmailValidation } from '../../../../../validation/validators' 2 | import { Validation } from '../../../../../presentation/protocols/validation' 3 | import { EmailValidatorAdapter } from '../../../../../infra/validators/email-validator-adapter' 4 | 5 | export const makeSignUpValidation = (): ValidationComposite => { 6 | const validations: Validation[] = [] 7 | for (const field of ['email', 'name', 'password', 'passwordConfirmation']) { 8 | validations.push(new RequiredFieldValidation(field)) 9 | } 10 | validations.push(new CompareFieldsValidation('password', 'passwordConfirmation')) 11 | validations.push(new EmailValidation('email', new EmailValidatorAdapter())) 12 | return new ValidationComposite(validations) 13 | } -------------------------------------------------------------------------------- /src/main/factories/controllers/user/update/update-validation-factory.ts: -------------------------------------------------------------------------------- 1 | import { ValidationComposite, RequiredFieldValidation, CompareFieldsValidation, EmailValidation } from '../../../../../validation/validators' 2 | import { Validation } from '../../../../../presentation/protocols/validation' 3 | import { EmailValidatorAdapter } from '../../../../../infra/validators/email-validator-adapter' 4 | 5 | export const makeSignUpValidation = (): ValidationComposite => { 6 | const validations: Validation[] = [] 7 | for (const field of ['email', 'name', 'password', 'passwordConfirmation']) { 8 | validations.push(new RequiredFieldValidation(field)) 9 | } 10 | validations.push(new CompareFieldsValidation('password', 'passwordConfirmation')) 11 | validations.push(new EmailValidation('email', new EmailValidatorAdapter())) 12 | return new ValidationComposite(validations) 13 | } -------------------------------------------------------------------------------- /src/main/factories/usecases/authentication/db-authentication-factory.ts: -------------------------------------------------------------------------------- 1 | import { AccountMssqlRepository } from '../../../../infra/db/mssqldb/models/account-repository/account-repository' 2 | import { BcryptAdapter } from "../../../../infra/criptography/bcrypt-adapter" 3 | import { DbAuthentication } from '../../../../data/usecases/authentication/db-authentication' 4 | import { JwtAdapter } from '../../../../infra/criptography/jwt-adapter' 5 | import { Authentication } from '../../../../domain/usecases/user/authentication' 6 | 7 | export const makeDbAuthentication = (): Authentication => { 8 | const salt = 12 9 | const bcryptAdapter = new BcryptAdapter(salt) 10 | const jwtAdapter = new JwtAdapter('da') 11 | const accountMssqlRepository = new AccountMssqlRepository() 12 | return new DbAuthentication(bcryptAdapter, accountMssqlRepository, jwtAdapter, accountMssqlRepository) 13 | } -------------------------------------------------------------------------------- /src/presentation/helpers/http/http-helper.ts: -------------------------------------------------------------------------------- 1 | import { HttpResponse } from '../../protocols/http' 2 | import { ServerError, UnauthorizedError } from '../../errors' 3 | 4 | export const ok = (data?: any): HttpResponse => ({ 5 | statusCode: 200, 6 | body: data 7 | }) 8 | 9 | export const badRequest = (error: Error): HttpResponse => ({ 10 | statusCode: 400, 11 | body: error 12 | }) 13 | 14 | export const unauthorized = (): HttpResponse => ({ 15 | statusCode: 401, 16 | body: new UnauthorizedError() 17 | }) 18 | 19 | export const notFound = (error: Error): HttpResponse => ({ 20 | statusCode: 404, 21 | body: error 22 | }) 23 | 24 | export const forbidden = (error: Error): HttpResponse => ({ 25 | statusCode: 403, 26 | body: error 27 | }) 28 | 29 | export const conflict = (error: Error): HttpResponse => ({ 30 | statusCode: 409, 31 | body: error 32 | }) 33 | 34 | export const serverError = (): HttpResponse => ({ 35 | statusCode: 500, 36 | body: new ServerError() 37 | }) 38 | -------------------------------------------------------------------------------- /src/presentation/controllers/login/login-controller.ts: -------------------------------------------------------------------------------- 1 | import { badRequest, serverError, unauthorized, ok } from '../../helpers/http/http-helper' 2 | import { Controller, HttpRequest, HttpResponse, Validation, Authentication } from './login-controller-protocols' 3 | 4 | export class LoginController implements Controller { 5 | 6 | constructor( 7 | private readonly authentication: Authentication, 8 | private readonly validation: Validation, 9 | ) { } 10 | 11 | async handle(httpRequest: HttpRequest): Promise { 12 | try { 13 | const error = this.validation.validate(httpRequest.body) 14 | if (error) return badRequest(error) 15 | 16 | const { email, password } = httpRequest.body 17 | 18 | const accessToken = await this.authentication.auth({ 19 | email, 20 | password 21 | }) 22 | 23 | if (!accessToken) return unauthorized() 24 | 25 | return ok({ accessToken }) 26 | } catch (error) { 27 | return serverError() 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/data/usecases/create-account/db-create-account.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateAccount, 3 | CreateAccountModel, 4 | AccountModel, 5 | Hasher, 6 | CreateAccountRepository, 7 | CheckAccountByEmailRepository 8 | } from './db-create-account-protocols' 9 | 10 | export class DbCreateAccount implements CreateAccount { 11 | 12 | constructor( 13 | private readonly encrypter: Hasher, 14 | private readonly createAccountRepository: CreateAccountRepository, 15 | private readonly checkAccountByEmailRepository: CheckAccountByEmailRepository 16 | ) { } 17 | 18 | async create(accountData: CreateAccountModel): Promise { 19 | const account = await this.checkAccountByEmailRepository.checkByEmail(accountData.email) 20 | 21 | if (account) throw new Error('ACCOUNT_EMAIL_EXISTING') 22 | 23 | const hashedPassword = await this.encrypter.hash(accountData.password) 24 | const newAccount = await this.createAccountRepository.create(Object.assign({}, accountData, { password: hashedPassword })) 25 | return newAccount 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /src/data/usecases/update-account/db-update-account.ts: -------------------------------------------------------------------------------- 1 | import { 2 | UpdateAccount, 3 | UpdateAccountModel, 4 | AccountModel, 5 | Hasher, 6 | UpdateAccountRepository, 7 | CheckAccountByEmailRepository 8 | } from './db-update-account-protocols' 9 | 10 | export class DbUpdateAccount implements UpdateAccount { 11 | 12 | constructor( 13 | private readonly encrypter: Hasher, 14 | private readonly updateAccountRepository: UpdateAccountRepository, 15 | private readonly checkAccountByEmailRepository: CheckAccountByEmailRepository 16 | ) { } 17 | 18 | async update(id: number, accountData: UpdateAccountModel): Promise { 19 | const account = await this.checkAccountByEmailRepository.checkByEmail(accountData.email) 20 | 21 | if (account) throw new Error('ACCOUNT_EMAIL_EXISTING') 22 | 23 | const hashedPassword = await this.encrypter.hash(accountData.password) 24 | const newAccount = await this.updateAccountRepository.update(id, Object.assign({}, accountData, { password: hashedPassword })) 25 | return newAccount 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/presentation/controllers/delete/delete-controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpRequest, 3 | HttpResponse, 4 | Controller, 5 | DeleteAccount, 6 | Validation 7 | } from './delete-controller-protocols' 8 | import { serverError, ok, conflict, notFound } from '../../helpers/http/http-helper' 9 | 10 | export class DeleteController implements Controller { 11 | 12 | constructor( 13 | private readonly DeleteAccount: DeleteAccount, 14 | private readonly validation: Validation 15 | ) { } 16 | 17 | async handle(httpRequest: HttpRequest): Promise { 18 | try { 19 | 20 | const error = this.validation.validate(httpRequest.body) 21 | if (error) return notFound(error) 22 | 23 | const { email } = httpRequest.body 24 | 25 | await this.DeleteAccount.delete({email}) 26 | 27 | return ok() 28 | 29 | } catch (error) { 30 | switch (error.message) { 31 | case 'ACCOUNT_EMAIL_NOT_EXISTING': 32 | return conflict(error) 33 | default: 34 | return serverError() 35 | } 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/presentation/controllers/get-by-id/get-by-id-controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpRequest, 3 | HttpResponse, 4 | Controller, 5 | GetAccountById, 6 | Validation, 7 | } from './get-by-id-controller-protocols' 8 | import { serverError, ok, notFound } from '../../helpers/http/http-helper' 9 | 10 | export class GetByIdController implements Controller { 11 | 12 | constructor( 13 | private readonly getAccountById: GetAccountById, 14 | private readonly validation: Validation, 15 | ) { } 16 | 17 | async handle(httpRequest: HttpRequest): Promise { 18 | try { 19 | 20 | const error = this.validation.validate(httpRequest.params) 21 | if (error) return notFound(error) 22 | 23 | const { id } = httpRequest.params 24 | 25 | const account = await this.getAccountById.getById(id) 26 | 27 | return ok(account) 28 | 29 | } catch (error) { 30 | switch (error.message) { 31 | case 'ID_NOT_EXISTING': 32 | return notFound(error) 33 | default: 34 | return serverError() 35 | } 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/routes/account-routes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { adaptRoute } from '../adapters/express/express-route-adapter' 3 | import { makeSignUpController } from '../factories/controllers/user/signup/signup-controller-factory' 4 | import { makeLoginController } from '../factories/controllers/user/login/login-controller-factory' 5 | import { makeUpdateController } from '../factories/controllers/user/update/update-controller-factory' 6 | import { makeDeleteController } from '../factories/controllers/user/delete/delete-controller-factory' 7 | import { makeGetByIdController } from '../factories/controllers/user/get-by-id/get-by-id-controller-factory' 8 | import { makeGetAllController } from '../factories/controllers/user/get-all/get-all-controller-factory' 9 | 10 | export default (router: Router): void => { 11 | router.get('/index/:id', adaptRoute(makeGetByIdController())) 12 | router.get('/index', adaptRoute(makeGetAllController())) 13 | router.post('/signup', adaptRoute(makeSignUpController())) 14 | router.post('/login', adaptRoute(makeLoginController())) 15 | router.put('/update/:id', adaptRoute(makeUpdateController())) 16 | router.delete('/delete', adaptRoute(makeDeleteController())) 17 | } -------------------------------------------------------------------------------- /src/data/usecases/authentication/db-authentication.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Authentication, 3 | AuthenticationModel, 4 | CheckAccountByEmailRepository, 5 | HashComparer, 6 | Encrypter, 7 | UpdateAccessTokenRepository 8 | } from './db-authentication-protocols' 9 | 10 | export class DbAuthentication implements Authentication { 11 | 12 | constructor( 13 | private readonly hashComparer: HashComparer, 14 | private readonly checkAccountByEmailRepository: CheckAccountByEmailRepository, 15 | private readonly encrypter: Encrypter, 16 | private readonly updateAccessTokenRepository: UpdateAccessTokenRepository 17 | ) { } 18 | 19 | async auth(authentication: AuthenticationModel): Promise { 20 | const account = await this.checkAccountByEmailRepository.checkByEmail(authentication.email) 21 | 22 | if (account) { 23 | const isValid = await this.hashComparer.compare(authentication.password, account.password) 24 | 25 | if (isValid) { 26 | const accessToken = await this.encrypter.encrypt(account.id) 27 | await this.updateAccessTokenRepository.updateAccessToken(account.id, accessToken) 28 | 29 | return accessToken 30 | } 31 | } 32 | return null 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Clean NODE API

2 | 3 |

O objetivo foi criar uma API simples de cadastro de usuário, com uma arquitetura bem definida e desacoplada, utilizando Clean Architecture para fazer a distribuição de responsabilidades em camadas, seguindo o máximo possivel os princípios do SOLID e, sempre que possível, aplicando Design Patterns para resolver alguns problemas comuns.

4 | 5 |

Design Patterns

6 | 7 | * Factory 8 | * Adapter 9 | * Composite 10 | 11 |

Metodologias e Designs

12 | 13 | * Clean Architecture 14 | * DDD 15 | * Modular Design 16 | * Use Cases 17 | 18 |

Bibliotecas e Ferramentas

19 | 20 | * NPM 21 | * Typescript 22 | * Git 23 | * MSSQL 24 | * Bcrypt 25 | * JsonWebToken 26 | * Validator 27 | * Express 28 | * Sucrase 29 | * Nodemon 30 | 31 |

Rotas

32 | 33 |

GET

34 | * /api/index -> Para listar todos os usuários
35 | * /api/index/:id -> Para buscar um usuários específico 36 | 37 |

POST

38 | * /api/signup -> Para cadastrar um usuario, informando o name, email, password e passwordConfirmation
39 | * /api/login -> Para fazer o login 40 | 41 |

PUT

42 | * /api/update -> Para fazer alterção no usuário 43 | 44 |

DELETE

45 | * /api/delete -> Para deletar o usuário, informando o email 46 | -------------------------------------------------------------------------------- /src/presentation/controllers/signup/signup-controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpRequest, 3 | HttpResponse, 4 | Controller, 5 | CreateAccount, 6 | Authentication, 7 | Validation 8 | } from './signup-controller-protocols' 9 | import { badRequest, serverError, ok, conflict } from '../../helpers/http/http-helper' 10 | 11 | export class SignUpController implements Controller { 12 | 13 | constructor( 14 | private readonly createAccount: CreateAccount, 15 | private readonly validation: Validation, 16 | private readonly authentication: Authentication 17 | ) { } 18 | 19 | async handle(httpRequest: HttpRequest): Promise { 20 | 21 | try { 22 | 23 | const error = this.validation.validate(httpRequest.body) 24 | if (error) return badRequest(error) 25 | 26 | const { name, email, password } = httpRequest.body 27 | 28 | const account = await this.createAccount.create({ 29 | name, 30 | email, 31 | password 32 | }) 33 | 34 | const accessToken = await this.authentication.auth({ 35 | email, 36 | password 37 | }) 38 | 39 | return ok({ account: account, accessToken: accessToken }) 40 | } catch (error) { 41 | 42 | switch (error.message) { 43 | case 'ACCOUNT_EMAIL_EXISTING': 44 | return conflict(error) 45 | default: 46 | return serverError() 47 | } 48 | 49 | } 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/presentation/controllers/update/update-controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpRequest, 3 | HttpResponse, 4 | Controller, 5 | UpdateAccount, 6 | Authentication, 7 | Validation 8 | } from './update-controller-protocols' 9 | import { badRequest, serverError, ok, conflict } from '../../helpers/http/http-helper' 10 | 11 | export class UpdateController implements Controller { 12 | 13 | constructor( 14 | private readonly updateAccount: UpdateAccount, 15 | private readonly validation: Validation, 16 | private readonly authentication: Authentication 17 | ) { } 18 | 19 | async handle(httpRequest: HttpRequest): Promise { 20 | 21 | try { 22 | 23 | const error = this.validation.validate(httpRequest.body) 24 | if (error) return badRequest(error) 25 | 26 | const { name, email, password } = httpRequest.body 27 | const { id } = httpRequest.params 28 | 29 | if (!id) return badRequest(error) 30 | 31 | const account = await this.updateAccount.update(id, { 32 | name, 33 | email, 34 | password 35 | }) 36 | 37 | const accessToken = await this.authentication.auth({ 38 | email, 39 | password 40 | }) 41 | 42 | return ok({ account: account, accessToken: accessToken }) 43 | 44 | } catch (error) { 45 | 46 | switch (error.message) { 47 | case 'ACCOUNT_EMAIL_EXISTING': 48 | return conflict(error) 49 | default: 50 | return serverError() 51 | } 52 | 53 | } 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/infra/db/mssqldb/models/account-repository/account-repository.ts: -------------------------------------------------------------------------------- 1 | import { UpdateAccountModel } from "../../../../../data/usecases/update-account/db-update-account-protocols" 2 | import { 3 | CreateAccountRepository, 4 | CheckAccountByEmailRepository, 5 | CheckAccountByIdRepository, 6 | UpdateAccessTokenRepository, 7 | UpdateAccountRepository, 8 | DeleteAccountRepository, 9 | GetAccountByIdRepository, 10 | GetAllAccountRepository 11 | } from "../../../../../data/protocols/db" 12 | import { CreateAccountModel, AccountModel } from "../../../../../data/usecases/create-account/db-create-account-protocols" 13 | import { DeleteAccountModel } from "../../../../../data/usecases/delete-account/db-delete-account-protocols" 14 | import knex from '../../helpers/connect' 15 | 16 | const TABLE_USERS = 'matheus.users' 17 | 18 | export class AccountMssqlRepository implements 19 | CreateAccountRepository, 20 | CheckAccountByEmailRepository, 21 | CheckAccountByIdRepository, 22 | UpdateAccessTokenRepository, 23 | UpdateAccountRepository, 24 | DeleteAccountRepository, 25 | GetAccountByIdRepository, 26 | GetAllAccountRepository { 27 | 28 | async getById(id: number): Promise { 29 | const account = await knex(TABLE_USERS) 30 | .select('id', 'name', 'email', 'accessToken') 31 | .where({ id }) 32 | .first() 33 | 34 | return account 35 | } 36 | 37 | async getAll(): GetAllAccountRepository.Result { 38 | const account = await knex(TABLE_USERS) 39 | .select('id', 'name', 'email', 'accessToken') 40 | 41 | return account 42 | } 43 | 44 | async create(accountData: CreateAccountModel): Promise { 45 | const [account] = await knex(TABLE_USERS).insert(accountData).returning('*') 46 | 47 | return account 48 | } 49 | 50 | async update(id: number, accountData: UpdateAccountModel): Promise { 51 | const [account] = await knex(TABLE_USERS).where({ id }).update(accountData).returning('*') 52 | 53 | return account 54 | } 55 | 56 | async delete(deleteAccountModel: DeleteAccountModel): Promise { 57 | const { email } = deleteAccountModel 58 | const result = await knex(TABLE_USERS).del().where({ email }) 59 | 60 | return result 61 | } 62 | 63 | async checkByEmail(email: string): Promise { 64 | const account = knex(TABLE_USERS).select('*').where({ 65 | email: email 66 | }).first() 67 | 68 | // const account = knex(TABLE_USERS) 69 | // .select('email') 70 | // .where('email', '=', email) 71 | // .first() 72 | 73 | return account 74 | } 75 | 76 | async checkById(id: number): Promise { 77 | const account = knex(TABLE_USERS).select('*').where({ id }).first() 78 | 79 | return account 80 | } 81 | 82 | async updateAccessToken(id: number, token: string): Promise { 83 | await knex(TABLE_USERS).where({ id }).update({ accessToken: token }) 84 | } 85 | } --------------------------------------------------------------------------------