diff --git a/controllers/mail.js b/controllers/mail.js index d540aae..ea970c4 100644 --- a/controllers/mail.js +++ b/controllers/mail.js @@ -1,6 +1,7 @@ var mailer = require('nodemailer'); var config = require('../config').config; - +var EventProxy = require('eventproxy').EventProxy; +var util = require('util'); mailer.SMTP = { host: config.mail_host, port: config.mail_port, @@ -9,13 +10,75 @@ mailer.SMTP = { pass: config.mail_pass }; -function send_mail(data,cb){ - mailer.send_mail(data,function(err,success){ - return cb(err,success); - }); +/** + * keep all the mails to send + * @type {Array} + */ +var mails = []; +var timer; +/** + * control mailer + * @type {EventProxy} + */ +var mailEvent = new EventProxy(); +/** + * when need to send an email, start to check the mails array and send all of emails. + */ +mailEvent.on("getMail", function() { + if(mails.length === 0) { + return; + } else { + //遍历邮件数组,发送每一封邮件,如果有发送失败的,就再压入数组,同时触发mailEvent事件 + var failed = false; + for(var i = 0, len = mails.length; i != len; ++i) { + var message = mails[i]; + mails.splice(i, 1); + i--; + len--; + var mail; + try { + message.debug = false; + mail = mailer.send_mail(message, function(error, success) { + if(error) { + mails.push(message); + failed = true; + } + }); + } catch(e) { + mails.push(message); + failed = true; + } + if(mail) { + var oldemit = mail.emit; + mail.emit = function() { + oldemit.apply(mail, arguments); + } + } + } + if(failed) { + clearTimeout(timer); + timer = setTimeout(trigger, 60000); + } + } +}); + +/** + * trigger email event + * @return {[type]} + */ +function trigger() { + mailEvent.trigger("getMail"); +} +/** + * send an email + * @param {mail} data [info of an email] + */ +function send_mail (data) { + mails.push(data); + trigger(); } -function send_active_mail(who,token,name,email,cb){ +function send_active_mail(who, token, name, email, cb) { var sender = config.mail_sender; var to = who; var subject = config.name + '社区帐号激活'; @@ -31,17 +94,15 @@ function send_active_mail(who,token,name,email,cb){ subject: subject, html: html } - - send_mail(data,function(err,success){ - return cb(err,success); - }); + cb (null, true); + send_mail(data); } -function send_reset_pass_mail(who,token,name,cb){ +function send_reset_pass_mail(who, token, name, cb) { var sender = config.mail_sender; var to = who; var subject = config.name + '社区密码重置'; var html = '

您好:

' + - '

我们收到您在' + config.name + '社区重置密码的请求,请单击下面的链接来重置密码:

' + + '

我们收到您在' + config.name + '社区重置密码的请求,请在24小时内单击下面的链接来重置密码:

' + '重置密码链接' + '

若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。

' + '

' + config.name +'社区 谨上。

' @@ -53,11 +114,11 @@ function send_reset_pass_mail(who,token,name,cb){ html: html } - send_mail(data,function(err,success){ - return cb(err,success); - }); + cb (null, true); + send_mail(data); } -function send_reply_mail(who,msg){ + +function send_reply_mail(who, msg) { var sender = config.mail_sender; var to = who; var subject = config.name + ' 新消息'; @@ -76,10 +137,11 @@ function send_reply_mail(who,msg){ html: html } - send_mail(data,function(err,success){}); + send_mail(data); } -function send_at_mail(who,msg){ + +function send_at_mail(who, msg) { var sender = config.mail_sender; var to = who; var subject = config.name + ' 新消息'; @@ -98,7 +160,7 @@ function send_at_mail(who,msg){ html: html } - send_mail(data,function(err,success){}); + send_mail(data); } exports.send_active_mail = send_active_mail; diff --git a/controllers/sign.js b/controllers/sign.js index 24ba7fb..c40d519 100644 --- a/controllers/sign.js +++ b/controllers/sign.js @@ -100,7 +100,14 @@ exports.showLogin = function(req, res) { req.session._loginReferer = req.headers.referer; res.render('sign/signin'); }; - +/** + * define some page when login just jump to the home page + * @type {Array} + */ +var notJump = [ + '/active_account', //active page + '/reset_pass' //reset password page, avoid to reset twice +]; /** * Handle user login. * @@ -131,7 +138,15 @@ exports.login = function(req, res, next) { } // store session cookie gen_session(user, res); - res.redirect(req.session._loginReferer || 'home'); + //check at some page just jump to home page + var refer = req.session._loginReferer || 'home'; + for (var i=0, len=notJump.length; i!=len; ++i) { + if (refer.indexOf(notJump[i]) >= 0) { + refer = 'home'; + break; + } + } + res.redirect(refer); }); }; @@ -179,35 +194,75 @@ exports.search_pass = function(req,res,next){ return; } - User.findOne({email:email},function(err,user){ - if(!user){ - res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email}); - return; - } - mail_ctrl.send_reset_pass_mail(email,md5(email+config.session_secret),user.name,function(err,success){ - res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请点击里面的链接来重置密码。'}); + // User.findOne({email:email},function(err,user){ + //动态生成retrive_key和timestamp到users collection,之后重置密码进行验证 + var retrieveKey = randomString(15); + var retrieveTime = new Date().getTime(); + User.findOne({email : email}, function(err, user) { + if(!user) { + res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email}); + return; + } + user.retrieve_key = retrieveKey; + user.retrieve_time = retrieveTime; + user.save(function(err) { + if(err) { + return next(err); + } + mail_ctrl.send_reset_pass_mail(email, retrieveKey, user.name, function(err,success) { + res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请在24小时内点击里面的链接来重置密码。'}); + }); }); }); } } - -exports.reset_pass = function(req,res,next){ - var key = req.query.key; - var name = req.query.name; - var new_pass = ''; - - User.findOne({name:name},function(err,user){ - if(!user || md5(user.email+config.session_secret) != key){ - res.render('notify/notify',{error: '信息有误,密码无法重置。'}); - return; - } - new_pass = random_password(); - user.pass = md5(new_pass); - user.save(function(err){ - res.render('notify/notify',{success: '你的密码已被重置为:' + new_pass + ',请立即用此密码登录后在设置页面更改密码。'}); - }); - }); - +/** + * reset password + * 'get' to show the page, 'post' to reset password + * after reset password, retrieve_key&time will be destroy + * @param {http.req} req + * @param {http.res} res + * @param {Function} next + */ +exports.reset_pass = function(req,res,next) { + var method = req.method.toLowerCase(); + if(method === 'get') { + var key = req.query.key; + var name = req.query.name; + User.findOne({name:name, retrieve_key:key},function(err,user) { + if(!user) { + return res.render('notify/notify',{error: '信息有误,密码无法重置。'}); + } + var now = new Date().getTime(); + var oneDay = 1000 * 60 * 60 * 24; + if(!user.retrieve_time || now - user.retrieve_time > oneDay) { + return res.render('notify/notify', {error : '该链接已过期,请重新申请。'}); + } + return res.render('sign/reset', {name : name, key : key}); + }); + } else { + var psw = req.body.psw || ''; + var repsw = req.body.repsw || ''; + var key = req.body.key || ''; + var name = req.body.name || ''; + if(psw !== repsw) { + return res.render('sign/reset', {name : name, key : key, error : '两次密码输入不一致。'}); + } + User.findOne({name:name, retrieve_key: key}, function(err, user) { + if(!user) { + return res.render('notify/notify', {error : '错误的激活链接'}); + } + user.pass = md5(psw); + user.retrieve_key = null; + user.retrieve_time = null; + user.save(function(err) { + if(err) { + return next(err); + } + return res.render('notify/notify', {success: '你的密码已重置。'}); + }) + }) + } } // auth_user middleware @@ -250,30 +305,30 @@ exports.auth_user = function(req,res,next){ }; // private -function gen_session(user,res){ +function gen_session(user,res) { var auth_token = encrypt(user._id + '\t'+user.name + '\t' + user.pass +'\t' + user.email, config.session_secret); res.cookie(config.auth_cookie_name, auth_token, {path: '/',maxAge: 1000*60*60*24*7}); //cookie 有效期1周 } -function encrypt(str,secret){ +function encrypt(str,secret) { var cipher = crypto.createCipher('aes192', secret); var enc = cipher.update(str,'utf8','hex'); enc += cipher.final('hex'); return enc; } -function decrypt(str,secret){ +function decrypt(str,secret) { var decipher = crypto.createDecipher('aes192', secret); var dec = decipher.update(str,'hex','utf8'); dec += decipher.final('utf8'); return dec; } -function md5(str){ +function md5(str) { var md5sum = crypto.createHash('md5'); md5sum.update(str); str = md5sum.digest('hex'); return str; } -function random_password(passwd_size){ - var size = passwd_size || 6; +function randomString(size) { + size = size || 6; var code_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var max_num = code_string.length + 1; var new_pass = ''; diff --git a/models/user.js b/models/user.js index 91dcba5..82647b0 100644 --- a/models/user.js +++ b/models/user.js @@ -28,7 +28,10 @@ var UserSchema = new Schema({ receive_reply_mail: {type: Boolean, default: false }, receive_at_mail: { type: Boolean, default: false }, - from_wp: { type: Boolean } + from_wp: { type: Boolean }, + + retrieve_time : {type: Number}, + retrieve_key : {type: String} }); mongoose.model('User', UserSchema); diff --git a/routes.js b/routes.js index e79d27c..7a9b475 100644 --- a/routes.js +++ b/routes.js @@ -35,6 +35,7 @@ exports = module.exports = function(app) { app.get('/search_pass', sign.search_pass); app.post('/search_pass', sign.search_pass); app.get('/reset_pass', sign.reset_pass); + app.post('/reset_pass', sign.reset_pass); // user app.get('/user/:name', user.index); diff --git a/views/sign/reset.html b/views/sign/reset.html new file mode 100644 index 0000000..42a8fbd --- /dev/null +++ b/views/sign/reset.html @@ -0,0 +1,42 @@ +<%- partial('sign/sidebar') %> + +
+
+
+ +
+
+
+
+ <% if(locals.error){ %> +
+ × + <%= error %> +
+ <% } %> +
+
+ +
+ +
+
+
+ +
+ +
+
+ + + +
+ +
+
+
+
+