From ef14163a16b63f71efc122281df760f45fa56a36 Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 13 Jul 2020 09:18:17 -0300 Subject: [PATCH 1/3] adds supply create action under permissions --- src/controllers/supply.controller.ts | 22 ++++++++++++++++++---- src/models/supply.model.ts | 9 +++------ src/routes/private.ts | 2 +- src/utils/rbac_abac.ts | 1 + 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/controllers/supply.controller.ts b/src/controllers/supply.controller.ts index 6a3f336..90bb732 100644 --- a/src/controllers/supply.controller.ts +++ b/src/controllers/supply.controller.ts @@ -12,13 +12,27 @@ class SupplyController implements BaseController{ } public create = async (req: Request, res: Response): Promise => { - const { id, name, unity, sex, image } = req.body; + const { + name, + activePrinciple, + power, + unity, + firstPresentation, + secondPresentation, + description, + observation, + pharmaceutical_form + } = req.body; const newSupply: ISupply = new Supply({ - id, name, + activePrinciple, + power, unity, - sex, - image + firstPresentation, + secondPresentation, + description, + observation, + pharmaceutical_form }); try{ await newSupply.save(); diff --git a/src/models/supply.model.ts b/src/models/supply.model.ts index 09fb439..077a4ac 100644 --- a/src/models/supply.model.ts +++ b/src/models/supply.model.ts @@ -31,12 +31,9 @@ export const supplySchema = new Schema({ }, observation: { type: String, - }, - createdAt: { - type: Date, - default: Date.now - }, - updatedAt: Date, + } +},{ + timestamps: true }); // Model diff --git a/src/routes/private.ts b/src/routes/private.ts index a7e927a..9395c81 100644 --- a/src/routes/private.ts +++ b/src/routes/private.ts @@ -89,8 +89,8 @@ class PrivateRoutes{ // supply this.router.get(`/supplies/`, hasPermissionIn('readAny','patient'), supplyController.index); + this.router.post(`/supplies/`, hasPermissionIn('createAny','supplies'), supplyController.create); this.router.patch('/supplies/:id', hasPermissionIn('updateAny','supplies'), supplyController.update); - // this.router.post(`/supplies/`, hasPermissionIn('createAny','patient'), supplyController.create); // this.router.get(`/supplies/:id`, hasPermissionIn('readAny','patient'), supplyController.show); // this.router.put(`/supplies/:id`, hasPermissionIn('updateAny','patient'), supplyController.update); // this.router.delete(`/supplies/:id`, hasPermissionIn('deleteAny','patient'), supplyController.delete); diff --git a/src/utils/rbac_abac.ts b/src/utils/rbac_abac.ts index 6ccafa8..fc07276 100644 --- a/src/utils/rbac_abac.ts +++ b/src/utils/rbac_abac.ts @@ -53,6 +53,7 @@ class AccessControlLoader { // supplies { role: 'professional', resource: 'supplies', action: 'read:any', attributes: '*' }, { role: 'pharmacist', resource: 'supplies', action: 'read:any', attributes: '*' }, + { role: 'admin', resource: 'supplies', action: 'create:any', attributes: '*' }, { role: 'admin', resource: 'supplies', action: 'update:any', attributes: '*' } ]; this.accessControl.setGrants(grantList); From ff2ed2f9a7ba093af5f6a519f334303e095dfe0a Mon Sep 17 00:00:00 2001 From: plammel Date: Thu, 11 Feb 2021 15:32:44 -0300 Subject: [PATCH 2/3] feat: incluye recuperar password via mail --- .gitignore | 3 + config.private.example.ts | 27 +++++ package-lock.json | 39 ++++++- package.json | 2 + src/controllers/auth.controller.ts | 137 +++++++++++++++++-------- src/interfaces/user.interface.ts | 1 + src/models/user.model.ts | 5 +- src/routes/auth.ts | 23 ++++- src/utils/roboSender/sendEmail.ts | 73 +++++++++++++ templates/emails/recover-password.html | 12 +++ tsconfig.json | 2 +- 11 files changed, 274 insertions(+), 50 deletions(-) create mode 100644 config.private.example.ts create mode 100644 src/utils/roboSender/sendEmail.ts create mode 100644 templates/emails/recover-password.html diff --git a/.gitignore b/.gitignore index e8d1c81..4a90b82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Archivos de configuración +config.private.ts + # Logs logs *.log diff --git a/config.private.example.ts b/config.private.example.ts new file mode 100644 index 0000000..201c298 --- /dev/null +++ b/config.private.example.ts @@ -0,0 +1,27 @@ +function getEnv(key: any, _default: any, type = 's') { + if (!!process.env[key] === false) { + return _default; + } + const value: any = process.env[key]; + switch (type) { + case 'b': + return value.toLowerCase() === 'true'; + case 'n': + return parseInt(value, 10); + default: + return value; + } +} +​ +// E-mail server settings +export const enviarMail = { + host: getEnv('EMAIL_HOST', 'smtp.gmail.com'), + port: getEnv('EMAIL_PORT', 587, 'n'), + secure: getEnv('EMAIL_SECURE', false, 'b'), + auth: { + user: getEnv('EMAIL_USERNAME', 'xxxxxxxxx@gmail.com'), + pass: getEnv('EMAIL_PASSWORD', 'xxxxxxx') + } +}; + +export const APP_DOMAIN = getEnv('APP_DOMAIN', 'http://localhost:4200'); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b4af5a6..978f25c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -933,6 +933,18 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1369,8 +1381,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "moment": { "version": "2.25.3", @@ -1518,11 +1529,21 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "nocache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, + "nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ==" + }, "nodemon": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", @@ -2016,8 +2037,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-support": { "version": "0.5.16", @@ -2191,6 +2211,12 @@ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, + "uglify-js": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.7.tgz", + "integrity": "sha512-SIZhkoh+U/wjW+BHGhVwE9nt8tWJspncloBcFapkpGRwNPqcH8pzX36BXe3TPBjzHWPMUZotpCigak/udWNr1Q==", + "optional": true + }, "undefsafe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", @@ -2294,6 +2320,11 @@ "string-width": "^2.1.1" } }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", diff --git a/package.json b/package.json index 040a5ee..f9eb25e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", + "handlebars": "^4.7.6", "helmet": "^3.21.2", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.15", @@ -31,6 +32,7 @@ "mongoose": "^5.6.9", "morgan": "^1.9.1", "needle": "^2.4.1", + "nodemailer": "^6.4.17", "passport": "^0.4.1", "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 2145b06..54c8fc3 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -8,11 +8,13 @@ import IUser from '../interfaces/user.interface'; import User from '../models/user.model'; import IRole from '../interfaces/role.interface'; import Role from '../models/role.model'; +import { renderHTML, MailOptions, sendMail } from '../utils/roboSender/sendEmail'; +import { enviarMail, APP_DOMAIN } from '../../config.private'; -class AuthController{ +class AuthController { public register = async (req: Request, res: Response): Promise => { - try{ + try { const { username, email, enrollment, cuil, businessName, password, roleType } = req.body; const newUser = new User({ username, email, password, enrollment, cuil, businessName }); const role: IRole = await Role.schema.methods.findByRoleOrCreate(roleType); @@ -21,13 +23,13 @@ class AuthController{ await newUser.save(); await role.save(); return res.status(200).json({ - newUser + newUser }); - }catch(e){ + } catch (e) { let errors: { [key: string]: string } = {}; Object.keys(e.errors).forEach(prop => { - errors[ prop ] = e.errors[prop].message; + errors[prop] = e.errors[prop].message; }); return res.status(422).json(errors); } @@ -36,15 +38,29 @@ class AuthController{ public resetPassword = async (req: Request, res: Response): Promise => { const { _id } = req.user as IUser; const { oldPassword, newPassword } = req.body; - try{ + try { const user: IUser | null = await User.findOne({ _id }); - if(!user) return res.status(404).json('No se ha encontrado el usuario'); + if (!user) return res.status(404).json('No se ha encontrado el usuario'); const isMatch: boolean = await User.schema.methods.isValidPassword(user, oldPassword); - if(!isMatch) return res.status(401).json({ message: 'Su contraseña actual no coincide con nuestros registros'}); + if (!isMatch) return res.status(401).json({ message: 'Su contraseña actual no coincide con nuestros registros' }); - await user.update({password: newPassword}); + await user.update({ password: newPassword }); return res.status(200).json('Se ha modificado la contraseña correctamente!'); - }catch(err){ + } catch (err) { + console.log(err); + return res.status(500).json('Server Error'); + } + } + + public recoverPassword = async (req: Request, res: Response): Promise => { + const { authenticationToken, newPassword } = req.body; + try { + const user: IUser | null = await User.findOne({ authenticationToken: authenticationToken }); + if (!user) return res.status(404).json('No se ha encontrado el usuario'); + + await user.update({ password: newPassword }); + return res.status(200).json('Se ha modificado la contraseña correctamente!'); + } catch (err) { console.log(err); return res.status(500).json('Server Error'); } @@ -52,19 +68,19 @@ class AuthController{ public login = async (req: Request, res: Response): Promise => { const { _id } = req.user as IUser; - try{ + try { - const user: IUser | null = await User.findOne({_id}).populate({path: 'roles', select: 'role'}); + const user: IUser | null = await User.findOne({ _id }).populate({ path: 'roles', select: 'role' }); - if(user){ + if (user) { const roles: string | string[] = []; - await Promise.all(user.roles.map( async (role) => { + await Promise.all(user.roles.map(async (role) => { roles.push(role.role); })); const token = this.signInToken(user._id, user.username, user.businessName, roles); const refreshToken = uuidv4(); - await User.updateOne({_id: user._id}, {refreshToken: refreshToken}); + await User.updateOne({ _id: user._id }, { refreshToken: refreshToken }); return res.status(200).json({ jwt: token, refreshToken: refreshToken @@ -72,7 +88,7 @@ class AuthController{ } return res.status(httpCodes.EXPECTATION_FAILED).json('Debe iniciar sesión');//in the case that not found user - }catch(err){ + } catch (err) { console.log(err); return res.status(500).json('Server Error'); } @@ -80,10 +96,10 @@ class AuthController{ public logout = async (req: Request, res: Response): Promise => { const { refreshToken } = req.body; - try{ + try { await User.findOneAndUpdate({ refreshToken: refreshToken }, { refreshToken: '' }); return res.status(204).json('Logged out successfully!'); - }catch(err){ + } catch (err) { console.log(err); return res.status(500).json("Server error"); } @@ -91,21 +107,21 @@ class AuthController{ public refresh = async (req: Request, res: Response): Promise => { const refreshToken = req.body.refreshToken; - try{ - const user: IUser | null = await User.findOne({refreshToken: refreshToken }).populate({path: 'roles', select: 'role'}); + try { + const user: IUser | null = await User.findOne({ refreshToken: refreshToken }).populate({ path: 'roles', select: 'role' }); - if(user){ + if (user) { // in next version, should embed roles information const roles: string | string[] = []; - await Promise.all(user.roles.map( async (role) => { - roles.push(role.role); + await Promise.all(user.roles.map(async (role) => { + roles.push(role.role); })); const token = this.signInToken(user._id, user.username, user.businessName, roles); // generate a new refresh_token const refreshToken = uuidv4(); - await User.updateOne({_id: user._id}, {refreshToken: refreshToken}); + await User.updateOne({ _id: user._id }, { refreshToken: refreshToken }); return res.status(200).json({ jwt: token, refreshToken: refreshToken @@ -114,7 +130,7 @@ class AuthController{ return res.status(httpCodes.EXPECTATION_FAILED).json('Debe iniciar sesión');//in the case that not found user - }catch(err){ + } catch (err) { console.log(err); return res.status(500).json('Server error'); } @@ -126,23 +142,23 @@ class AuthController{ // son los campos que permitiremos actualizar. const { id } = req.params; const values: any = {}; - try{ + try { _(req.body).forEach((value: string, key: string) => { - if (!_.isEmpty(value) && _.includes(["email", "password", "username", "enrollment", "cuil", "businessName"], key)){ + if (!_.isEmpty(value) && _.includes(["email", "password", "username", "enrollment", "cuil", "businessName"], key)) { values[key] = value; } }); const opts: any = { runValidators: true, new: true, context: 'query' }; - const user: IUser | null = await User.findOneAndUpdate({_id: id}, values, opts).select("username email cuil enrollment businessName"); + const user: IUser | null = await User.findOneAndUpdate({ _id: id }, values, opts).select("username email cuil enrollment businessName"); return res.status(200).json(user); - }catch(e){ + } catch (e) { // formateamos los errores de validacion - if(e.name !== 'undefined' && e.name === 'ValidationError'){ + if (e.name !== 'undefined' && e.name === 'ValidationError') { let errors: { [key: string]: string } = {}; Object.keys(e.errors).forEach(prop => { - errors[ prop ] = e.errors[prop].message; + errors[prop] = e.errors[prop].message; }); return res.status(422).json(errors); } @@ -154,15 +170,15 @@ class AuthController{ public getUser = async (req: Request, res: Response): Promise => { // obtenemos los datos del usuario, buscando por: "email" / "username" / "cuil" const { email, username, cuil } = req.body; - try{ + try { const users: IUser[] | null = await User.find({ - $or: [{"email": email}, {"username": username}, {"cuil": cuil}] + $or: [{ "email": email }, { "username": username }, { "cuil": cuil }] }).select("username email cuil enrollment, businessName"); - if(!users) return res.status(400).json('Usuario no encontrado'); + if (!users) return res.status(400).json('Usuario no encontrado'); return res.status(200).json(users); - }catch(err){ + } catch (err) { console.log(err); return res.status(500).json("Server Error"); } @@ -171,16 +187,16 @@ class AuthController{ public assignRole = async (req: Request, res: Response): Promise => { const { id } = req.params; const { roleId } = req.body; - try{ - const role: IRole | null = await Role.findOne({ _id : roleId }); - if(role){ - await User.findByIdAndUpdate({ _id: id },{ + try { + const role: IRole | null = await Role.findOne({ _id: roleId }); + if (role) { + await User.findByIdAndUpdate({ _id: id }, { roles: role }); } - const user: IUser | null = await User.findOne({ _id : id }); + const user: IUser | null = await User.findOne({ _id: id }); return res.status(200).json(user); - }catch(err){ + } catch (err) { console.log(err); return res.status(500).json('Server Error'); } @@ -201,6 +217,45 @@ class AuthController{ return token; } + /** + * Envía un link para recuperar la contraseña en caso qeu sea un usuario temporal con email (fuera de onelogin). + * AuthUser + */ + public setValidationTokenAndNotify = async (username: string) => { + try { + let usuario: any = await User.findOne({ username }); + if (usuario) { + usuario.authenticationToken = uuidv4(); + console.log(usuario) + await usuario.save(); + + const extras: any = { + titulo: 'Recuperación de contraseña', + usuario, + url: `${APP_DOMAIN}/auth/recovery-password/${usuario.authenticationToken}`, + }; + console.log('extras', extras) + const htmlToSend = await renderHTML('emails/recover-password.html', extras); + + const options: MailOptions = { + from: enviarMail.auth.user, + to: usuario.email.toString(), + subject: 'Recuperación de contraseña', + text: '', + html: htmlToSend, + attachments: null + }; + + await sendMail(options); + return usuario; + } else { + return null; + } + } catch (error) { + throw error; + } + } + } export default new AuthController(); diff --git a/src/interfaces/user.interface.ts b/src/interfaces/user.interface.ts index 3ad8a89..88e2e43 100644 --- a/src/interfaces/user.interface.ts +++ b/src/interfaces/user.interface.ts @@ -9,6 +9,7 @@ export default interface IUser extends Document{ password: string; roles: IRole[]; refreshToken?: string; + authenticationToken?: string; createdAt?: Date; updatedAt?: Date; isValidPassword(thisUser: IUser, password: string): Promise; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 05b0b8f..6e33947 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -60,6 +60,9 @@ export const userSchema = new Schema({ refreshToken: { type: String, }, + authenticationToken: { + type: String, + }, createdAt: { type: Date, default: Date.now @@ -68,7 +71,7 @@ export const userSchema = new Schema({ }); // Model -const User: Model = model('User', userSchema); +const User: Model = model('User', userSchema, 'User'); // Model methods User.schema.method('isValidPassword', async function(thisUser: IUser, password: string): Promise{ diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 649c907..0c86c1a 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -1,18 +1,35 @@ -import { Router} from 'express'; +import { Router } from 'express'; import { passportMiddlewareLocal, checkAuth } from '../middlewares/passport-config.middleware'; import authController from '../controllers/auth.controller'; class AuthRoutes { - constructor(private router: Router = Router()){} + constructor(private router: Router = Router()) { } - routes(): Router{ + routes(): Router { this.router.post('/login', passportMiddlewareLocal, authController.login); this.router.get('/jwt-login', checkAuth, authController.login); // this.router.post('/register', authController.register); this.router.post('/logout', authController.logout); this.router.post('/refresh', authController.refresh); + this.router.post('/recovery-password', authController.recoverPassword); + this.router.post('/setValidationTokenAndNotify', async (req, res, next) => { + try { + const username = req.body.usuario; + if (username) { + const user = await authController.setValidationTokenAndNotify(username); + if(user) { + res.json( { status: 'ok', msg: 'Se ha enviado un correo a su casilla!'}); + } else { + res.json( { status: 'notfound', msg: 'Usuario no encontrado! Por favor revise sus datos'}); + } + return next(403); + } + } catch (error) { + return next(error); + } + }); return this.router; } diff --git a/src/utils/roboSender/sendEmail.ts b/src/utils/roboSender/sendEmail.ts new file mode 100644 index 0000000..d5cc9f4 --- /dev/null +++ b/src/utils/roboSender/sendEmail.ts @@ -0,0 +1,73 @@ +import { enviarMail } from '../../../config.private'; +import * as fs from 'fs'; +import moment = require('moment'); +const handlebars = require('handlebars'); +const path = require('path'); +const nodemailer = require('nodemailer'); + +handlebars.registerHelper('datetime', (dateTime: any) => { + return moment(dateTime).format('D MMM YYYY [a las] H:mm [hs]'); +}); + +export interface MailOptions { + from: string; + to: string; + subject: string; + text: string; + html: string; + attachments: any; +} + +export function sendMail(options: MailOptions) { + return new Promise((resolve, reject) => { + const transporter = nodemailer.createTransport({ + host: enviarMail.host, + port: enviarMail.port, + secure: enviarMail.secure, + auth: enviarMail.auth, + }); + + const mailOptions = { + from: options.from, + to: options.to, + subject: options.subject, + text: options.text, + html: options.html, + attachments: options.attachments + }; + + transporter.sendMail(mailOptions, (error: any, info: any) => { + if (error) { + return reject(error); + } + return resolve(info); + }); + }); +} + +export function renderHTML(templateName: string, extras: any): Promise { + return new Promise((resolve, reject) => { + // [TODO] Analizar el path relativo o absoluto + const TEMPLATE_PATH = './templates/'; + const url = path.join(TEMPLATE_PATH, templateName); + fs.readFile(url, { encoding: 'utf-8' }, (err, html) => { + if (err) { + return reject(err); + } + try { + const template = handlebars.compile(html); + const htmlToSend = template(extras); + return resolve(htmlToSend); + } catch (exp) { + return reject(exp); + } + + }); + }); +} + +export function registerPartialTemplate(key: string, fileName: string) { + const filePath = path.join(process.cwd(), `templates/${fileName}`); + const file = fs.readFileSync(filePath); + handlebars.registerPartial(key, file.toString()); +} diff --git a/templates/emails/recover-password.html b/templates/emails/recover-password.html new file mode 100644 index 0000000..9af753c --- /dev/null +++ b/templates/emails/recover-password.html @@ -0,0 +1,12 @@ +{{#> layout }} +{{#with usuario}} +
+ Hola {{nombre}} {{apellido}}
+
+

