diff --git a/semana20/aula60/template-backend/.gitignore b/semana20/aula60/template-backend/.gitignore new file mode 100644 index 0000000..8ece3ba --- /dev/null +++ b/semana20/aula60/template-backend/.gitignore @@ -0,0 +1,4 @@ +node_modules +package-lock.json +build +.env \ No newline at end of file diff --git a/semana20/aula60/template-backend/package.json b/semana20/aula60/template-backend/package.json new file mode 100644 index 0000000..23e100d --- /dev/null +++ b/semana20/aula60/template-backend/package.json @@ -0,0 +1,150 @@ +{ + "name": "template-backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "@types/jsonwebtoken": "^8.5.6", + "accepts": "^1.3.7", + "anymatch": "^3.1.2", + "arg": "^4.1.3", + "array-flatten": "^1.1.1", + "balanced-match": "^1.0.2", + "bcryptjs": "^2.4.3", + "bignumber.js": "^9.0.0", + "binary-extensions": "^2.2.0", + "body-parser": "^1.19.1", + "brace-expansion": "^1.1.11", + "braces": "^3.0.2", + "buffer-from": "^1.1.2", + "bytes": "^3.1.1", + "chokidar": "^3.5.2", + "colorette": "^2.0.16", + "commander": "^7.2.0", + "concat-map": "^0.0.1", + "content-disposition": "^0.5.4", + "content-type": "^1.0.4", + "cookie": "^0.4.1", + "cookie-signature": "^1.0.6", + "core-util-is": "^1.0.3", + "cors": "^2.8.5", + "create-require": "^1.1.1", + "debug": "^2.6.9", + "depd": "^1.1.2", + "destroy": "^1.0.4", + "diff": "^4.0.2", + "dotenv": "^10.0.0", + "dynamic-dedupe": "^0.3.0", + "ee-first": "^1.1.1", + "encodeurl": "^1.0.2", + "escalade": "^3.1.1", + "escape-html": "^1.0.3", + "esm": "^3.2.25", + "etag": "^1.8.1", + "express": "^4.17.2", + "fill-range": "^7.0.1", + "finalhandler": "^1.1.2", + "forwarded": "^0.2.0", + "fresh": "^0.5.2", + "fs.realpath": "^1.0.0", + "function-bind": "^1.1.1", + "getopts": "^2.2.5", + "glob": "^7.2.0", + "glob-parent": "^5.1.2", + "has": "^1.0.3", + "http-errors": "^1.8.1", + "iconv-lite": "^0.4.24", + "inflight": "^1.0.6", + "inherits": "^2.0.4", + "interpret": "^2.2.0", + "ipaddr.js": "^1.9.1", + "is-binary-path": "^2.1.0", + "is-core-module": "^2.8.1", + "is-extglob": "^2.1.1", + "is-glob": "^4.0.3", + "is-number": "^7.0.0", + "isarray": "^1.0.0", + "jsonwebtoken": "^8.5.1", + "knex": "^0.95.15", + "lodash": "^4.17.21", + "make-error": "^1.3.6", + "media-typer": "^0.3.0", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.6.0", + "mime-db": "^1.51.0", + "mime-types": "^2.1.34", + "minimatch": "^3.0.4", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "ms": "^2.0.0", + "mysql": "^2.18.1", + "negotiator": "^0.6.2", + "normalize-path": "^3.0.0", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "path-is-absolute": "^1.0.1", + "path-parse": "^1.0.7", + "path-to-regexp": "^0.1.7", + "pg-connection-string": "^2.5.0", + "picomatch": "^2.3.1", + "process-nextick-args": "^2.0.1", + "proxy-addr": "^2.0.7", + "qs": "^6.9.6", + "range-parser": "^1.2.1", + "raw-body": "^2.4.2", + "readable-stream": "^2.3.7", + "readdirp": "^3.6.0", + "rechoir": "^0.7.0", + "resolve": "^1.21.0", + "resolve-from": "^5.0.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.2.1", + "safer-buffer": "^2.1.2", + "send": "^0.17.2", + "serve-static": "^1.14.2", + "setprototypeof": "^1.2.0", + "source-map": "^0.6.1", + "source-map-support": "^0.5.21", + "sqlstring": "^2.3.1", + "statuses": "^1.5.0", + "string_decoder": "^1.1.1", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.1", + "supports-preserve-symlinks-flag": "^1.0.0", + "tarn": "^3.0.2", + "tildify": "^2.0.0", + "to-regex-range": "^5.0.1", + "toidentifier": "^1.0.1", + "tree-kill": "^1.2.2", + "ts-node": "^9.1.1", + "ts-node-dev": "^1.1.8", + "tsconfig": "^7.0.0", + "type-is": "^1.6.18", + "typescript": "^4.5.4", + "unpipe": "^1.0.0", + "util-deprecate": "^1.0.2", + "utils-merge": "^1.0.1", + "uuid": "^8.3.2", + "vary": "^1.1.2", + "wrappy": "^1.0.2", + "xtend": "^4.0.2", + "yn": "^3.1.1" + }, + "scripts": { + "dev": "ts-node-dev ./src/index.ts", + "start": "tsc && node ./build/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/bcryptjs": "^2.4.2", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/uuid": "^8.3.3" + } +} diff --git a/semana20/aula60/template-backend/src/business/PostBusiness.ts b/semana20/aula60/template-backend/src/business/PostBusiness.ts new file mode 100644 index 0000000..886ac1e --- /dev/null +++ b/semana20/aula60/template-backend/src/business/PostBusiness.ts @@ -0,0 +1,18 @@ +import { Post } from "../model/Post"; +import { PostRepository } from "./PostRepository"; + +export class PostBusiness { + private postData: PostRepository; + + constructor(postRepository: PostRepository) { + this.postData = postRepository; + } + + getPostById = async (id: string): Promise => { + const post = await this.postData.getPostById(id); + + return post; + }; + + //irá ter outros métodos como criar post +} diff --git a/semana20/aula60/template-backend/src/business/PostRepository.ts b/semana20/aula60/template-backend/src/business/PostRepository.ts new file mode 100644 index 0000000..51d9ec9 --- /dev/null +++ b/semana20/aula60/template-backend/src/business/PostRepository.ts @@ -0,0 +1,5 @@ +import { Post } from "../model/Post"; + +export interface PostRepository { + getPostById(id: string): Promise; +} diff --git a/semana20/aula60/template-backend/src/business/UserBusiness.ts b/semana20/aula60/template-backend/src/business/UserBusiness.ts new file mode 100644 index 0000000..e69de29 diff --git a/semana20/aula60/template-backend/src/controller/PostController.ts b/semana20/aula60/template-backend/src/controller/PostController.ts new file mode 100644 index 0000000..65bda68 --- /dev/null +++ b/semana20/aula60/template-backend/src/controller/PostController.ts @@ -0,0 +1,24 @@ +import { Request, Response } from "express"; +import { PostBusiness } from "../business/PostBusiness"; +import { PostDatabase } from "../data/PostDatabase"; + +export class PostController { + postBusiness: PostBusiness; + + constructor() { + this.postBusiness = new PostBusiness(new PostDatabase()); + } + + getPostById = async (req: Request, res: Response) => { + try { + const { id } = req.params; + + const post = await this.postBusiness.getPostById(id); + + res.send(post); + } catch (error: any) { + console.log(error); + res.status(500).send("Ocorreu um erro inesperado!"); + } + }; +} diff --git a/semana20/aula60/template-backend/src/controller/UserController.ts b/semana20/aula60/template-backend/src/controller/UserController.ts new file mode 100644 index 0000000..e69de29 diff --git a/semana20/aula60/template-backend/src/controller/app.ts b/semana20/aula60/template-backend/src/controller/app.ts new file mode 100644 index 0000000..56ed8ce --- /dev/null +++ b/semana20/aula60/template-backend/src/controller/app.ts @@ -0,0 +1,11 @@ +import express from "express"; +import cors from "cors"; + +export const app = express(); + +app.use(express.json()); +app.use(cors()); + +app.listen(3003, () => { + console.log("Servidor pronto!"); +}); diff --git a/semana20/aula60/template-backend/src/data/BaseDatabase.ts b/semana20/aula60/template-backend/src/data/BaseDatabase.ts new file mode 100644 index 0000000..aad00cb --- /dev/null +++ b/semana20/aula60/template-backend/src/data/BaseDatabase.ts @@ -0,0 +1,18 @@ +import knex from "knex"; +import dotenv from "dotenv"; + +dotenv.config(); + +export class BaseDatabase { + protected connection = knex({ + client: "mysql", + connection: { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_SCHEMA, + port: 3306, + multipleStatements: true, + }, + }); +} diff --git a/semana20/aula60/template-backend/src/data/PostDatabase.ts b/semana20/aula60/template-backend/src/data/PostDatabase.ts new file mode 100644 index 0000000..325af28 --- /dev/null +++ b/semana20/aula60/template-backend/src/data/PostDatabase.ts @@ -0,0 +1,11 @@ +import { Post } from "../model/Post"; +import { BaseDatabase } from "../data/BaseDatabase"; +import { PostRepository } from "../business/PostRepository"; + +export class PostDatabase extends BaseDatabase implements PostRepository { + getPostById = async (id: string): Promise => { + const result = await this.connection("labook_posts").where({ id }); + + return result[0]; + }; +} diff --git a/semana20/aula60/template-backend/src/index.ts b/semana20/aula60/template-backend/src/index.ts new file mode 100644 index 0000000..6e734ec --- /dev/null +++ b/semana20/aula60/template-backend/src/index.ts @@ -0,0 +1,6 @@ +import { app } from "./controller/app"; +import { PostController } from "./controller/PostController"; + +const postController = new PostController(); + +app.get("/posts/:id", postController.getPostById); diff --git a/semana20/aula60/template-backend/src/model/Post.ts b/semana20/aula60/template-backend/src/model/Post.ts new file mode 100644 index 0000000..5c6c6ec --- /dev/null +++ b/semana20/aula60/template-backend/src/model/Post.ts @@ -0,0 +1,13 @@ +export enum POST_TYPES { + NORMAL = "NORMAL", + EVENT = "EVENT", +} + +export interface Post { + userId: string; + id: string; + photo: string; + description: string; + createdAt: Date; + type: POST_TYPES; +} diff --git a/semana20/aula60/template-backend/src/model/User.ts b/semana20/aula60/template-backend/src/model/User.ts new file mode 100644 index 0000000..155d4bb --- /dev/null +++ b/semana20/aula60/template-backend/src/model/User.ts @@ -0,0 +1,12 @@ +export interface AuthenticationData { + id: string; +} + +export interface UserCredentials { + email: string; + password: string; +} + +export interface User extends AuthenticationData, UserCredentials { + name: string; +} diff --git a/semana20/aula60/template-backend/src/services/Authenticator.ts b/semana20/aula60/template-backend/src/services/Authenticator.ts new file mode 100644 index 0000000..0b52622 --- /dev/null +++ b/semana20/aula60/template-backend/src/services/Authenticator.ts @@ -0,0 +1,25 @@ +import { config } from "dotenv"; +import { JwtPayload, sign, verify } from "jsonwebtoken"; +import { AuthenticationData } from "../model/User"; + +config(); + +export class Authenticator { + generateToken = (payload: AuthenticationData) => { + return sign(payload, process.env.JWT_KEY!, { + expiresIn: process.env.ACCESS_TOKEN_EXPIRES_IN, + }); + }; + + getTokenData = (token: string): AuthenticationData | null => { + try { + const tokenData = verify(token, process.env.JWT_KEY!) as JwtPayload; + + return { + id: tokenData.id, + }; + } catch (error) { + return null; + } + }; +} diff --git a/semana20/aula60/template-backend/src/services/HashManager.ts b/semana20/aula60/template-backend/src/services/HashManager.ts new file mode 100644 index 0000000..1c745d2 --- /dev/null +++ b/semana20/aula60/template-backend/src/services/HashManager.ts @@ -0,0 +1,13 @@ +import * as bcrypt from "bcryptjs"; + +export class HashManager { + async hash(text: string): Promise { + const rounds = Number(process.env.BCRYPT_COST); + const salt = await bcrypt.genSalt(rounds); + return bcrypt.hash(text, salt); + } + + async compare(text: string, hash: string): Promise { + return bcrypt.compare(text, hash); + } +} diff --git a/semana20/aula60/template-backend/src/services/IdGenerator.ts b/semana20/aula60/template-backend/src/services/IdGenerator.ts new file mode 100644 index 0000000..0844e70 --- /dev/null +++ b/semana20/aula60/template-backend/src/services/IdGenerator.ts @@ -0,0 +1,5 @@ +import { v4 } from "uuid"; + +export class IdGenerator { + generate = () => v4(); +} diff --git a/semana20/aula60/template-backend/tsconfig.json b/semana20/aula60/template-backend/tsconfig.json new file mode 100644 index 0000000..c0c61a6 --- /dev/null +++ b/semana20/aula60/template-backend/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es6", + "strict": true, + "module": "commonjs", + "sourceMap": true, + "esModuleInterop": true, + "outDir": "./build", + "rootDir": "./src", + "removeComments": true, + "noImplicitAny": true + } +}