Depois de desenvolver toda a regra principal da aplicação, vamos começar a implementar de fato as nossas dependências.
Mão na massa
Primeiramente vamos alterar o arquivo .env, que foi criado lá parte 2, deixe o arquivo com as seguintes informações:
APP_PORT=8001
DB_DIALECT=postgres
DB_HOST=db_two_factor
DB_DATABASE=two_factor
DB_USER=root
DB_PASSWORD=root
JWT_SECRET=My@jwt#Secret%
EMAIL_HOST=smtp.gmail.com
EMAIL_USER=email_de_exemplo@gmail.com
EMAIL_PASS=sua_senha
EMAIL_PORT=465
Essas são todas as credenciais necessárias nesse projeto. Recomendo parar e subir os containers novamente, depois, acesse o terminal do serviço node para que possamos instalar nossas bibliotecas.
Outro detalhe é que os dados acima são somente de exemplo, se você tem uma conta do google, assim como eu, altere somente os campos EMAIL_USER e EMAIL_PASS, agora caso queiram utilizar algo diferente, é necessário alterar o EMAIL_HOST e talvez o EMAIL_POST.
Dependências
A primeira dependência que vamos instalar, é uma biblioteca que fará o carregamento das informações contidas no arquivo .env, no terminal, execute o comando abaixo para fazer a instalação:
npm i dotenv
Após isso, crie um arquivo chamado load-env.js dentro de src/infra/env. Nesse arquivo vamos implementar a lógica para que ele consiga ler as informações do nosso arquivo .env. Adicione o código abaixo no arquivo:
import { config } from 'dotenv';
const loadEnv = async () => {
let env = '';
if (process.env.NODE_ENV) {
env = `.${process.env.NODE_ENV}`;
}
const configPath = `.env${env}`;
const result = config({
path: configPath
});
if (result.error) {
throw result.error
}
}
export default loadEnv;
Agora, na raiz da nossa aplicação, crie um arquivo chamado index.js com o seguinte conteúdo:
import loadEnv from './src/infra/env/load-env.js';
async function run() {
await loadEnv();
}
run();
Esse arquivo será o ponto de partida da nossa aplicação. Por enquanto deixemos ele assim, voltaremos a modificá-lo no futuro.
Em seguida, vamos instalar uma biblioteca para manipular senhas, ela será útil por exemplo para criptografar senhas e fazer comparações de hashs:
npm i bcrypt
Agora crie um arquivo chamado password-hash.js, dentro de src/infra/hash, com o conteúdo abaixo:
import IPasswordHash from './../../domain/ipassword-hash.js';
import Bcrypt from 'bcrypt';
import { promisify } from 'util';
const compareAsync = promisify(Bcrypt.compare);
export default class PasswordHash extends IPasswordHash {
constructor() {
super();
}
compare(password, hash) {
return compareAsync(password, hash);
}
}
Basicamente implementamos nosso comparativo de senha, detalhe para o uso da função interna do node chamada promisify, que converte funções callback para promise, facilitando o use de qualquer recurso.
Agora vamos instalar duas bibliotecas muito úteis, a primeira vai servir para gerar um token jwt, e o segundo nos servirá para gerar uuids:
npm i jsonwebtoken short-uuid
Após a instalação, vamos começar por configurar a nossa geração de token jwt. Dentro de src/infra/jwt, crie o arquivo jwt.js, com o seguinte conteúdo:
import IToken from './../../domain/itoken.js';
import JWT from 'jsonwebtoken';
export default class Jwt extends IToken {
constructor() {
super();
}
generateWebToken(user) {
const token = JWT.sign({
id: user.id,
email: user.email,
}, process.env.JWT_SECRET);
return token;
}
}
Aqui não tenho muito o que falar a não ser recomendar pesquisarem um pouco sobre essa biblioteca para terem noção do quão poderosa ela é.
Agora vamos configurar nosso gerador de uuids, dentro de src/infra/token, crie o arquivo token-service.js, e insira o conteúdo abaixo:
import Short from 'short-uuid';
import IGenerateToken from '../../domain/igenerate-token.js';
export default class TokenService extends IGenerateToken {
constructor() {
super();
}
getToken() {
return Short.uuid();
}
getEmailToken() {
return Short.generate();
}
}
Essa biblioteca vai nos fornecer dois recursos: o primeiro é a geração de um uuid completo, algo como fd5c084c-ff7c-4651-9a52-37096242d81c, além disso, ela também permite a geração de identificadores menores, então o método generate() retornaria algo parecido com isso mhvXdrZT4jP5T8vBxuvm75.
Agora vamos instalar a biblioteca que vai fazer o envio de e-mail:
npm i nodemailer
Após a instalação, vamos configurar nosso serviço de e-mail, dentro do diretório src/infra/email, crie um arquivo chamado body-email-html.js, ele vai conter o conteúdo html que será enviado para o cliente:
const bodyEmailHtml = (token) => {
return `<!DOCTYPE html>
<html>
<head>
<style>
.container {
margin: 10% 20%;
border-radius: 10px;
background-color: white;
}
.title {
padding: 1% 0% 0%;
text-align: center
}
p {
color:blue;
text-align: center;
font-family: Arial, sans-serif;
font-size: 30px;
padding-bottom: 5%;
}
</style>
</head>
<body>
<div class="container">
<div class="title">
<h3>Use the token below to finish login.</h3>
</div>
<hr/>
<p>${token}</p>
</div>
</body>
</html>`;
};
export default bodyEmailHtml;
Ainda dentro do mesmo diretório, crie um arquivo chamado email.js, nele vamos realmente implementar a funcionalidade de envio:
import IEmail from './../../domain/iemail.js';
import nodemailer from "nodemailer";
import bodyEmailHtml from './body-email-html.js';
export default class Email extends IEmail {
constructor() {
super();
}
async send(user) {
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
await transporter.sendMail({
from: process.env.EMAIL_USER,
to: user.email,
subject: "Hello, see you token",
html: bodyEmailHtml(user.emailToken)
});
}
}
Como podem ver, não tem nada de complexo, basicamente informamos a configuração de conexão, e após isso enviamos o e-mail para o remetente correto. Outra recomendação é olhar mais detalhadamente essa biblioteca para ter uma noção dos inúmeros outros recursos que ela oferece.
Bem pessoal, a partir de agora faremos algumas instalações bem úteis, porém só vamos configurá-las no próximo artigo. Vamos começar pelo hapi, que fará todo o intermédio entre as requisições web e a nossa aplicação:
npm i @hapi/boom @hapi/hapi @hapi/inert @hapi/vision hapi-swagger joi
Muitos devem conhecer o express ou outra lib que faça a mesma coisa, caso queira utilizar algo diferente do hapi, fiquem a vontade.
Em seguida, vamos instalar um orm, que vai servir para intermediar nossa comunicação e manipulação do banco, eu optei pelo sequelize, mas fique a vontade se quiser utilizar outro:
npm i pg pg-hstore sequelize
E por último, mas não menos importante, vamos instalar uma lib, que é bem útil enquanto você desenvolve.
npm i --save-dev nodemon
O nodemon é bem útil enquanto desenvolvemos, pois não precisaremos reiniciar o servidor a cada alteração. Para vocês que estão seguindo esses artigos, não será tão útil, devido eu ter feito de uma forma mais organizada, mas para mim que fazia o projeto e não segui exatamente a ordem dos artigos, ajudou bastante.
Outro ponto é que essa biblioteca, assim como a de teste, vai ser instalada somente para ambiente de desenvolvimento.
Resumo
Neste artigo, vimos a instalação e uso de algumas dependências, recomendo novamente que pesquisem um pouco mais sobre cada biblioteca instalada, seus recursos, etc.
Se você for um dev mais experiente, e conheça ou queira utilizar outras bibliotecas, deixei claro que você pode fazer isso tranquilamente. Repare que essas mudanças não afetam a nossa lógica principal, esse é um benefício de adotar uma arquitetura que tenha como foco a camada de regras de negócio. Até o próximo artigo.
Top comments (2)
Muito bom material.
Agreço muito por compartilhar!
Irei segui-lo desde o início.
Só não entendi pq a função loadEnv é async
...
const loadEnv = async () => {
...?
@mentorduncan nesse caso aqui realmente não precisaria ser async, acredito que eu tenha feito pensando em algo e no fim esqueci de remover.