+ Recibiste este mensaje porque solicitaste cambiar tu contraseña. +

+{{/with}} +
+Para obtener una nueva contraseña, dirigite al formulario de Regeneración de contraseña +{{/layout}} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index e687315..7f8d846 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -64,7 +64,7 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, "include": [ - "./src/**/*" + "./src/**/*", "config.private.ts" ], "exclude": [ "node_modules" From f6241e53ff9167270174074dd4c0058d845181ca Mon Sep 17 00:00:00 2001 From: plammel Date: Wed, 9 Mar 2022 11:06:29 -0300 Subject: [PATCH 3/3] fix-response-post-token --- src/routes/auth.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 0c86c1a..053ef48 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -20,13 +20,14 @@ class AuthRoutes { if (username) { const user = await authController.setValidationTokenAndNotify(username); if(user) { - res.json( { status: 'ok', msg: 'Se ha enviado un correo a su casilla!'}); + return res.json( { status: 'ok', msg: 'Se ha enviado un correo a su casilla!'}); } else { - res.json( { status: 'notfound', msg: 'Usuario no encontrado! Por favor revise sus datos'}); + return res.json( { status: 'notfound', msg: 'Usuario no encontrado! Por favor revise sus datos'}); } - return next(403); } - } catch (error) { + return next(403); + + } catch (error) { return next(error); } });