You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js与React.js中RangeError: Maximum call stack size exceeded报错排查

错误原因
  • 核心原因:password虚拟属性setter死循环
    你为password虚拟属性定义的setter中直接执行了this.password = password,这行代码会再次触发password虚拟属性的setter逻辑,形成无限递归调用,最终触发调用栈溢出。你提交的报错日志中反复触发auth.model.js第34行逻辑就是这个setter的循环调用导致。
  • 次要问题1:用户模型配置错误
    1. 重复定义了name字段,后定义的name会覆盖前一个配置,丢失了unique: true、lowercase: true的约束
    2. 缺失email字段定义,无法存储用户邮箱信息
    3. Schema配置项拼写错误,正确写法是timestamps: true,而非timeStamp: true
    4. 缺失crypto模块引入,加密密码时会报错
  • 次要问题2:控制器逻辑错误
    1. activationController中嵌套重复定义了自身,属于冗余错误代码
    2. registerController中User.findOne是异步操作,你没有等待查询结果就直接生成token发送激活邮件,即便邮箱已被占用,依然会发送激活邮件,逻辑错误
    3. activationController中已经通过jwt.verify拿到了解密后的decoded数据,不需要再重复调用jwt.decode解析token
修复方案

1. 修复用户模型代码

const mongoose = require('mongoose')
// 补充缺失的crypto引入
const crypto = require('crypto')

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        trim: true,
        required: true,
        unique: true,
        lowercase: true
    },
    // 补充缺失的email字段定义
    email: {
        type: String,
        trim: true,
        required: true,
        unique: true,
        lowercase: true
    },
    hashed_password: {
        type: String,
        required: true
    },
    salt: String,
    role: {
        type: String,
        default: 'Normal'
    },
    resetPasswordLink: {
        data: String,
        default: ''
    }
// 修正配置项拼写错误
}, {timestamps: true})

// 修复password虚拟属性setter死循环
userSchema.virtual('password')
.set(function(password){
    // 赋值给私有属性_password,避免触发虚拟属性setter
    this._password = password
    this.salt = this.makeSalt()
    this.hashed_password = this.encryptPassword(password)
})
.get(function(){
    return this._password
})

userSchema.methods = {
    makeSalt: function(){
        return Math.round(new Date().valueOf() * Math.random()) + ''
    },
    encryptPassword: function(password){
        if(!password) return ''
        try{
            return crypto
            .createHmac('sha1', this.salt)
            .update(password)
            .digest('hex')
        } catch (err) {
            return ''
        }
    },
    authenticate: function (plainPassword) {
        return this.encryptPassword(plainPassword) === this.hashed_password
    }
}

module.exports = mongoose.model('User', userSchema)

2. 修复activationController代码

exports.activationController = (req, res) => {
  const { token } = req.body;

  if (token) {
    jwt.verify(token, process.env.JWT_ACCOUNT_ACTIVATION, (err, decoded) => {
      if (err) {
        return res.status(401).json({
          errors: 'Expired link. Signup again'
        });
      }
      // 直接使用verify返回的decoded结果,无需重复解析
      const { name, email, password } = decoded;
      const user = new User({
        name,
        email,
        password
      });

      user.save((err, user) => {
        if (err) {
          return res.status(401).json({
            errors: 'User cannot be saved.'
          });
        }
        return res.json({
          success: true,
          message: 'Signup success'
        });
      });
    });
  } else {
    return res.json({
      message: 'Please try again.'
    });
  }
};

3. 修复registerController异步逻辑

exports.registerController = (req, res) => {
    const { name, email, password } = req.body;
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
      const firstError = errors.array().map(error => error.msg)[0];
      return res.status(422).json({
        errors: firstError
      });
    }

    User.findOne({ email }).exec((err, user) => {
      if (err) {
        return res.status(500).json({ errors: '服务器内部错误' });
      }
      if (user) {
        return res.status(400).json({
          errors: 'Email is taken'
        });
      }
      // 确认邮箱未被占用后再生成token发送邮件
      const token = jwt.sign(
        { name, email, password },
        process.env.JWT_ACCOUNT_ACTIVATION,
        { expiresIn: '5m' }
      );

      const emailData = {
        from: process.env.EMAIL_FROM,
        to: email,
        subject: 'Account activation link',
        html: `
                  <h1>Please use the following to activate your account</h1>
                  <button style="padding: 15px; background-color: blue; text-align:center; opacity: 0.2; outline: none; border-radius: 10px;"><a style="text-decoration: none; color: white;" href="${process.env.CLIENT_URL}/users/activate/${token}">Activate your account</a></button>
                  <hr />
                  <p>This email may contain sensitive information</p>
                  <p>${process.env.CLIENT_URL}</p>
              `
      };
      // 此处补充调用邮件发送服务的逻辑
    });
};

内容的提问来源于stack exchange,提问作者Afolabi Opeyemi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 07:36:07