使用 NodeJs/Knex/Nodemailer 的忘记密码功能无法正常工作

Forgot password functionality using NodeJs/Knex/Nodemailer and it is not working properly

注:我是第一次发帖,如果有什么意见请告诉我

目标:我正在构建一些端点,让用户在忘记密码时重设密码。流程如下所示:

  1. 用户不知道密码,所以他们点击了忘记密码。
  2. 用户输入电子邮件并点击发送
  3. 用户收到包含 link 的电子邮件以重置密码。单击 link 并被重定向以输入他们的新密码。
  4. 他们点击 'save',他们被重定向到登录以使用他们的新密码登录

我正在使用 Insomnia 来达到测试的终点。

有效的东西:

错误:

我尝试过的东西:

这是我的代码: 任何指导将不胜感激。如果您需要查看任何其他文件,请告诉我。

Forgot.password.js

const router = require('express').Router();
const crypto = require('crypto')
const User = require('../models/users.model')
const nodemailer = require('nodemailer')

router.post('/forgotpassword', (req, res) => {
  let {
    email
  } = req.body
  console.log(req.body)
  // if (req.body.email === '') {
  //   res.status(400).json({ message: 'Email is required'})
  // } console.error(req.body.email)
  User.findBy({
      email
    })
    .first()
    .then(user => {
      if (user === null) {
        res.status(403).json({
          message: 'Email not in db'
        })
      } else {
        const token = crypto.randomBytes(20).toString('hex')
        User.update({
          resetPasswordToken: token,
          resetPasswordExpires: Date.now() + 3600000,
        })
 
        const transporter = nodemailer.createTransport({
          service: 'gmail',
          auth: {
            user: `${process.env.EMAIL_USER}`,
            pass: `${process.env.EMAIL_PASS}`
          }
        })
        

        const mailOptions = {
          from: `${process.env.EMAIL_USER}`,
          to: `${user.email}`,
          subject: '[Promoquo] Reset Password Link',
          text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
            'Please click on the following link, or paste this into your browser to complete the process within one hour of receiving it:\n\n' +
            `http://localhost:5000/reset/${token}\n\n` +
            'If you did not request this, please ignore this email and your password will remain unchanged.\n',
        }
        transporter.sendMail(mailOptions, (err, res) => {
          if (err) {
            console.log('ERROR coming from forgot.password js and it sucks', err)
          } else {
            console.log('here is the res', res)
            res.status(200).json({
              message: 'recovery email sent hell yes'
            })
          }
        })
      }
      res.status(200).json({
        message: 'Reset password email has been sent WOOHOO '
      }) 
    })
    .catch(error => {
      res.status(500).json({
        message: 'ERROR on last catch forgotpassword.js, likely no user exists',
        error
      })
      console.log(error)
    })
})

module.exports = router

Update.password.js

const router = require('express').Router();
const passport = require('passport')
const bcrypt = require('bcrypt')
const User = require('../models/users.model')

const BCRYPT_SALT_ROUNDS = 12

router.put('/updatePasswordViaEmail', (req, res) => {
  User.find({
    where: {
      username: req.body.username,
      resetPasswordToken: req.body.resetPasswordToken,
      resetPasswordExpires: Date.now() + 3600000,
    }
  })
  .then(user => {
    if (user == null) {
      console.error('password reset link has expired')
      res.status(403).json({ message: 'Password reset link is invalid or has expired' })
    } else if (user != null) {
      console.log('user exists in db')
      bcrypt.hash(req.body.password, BCRYPT_SALT_ROUNDS)
      .then(hashedPassword => {
        User.update({
          password: hashedPassword,
          resetPasswordToken: null,
          resetPasswordExpires: null,
        })
      })
      .then(() => {
        console.log('log for THEN updating password')
        res.status(200).json({ message: 'password updated' })
      })
    } else {
      console.error('no user exists in db to update')
      res.status(401).json({ message: 'no user exists in db to update'})
    }
  })
})

module.exports = router

Users.model.js

const db = require('../dbConfig')

module.exports = {
  add,
  find,
  findBy,
  findById,
  findByEmail,
  findByType,
  update
};

function find() {
  return db('users').select('id', 'username', 'email', 'password');
}

function findBy(filter) {
  return db('users').where(filter);
}

async function add(user) {
  const [id] = await db('users').insert(user);

  return findById(id);
}

function findById(id) {
  return db('users').where({ id }).first();
}

function findByEmail(email) {
  return db('users').where({ email }).first();
}

function findByType(type) {
  return db('users').where({ type }).first();
}

function update(changes, id) {
  return db('users').where({ id }).update(changes)
}

20200913211559_users.js(这是table)

exports.up = function(knex) {
  return knex.schema.createTable('users', tbl => {
    tbl.increments();
    tbl.string('firstname', 30).notNullable();
    tbl.string('lastname', 30).notNullable();
    tbl.string('username', 30).notNullable()
    tbl.string('email', 50).notNullable()
    tbl.string('password', 128).notNullable();
    tbl.string('type').notNullable();
    tbl.boolean('confirmed').defaultTo('false');
    tbl.string('resetPasswordToken');
    tbl.date('resetPasswordExpires');
  })
};

exports.down = function(knex) {
  return knex.schema.dropTableIfExists('users')
};

您的 User.update() 行不是 运行(您需要 return 他们的承诺到承诺链中,或者挂钩到他们的回调中)。 async/await 是你的朋友,以避免“回调地狱”。

const user = await User.find({
  where: {
    username: req.body.username,
    resetPasswordToken: req.body.resetPasswordToken,
    resetPasswordExpires: Date.now() + 3600000,
  }
})
if (!user) { /* ... */ }
const token = crypto.randomBytes(20).toString('hex')
await User.update({ // await here!
  resetPasswordToken: token,
  resetPasswordExpires: Date.now() + 3600000,
})