├── Insomnia.json ├── LICENSE ├── README.md ├── package.json └── src ├── app.js ├── controllers └── UserController.js ├── data └── users.txt ├── middlewares ├── Exception.js └── MiddlewareException.js ├── repositories └── UserRepository.js ├── services └── UserServices.js └── utils ├── getParam.js ├── idGenerator.js ├── response.js ├── validateParam.js └── validateType.js /Insomnia.json: -------------------------------------------------------------------------------- 1 | {"_type":"export","__export_format":4,"__export_date":"2021-05-16T18:10:30.925Z","__export_source":"insomnia.desktop.app:v2021.3.0","resources":[{"_id":"req_7798205518334c288330749480bca1c1","parentId":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","modified":1621180586120,"created":1621119605343,"url":"{{ _.baseURL }}/users/","name":"List All","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621113316544.3125,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621119595972,"created":1621119595972,"name":"Users","description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1621119595972,"_type":"request_group"},{"_id":"wrk_0e677ea7e62e47efb96e1979a517ebdc","parentId":null,"modified":1621019111231,"created":1621019111231,"name":"http_node_api","description":"","scope":"collection","_type":"workspace"},{"_id":"req_4cb5cde96ea54c2997f10d7972f62ed5","parentId":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","modified":1621188597556,"created":1621138162428,"url":"{{ _.baseURL }}/users/:uid","name":"List One","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621113316494.3125,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_06fab95e30ac4185bca6ff4e6822c184","parentId":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","modified":1621188592110,"created":1621133744517,"url":"{{ _.baseURL }}/users/data?","name":"Create","description":"","method":"POST","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621113316481.8125,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_d247d5f09ccf46068e55127cca4249bb","parentId":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","modified":1621188586832,"created":1621124040169,"url":"{{ _.baseURL }}/users/:uid/data?","name":"Update","description":"","method":"PUT","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621113316469.3125,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_9132519f760c471fa9dde512e29514c5","parentId":"fld_de0a0f81f7bb43b8ba86a8c86fc11422","modified":1621188576612,"created":1621129912035,"url":"{{ _.baseURL }}/users/:uid","name":"Delete","description":"","method":"DELETE","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621113316444.3125,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_811372df07854c0aa49175c00c285ed1","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621188567503,"created":1621124231602,"url":"{{ _.baseURL }}/abc","name":"404","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621069359350.5,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_1b1ca763243f4037a8d8081445101e6f","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621138148431,"created":1621019122728,"url":"{{ _.baseURL }}","name":"Welcome","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1621019122729,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_d235acc8c37e45c6f2d301b285ee21d7b77021cd","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621119776844,"created":1621019111319,"name":"Base Environment","data":{"baseURL":"localhost:3333"},"dataPropertyOrder":{"&":["baseURL"]},"color":null,"isPrivate":false,"metaSortKey":1621019111319,"_type":"environment"},{"_id":"jar_d235acc8c37e45c6f2d301b285ee21d7b77021cd","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621019111321,"created":1621019111321,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_330a2acce67345c9966d8346ae8c08f7","parentId":"wrk_0e677ea7e62e47efb96e1979a517ebdc","modified":1621019111238,"created":1621019111238,"fileName":"http_node_api","contents":"","contentType":"yaml","_type":"api_spec"}]} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 João Victor Negreiros 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | API 100% NodeJS 5 |

6 |

7 | Uma api sem dependências! 8 |

9 | Dependencies 10 | Language Top 11 | 12 | License 13 | 14 |
15 | 16 | --- 17 | 18 |

Conteúdos

19 | 20 | [➜ Sobre o projeto](#mag_right-sobre-o-projeto)
21 | [➜ O que aprendi](#books-o-que-aprendi)
22 | [➜ Como usar](#information_source-como-usar)
23 | [➜ Rotas](#arrow_right_hook-rotas)
24 | 25 | --- 26 | 27 | ## :mag_right: Sobre o projeto 28 | 29 | [Voltar ao topo](#conteudos)
30 | 31 | O objetivo dessa aplicação era criar uma API sem nenhuma dependência externa, apenas utilizando as bibliotecas nativas do NodeJS. Tudo foi feito utilizando 100% Javascript. 32 | 33 | --- 34 | 35 | ### ➡ Front-end 36 | O front-end dessa API também foi feito utilizando 100% Javascript, sem arquivos HTML e CSS pré-criados. 37 | 38 | Para visualizar o resultado acesse o repositório: [joaovictornsv/http-node-api-web](https://github.com/joaovictornsv/http-node-api-web) 39 | 40 | --- 41 | 42 | ### ➡ Banco de Dados 43 | Não foi utilizado nenhum banco de dados, os registros são armazenados em um arquivo `users.txt`. Escolhi trabalhar com txt ao invés de JSON para ter o desafio de ler arquivos usando o Node. 44 | 45 | --- 46 | 47 | ### ➡ Arquitetura 48 | Tentei utilizar, na medida do possível, o conceito da Clean Architecture, separando os arquivos em Controllers, Services e Repository. 49 | 50 | --- 51 | 52 | ### ➡ Tratamento de erros 53 | Como o módulo `http` nativo do Node não nos permite usar o `request.body`, tive que adaptar minhas rotas e validar os dados passados por elas. 54 | Além disso, a estrutura da rota também foi validada, pois diferente de bibliotecas como o ExpressJS, onde passar a string `/users/:id` em uma função de request já nos permite acessar o parâmetro com facilidade, no Node esse acesso não é permitido. Para isso tive que criar modos de obtê-los. 55 | 56 | Para os tratamentos e validações utilizei as famosas RegEx's, as quais aprendi bastante sobre como atuam durante o desenvolvimento desse projeto. 57 | 58 | --- 59 | 60 | ### ➡ Funcionalidades ✔️ 61 | A API possui um sistema de CRUD completo (Create, Read, Update and Delete) e possui as seguintes funcionalidades: 62 | 63 | #### Funções principais: 64 | - Listagem de usuários ou usuário único 65 | - Criação de novo usuário 66 | - Alteração de dados de um usuário 67 | - Remoção de um usuário 68 | 69 | #### Outras funcionalidades: 70 | - Validação de dados 71 | - Validação de rotas 72 | - Tratamento de erros usando Middlewares 73 | - Gerador de IDs únicos 74 | - Uso de Controller, Repository e Services 75 | 76 | --- 77 | 78 | ## :books: O que aprendi 79 | 80 | [Voltar ao topo](#conteudos)
81 | 82 | Ter que implementar cada etapa da api, desde o tratamento dos dados até o retorno da resposta para o cliente contribuiu bastante para treinar minha lógica de programação, a eficiência em resolver problemas pequenos e também a criar códigos mais legíveis e limpos. 83 | Além disso, aprendi a utilizar RegEx's para realizar várias validações no projeto. Por fim, acredito que criar essa API "pura" me fez evoluir mais um degrau nessa jornada de back-end. 84 | 85 | --- 86 | 87 | ## :information_source: Como usar 88 | 89 | [Voltar ao topo](#conteudos)
90 | 91 | ```bash 92 | # Clone this repository 93 | $ git clone https://github.com/joaovictornsv/http-node-api 94 | 95 | # Go into the repository 96 | $ cd http-node-api 97 | 98 | # Run the server 99 | $ yarn start 100 | ``` 101 | 102 | --- 103 | 104 | ## :arrow_right_hook: Rotas 105 | 106 | [Voltar ao topo](#conteudos)
107 | 108 | [![Run in Insomnia}](https://insomnia.rest/images/run.svg)](https://insomnia.rest/run/?label=http-node-api&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fjoaovictornsv%2Fhttp-node-api%2Fmaster%2FInsomnia.json) 109 | 110 |
111 | 112 | Ver rotas 113 | 114 | 115 |
116 | 117 | ![](https://img.shields.io/badge/get-BD93F9.svg?&style=for-the-badge&logoColor=white) 118 | 119 | - Home page 120 | 121 | ``` 122 | / 123 | ``` 124 | 125 | - Get all users 126 | 127 | ``` 128 | /users 129 | ``` 130 | 131 | - Get a specific user 132 | ``` 133 | /users/:id 134 | ``` 135 | 136 | --- 137 | 138 | ![](https://img.shields.io/badge/post-49F37B.svg?&style=for-the-badge&logoColor=white) 139 | - Create a user 140 | 141 | ``` 142 | users/data?... 143 | 144 | Search params: 145 | - name 146 | - email 147 | - age 148 | - city 149 | ``` 150 | 151 | --- 152 | 153 | ![](https://img.shields.io/badge/put-FFB86C.svg?&style=for-the-badge&logoColor=white) 154 | - Update a user 155 | 156 | ``` 157 | users/:id/data?... 158 | 159 | Search params: 160 | - name 161 | - email 162 | - age 163 | - city 164 | ``` 165 | 166 | --- 167 | 168 | ![](https://img.shields.io/badge/delete-FF4D4B.svg?&style=for-the-badge&logoColor=white) 169 | 170 | - Delete a user 171 | 172 | ``` 173 | /users/:id 174 | ``` 175 |
176 | 177 | --- 178 | 179 |
180 | Profile 181 | Made with 💙 by João Victor 182 |
183 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-node-api", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "node src/app.js" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const _ = require('url'); 3 | const path = require('path'); 4 | 5 | const UserController = require('./controllers/UserController'); 6 | const MiddlewareException = require('./middlewares/MiddlewareException'); 7 | const { 8 | getRE, postRE, putRE, getParam, 9 | } = require('./utils/getParam'); 10 | 11 | const validateParam = require('./utils/validateParam'); 12 | 13 | const filePath = path.resolve(__dirname, 'data', 'users.txt'); 14 | 15 | const userController = new UserController(filePath); 16 | const middlewareException = new MiddlewareException(); 17 | 18 | const app = http.createServer(async (req, res) => { 19 | const url = new _.URL(`http://localhost:3333${req.url}`); 20 | const { method } = req; 21 | 22 | const route = url.pathname; 23 | 24 | try { 25 | if (route === '/') { 26 | res.writeHead(200, { 'content-type': 'application/json' }); 27 | res.write(JSON.stringify({ welcome: 'http node api' })); 28 | 29 | return res.end(); 30 | } 31 | 32 | if (method === 'GET' && route.match(getRE)) { 33 | const id = getParam(route); 34 | 35 | if (id) { 36 | return await userController.indexUser(res, id); 37 | } 38 | 39 | return await userController.index(res); 40 | } 41 | 42 | if (method === 'POST' && route.match(postRE)) { 43 | const name = validateParam(url.searchParams.get('name'), 'string', 'name'); 44 | const email = validateParam(url.searchParams.get('email'), 'email', 'email'); 45 | const age = validateParam(url.searchParams.get('age'), 'number', 'age'); 46 | const city = validateParam(url.searchParams.get('city'), 'string', 'city'); 47 | 48 | const user = { 49 | name, age, city, email, 50 | }; 51 | 52 | return await userController.addUser(res, user); 53 | } 54 | 55 | if (method === 'PUT' && route.match(putRE)) { 56 | const id = getParam(route); 57 | const name = validateParam(url.searchParams.get('name'), 'string', 'name'); 58 | const email = validateParam(url.searchParams.get('email'), 'email', 'email'); 59 | const age = validateParam(url.searchParams.get('age'), 'number', 'age'); 60 | const city = validateParam(url.searchParams.get('city'), 'string', 'city'); 61 | 62 | const user = { 63 | id, name, age, city, email, 64 | }; 65 | 66 | return await userController.updateUser(res, user); 67 | } 68 | 69 | if (method === 'DELETE' && route.match(getRE)) { 70 | const id = getParam(route); 71 | 72 | return await userController.deleteUser(res, id); 73 | } 74 | // 404 75 | 76 | res.setHeader('Access-Control-Allow-Origin', "*"); 77 | res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,PUT,DELETE'); 78 | 79 | res.writeHead(200, { 'content-type': 'application/json' }); 80 | res.write(JSON.stringify({erro:`Cannot ${method} ${route}`})); 81 | return res.end(); 82 | } catch (err) { 83 | middlewareException.handle(res, err); 84 | return res.end(); 85 | } 86 | }); 87 | 88 | app.listen(3333, () => console.log('Server is listening on port 3333.')); 89 | -------------------------------------------------------------------------------- /src/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | const UserServices = require('../services/UserServices'); 2 | const response = require('../utils/response'); 3 | 4 | class UserController { 5 | constructor(path) { 6 | this.path = path; 7 | } 8 | 9 | async index(res) { 10 | const userServices = new UserServices(this.path); 11 | 12 | const users = await userServices.getAllUsers(); 13 | 14 | return response(res, users); 15 | } 16 | 17 | async indexUser(res, id = '') { 18 | const userServices = new UserServices(this.path); 19 | 20 | const user = await userServices.getUser(id); 21 | 22 | return response(res, user); 23 | } 24 | 25 | async addUser(res, user) { 26 | const userServices = new UserServices(this.path); 27 | 28 | const newUser = await userServices.createUser(user); 29 | 30 | return response(res, newUser, 201); 31 | } 32 | 33 | async updateUser(res, user) { 34 | const userServices = new UserServices(this.path); 35 | 36 | const newUser = await userServices.modifyUser(user); 37 | 38 | return response(res, newUser); 39 | } 40 | 41 | async deleteUser(res, id) { 42 | const userServices = new UserServices(this.path); 43 | 44 | await userServices.removeUser(id); 45 | 46 | return response(res, { message: `User with id '${id}' deleted!` }); 47 | } 48 | } 49 | module.exports = UserController; 50 | -------------------------------------------------------------------------------- /src/data/users.txt: -------------------------------------------------------------------------------- 1 | uid;name;email;age;city -------------------------------------------------------------------------------- /src/middlewares/Exception.js: -------------------------------------------------------------------------------- 1 | class Exception { 2 | constructor(message, statusCode = 400, contentType = 'application/json') { 3 | this.statusCode = statusCode; 4 | this.contentType = { 'content-type': contentType }; 5 | this.message = JSON.stringify({ error: message }); 6 | } 7 | } 8 | 9 | module.exports = Exception; 10 | -------------------------------------------------------------------------------- /src/middlewares/MiddlewareException.js: -------------------------------------------------------------------------------- 1 | const Exception = require('./Exception'); 2 | 3 | class MiddlewareException { 4 | handle(res, error) { 5 | res.setHeader('Access-Control-Allow-Origin', "*"); 6 | res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,PUT,DELETE'); 7 | if (error instanceof Exception) { 8 | res.writeHead(error.statusCode, error.contentType); 9 | res.write(error.message); 10 | } else { 11 | console.log(error); 12 | res.write(error); 13 | } 14 | } 15 | } 16 | 17 | module.exports = MiddlewareException; 18 | -------------------------------------------------------------------------------- /src/repositories/UserRepository.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs').promises; 2 | 3 | class UserRepository { 4 | constructor(path) { 5 | this.file = path; 6 | } 7 | 8 | async find() { 9 | const file = await fs.readFile(this.file, 'utf8'); 10 | const lines = file.split('\n').splice(1); 11 | if (!lines) { 12 | return []; 13 | } 14 | const data = lines.map((line) => { 15 | let [id, name, email, age, city] = line.split(';'); 16 | 17 | age = parseInt(age); 18 | 19 | return { 20 | id, name, email, age, city, 21 | }; 22 | }); 23 | 24 | return data; 25 | } 26 | 27 | async append(user) { 28 | const { 29 | id, name, email, age, city, 30 | } = user; 31 | 32 | await fs.writeFile(this.file, `\n${id};${name};${email};${age};${city}`, { flag: 'a' }); 33 | } 34 | 35 | async write(user) { 36 | const { 37 | id, name, age, email, city, 38 | } = user; 39 | 40 | await fs.writeFile(this.file, `${id};${name};${email};${age};${city}`); 41 | } 42 | 43 | async findByID(id) { 44 | const users = await this.find(); 45 | const user = users.find((u) => { 46 | if (u.id === id) { 47 | return u; 48 | } 49 | }); 50 | 51 | return user; 52 | } 53 | 54 | async findByEmail(email) { 55 | const users = await this.find(); 56 | const user = users.find((u) => { 57 | if (u.email === email) { 58 | return u; 59 | } 60 | }); 61 | 62 | return user; 63 | } 64 | 65 | async updateData(data) { 66 | const overwrite = async () => { 67 | if (data.length === 0) { 68 | await fs.writeFile(this.file, 'uid;name;email;age;city'); 69 | } else { 70 | for (let i = 0; i < data.length; i++) { 71 | if (i === 0) { 72 | await fs.writeFile(this.file, 'uid;name;email;age;city'); 73 | await this.append(data[i]); 74 | } else { 75 | await this.append(data[i]); 76 | } 77 | } 78 | } 79 | }; 80 | 81 | await overwrite(); 82 | } 83 | } 84 | 85 | module.exports = UserRepository; 86 | -------------------------------------------------------------------------------- /src/services/UserServices.js: -------------------------------------------------------------------------------- 1 | const UserRepository = require('../repositories/UserRepository'); 2 | const Exception = require('../middlewares/Exception'); 3 | const { uid, authID } = require('../utils/idGenerator'); 4 | 5 | class UserServices { 6 | constructor(path) { 7 | this.userRepository = new UserRepository(path); 8 | } 9 | 10 | async getAllUsers() { 11 | let users; 12 | await this.userRepository.find() 13 | .then((result) => { 14 | users = result; 15 | }) 16 | .catch((err) => { 17 | console.error(err); 18 | throw new Exception('Error reading database'); 19 | }) 20 | 21 | return users; 22 | } 23 | 24 | async getUser(id) { 25 | const userExists = await this.userRepository.findByID(id); 26 | 27 | if (!authID(id)) { 28 | throw new Exception("The 'id must be of type 'uid'"); 29 | } 30 | 31 | if (!userExists) { 32 | throw new Exception('User with this id does not exists'); 33 | } 34 | 35 | return userExists; 36 | } 37 | 38 | async createUser(user) { 39 | const { 40 | name, age, city, email, 41 | } = user; 42 | 43 | if (!name || !age || !city || !email) { 44 | throw new Exception('This params are incorret!'); 45 | } 46 | 47 | const userAlreadyExists = await this.userRepository.findByEmail(email); 48 | 49 | if (userAlreadyExists) { 50 | throw new Exception('An user with this email already exists'); 51 | } 52 | 53 | user.id = uid(); 54 | 55 | await this.userRepository.append(user); 56 | 57 | return user; 58 | } 59 | 60 | async modifyUser(user) { 61 | if (!user.id) { 62 | throw new Exception('Id not provided!'); 63 | } 64 | 65 | if (!authID(user.id)) { 66 | throw new Exception("The 'id must be of type 'uid'"); 67 | } 68 | 69 | if (!user.name && !user.age && !user.city && !user.email) { 70 | throw new Exception('No fields to edit provided!'); 71 | } 72 | 73 | const userExists = await this.userRepository.findByID(user.id); 74 | 75 | if (!userExists) { 76 | throw new Exception('User with this id does not exist'); 77 | } 78 | 79 | const changes = []; 80 | 81 | user.name && changes.push(['name', user.name]); 82 | user.email && changes.push(['email', user.email]); 83 | user.age && changes.push(['age', user.age]); 84 | user.city && changes.push(['city', user.city]); 85 | 86 | const users = await this.userRepository.find(); 87 | 88 | const usersUpdated = users.map((u) => { 89 | if (u.id === user.id) { 90 | changes.forEach((change) => { 91 | const [key, value] = change; 92 | u[key] = value; 93 | }); 94 | } 95 | 96 | return u; 97 | }); 98 | 99 | await this.userRepository.updateData(usersUpdated); 100 | 101 | return this.userRepository.findByID(user.id); 102 | } 103 | 104 | async removeUser(id) { 105 | if (!id) { 106 | throw new Exception('Id not provided!'); 107 | } 108 | 109 | if (!authID(id)) { 110 | throw new Exception("The 'id must be of type 'uid'"); 111 | } 112 | 113 | const userExists = await this.userRepository.findByID(id); 114 | 115 | if (!userExists) { 116 | throw new Exception('User does not exist'); 117 | } 118 | 119 | const users = await this.userRepository.find(); 120 | 121 | const usersUpdated = users.filter((user) => { 122 | if (user.id !== id) { 123 | return user; 124 | } 125 | }); 126 | 127 | await this.userRepository.updateData(usersUpdated); 128 | } 129 | } 130 | 131 | module.exports = UserServices; 132 | -------------------------------------------------------------------------------- /src/utils/getParam.js: -------------------------------------------------------------------------------- 1 | const getRE = /\/(users)\/?([0-9a-z]|(\-))*?\/?$/g; 2 | const putRE = /^\/(users)\/([0-9a-z]|(\-))*\/(data((?!((\/)|(\?\/)))))/g; 3 | const postRE = /^\/(users)\/(data((?!((\/)|(\?\/)))))/g; 4 | 5 | function getParam(route) { 6 | const allParams = route.split('/'); 7 | let param = null; 8 | if (allParams.length >= 3) { 9 | param = allParams[2]; 10 | 11 | if (param === '') { 12 | param = null; 13 | } 14 | } 15 | 16 | return param; 17 | } 18 | 19 | module.exports = { 20 | getRE, postRE, putRE, getParam, 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/idGenerator.js: -------------------------------------------------------------------------------- 1 | function encrypt(number) { 2 | let numberEncrypt = number.toString().replace('0', 'o'); 3 | numberEncrypt = numberEncrypt.replace('1', 'i'); 4 | numberEncrypt = numberEncrypt.replace('3', 'e'); 5 | numberEncrypt = numberEncrypt.replace('4', 'a'); 6 | numberEncrypt = numberEncrypt.replace('5', 's'); 7 | 8 | return numberEncrypt; 9 | } 10 | 11 | function authID(id) { 12 | const uidRE = /^(uid-j[0-9a-z*]*-v[0-9a-z*]*-j[0-9a-z*]*-v[0-9a-z*]*)$/g; 13 | 14 | if (id.toString().match(uidRE)) { 15 | return true; 16 | } 17 | return false; 18 | } 19 | 20 | function uid() { 21 | const date = new Date(); 22 | 23 | const increment = date.getDay() + date.getMonth() + date.getFullYear(); 24 | 25 | const UTCMilli = date.getUTCMilliseconds(); 26 | 27 | const first = 28 | date.getMilliseconds() * (UTCMilli + 10) * (UTCMilli + 20) 29 | * (parseInt(Math.random() * 10) + parseInt(Math.random() * 10) + 1) 30 | + increment; 31 | 32 | const second = 33 | (date.getSeconds()+1) * (date.getUTCSeconds()+1) * (UTCMilli + 30) 34 | * (parseInt(Math.random() * 10) + parseInt(Math.random() * 10) + 2) 35 | + increment; 36 | 37 | const third = 38 | (date.getMinutes()+1) * (date.getUTCMinutes()+1) * (UTCMilli + 40) 39 | * (parseInt(Math.random() * 10) + parseInt(Math.random() * 10) + 3) 40 | + increment; 41 | 42 | const fourth = 43 | (date.getHours()+1) * (date.getUTCHours()+1) * (UTCMilli + 50) 44 | * (parseInt(Math.random() * 10) + parseInt(Math.random() * 10) + 4) 45 | + increment; 46 | 47 | const id = `uid-j${encrypt(first)}-v${encrypt(second)}-j${encrypt(third)}-v${encrypt(fourth)}`; 48 | 49 | return id; 50 | } 51 | 52 | module.exports = { uid, authID }; 53 | -------------------------------------------------------------------------------- /src/utils/response.js: -------------------------------------------------------------------------------- 1 | function response(res, data = {}, statusCode = 200, contentType = 'application/json') { 2 | res.setHeader('Access-Control-Allow-Origin', "*"); 3 | res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,PUT,DELETE'); 4 | res.writeHead(statusCode, { 'content-type': contentType}); 5 | res.write(JSON.stringify(data)); 6 | 7 | return res.end(); 8 | } 9 | 10 | module.exports = response; 11 | -------------------------------------------------------------------------------- /src/utils/validateParam.js: -------------------------------------------------------------------------------- 1 | const validateType = require('./validateType'); 2 | const Exception = require('../middlewares/Exception') 3 | 4 | function validateParam(param, type, field) { 5 | if (!param) { 6 | return null; 7 | } 8 | 9 | const searchParamRE = /^((?!((\/)|(\;))).)*$/g; 10 | 11 | if (param.match(searchParamRE)) { 12 | return validateType(param, type, field); 13 | } else { 14 | throw new Exception('Invalid params'); 15 | } 16 | } 17 | 18 | module.exports = validateParam; 19 | -------------------------------------------------------------------------------- /src/utils/validateType.js: -------------------------------------------------------------------------------- 1 | const Exception = require('../middlewares/Exception'); 2 | 3 | function validateType(param, type, field) { 4 | let Vparam = param; 5 | const Vtype = type; 6 | 7 | if (Vtype === 'number') { 8 | Vparam = parseInt(Vparam, 10); 9 | 10 | if (typeof Vparam === Vtype && !isNaN(Vparam)) { 11 | return Vparam; 12 | } 13 | } 14 | 15 | if (Vtype === 'email') { 16 | const emailRE = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/g; 17 | 18 | if (Vparam.match(emailRE)) { 19 | return Vparam; 20 | } 21 | } 22 | 23 | if (Vtype === 'string') { 24 | return Vparam; 25 | } 26 | 27 | throw new Exception(`The field '${field}' must be of type '${Vtype}'`); 28 | } 29 | 30 | module.exports = validateType; 31 | --------------------------------------------------------------------------------