mirror of
https://github.com/cnodejs/nodeclub.git
synced 2026-02-01 16:34:28 +00:00
Merge pull request #20 from dead-horse/master
修复今天的几个问题,修改找回密码逻辑,修改登录跳转逻辑;发送邮件时,不等待邮件发送成功先通知用户,避免用户等待时间过长。邮件压入队列,如果失败1分钟后再次发送
This commit is contained in:
commit
42d8dedf71
@ -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 = '<p>您好:<p/>' +
|
||||
'<p>我们收到您在' + config.name + '社区重置密码的请求,请单击下面的链接来重置密码:</p>' +
|
||||
'<p>我们收到您在' + config.name + '社区重置密码的请求,请在24小时内单击下面的链接来重置密码:</p>' +
|
||||
'<a href="' + config.host + '/reset_pass?key=' + token + '&name=' + name + '">重置密码链接</a>' +
|
||||
'<p>若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。</p>' +
|
||||
'<p>' + config.name +'社区 谨上。</p>'
|
||||
@ -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;
|
||||
|
||||
@ -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 = '';
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
42
views/sign/reset.html
Normal file
42
views/sign/reset.html
Normal file
@ -0,0 +1,42 @@
|
||||
<%- partial('sign/sidebar') %>
|
||||
|
||||
<div id='content'>
|
||||
<div class='panel'>
|
||||
<div class='header'>
|
||||
<ul class='breadcrumb'>
|
||||
<li><a href='/'>主页</a><span class='divider'>/</span></li>
|
||||
<li class='active'>重置密码</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='inner'>
|
||||
<div class='sep10'></div>
|
||||
<div class='sep10'></div>
|
||||
<% if(locals.error){ %>
|
||||
<div class="alert alert-error">
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||
<strong><%= error %></strong>
|
||||
</div>
|
||||
<% } %>
|
||||
<form id='signin_form' class='form-horizontal' action='/reset_pass' method='post'>
|
||||
<div class='control-group'>
|
||||
<label class='control-label' for='psw'>新密码</label>
|
||||
<div class='controls'>
|
||||
<input class='input-xlarge' id='psw' name='psw' size='30' type='password' />
|
||||
</div>
|
||||
</div>
|
||||
<div class='control-group'>
|
||||
<label class='control-label' for='repsw'>确认密码</label>
|
||||
<div class='controls'>
|
||||
<input class='input-xlarge' id='repsw' name='repsw' size='30' type='password' />
|
||||
</div>
|
||||
</div>
|
||||
<input type='hidden' name='_csrf' value='<%= csrf %>' />
|
||||
<input type='hidden' name='name' id='name' value='<%= name%>'>
|
||||
<input type='hidden' name='key' id='key' value='<%= key%>'>
|
||||
<div class='form-actions'>
|
||||
<input type='submit' class='btn' value='确定' />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
x
Reference in New Issue
Block a user