Skip to content

Commit 42d8ded

Browse files
committed
Merge pull request cnodejs#20 from dead-horse/master
修复今天的几个问题,修改找回密码逻辑,修改登录跳转逻辑;发送邮件时,不等待邮件发送成功先通知用户,避免用户等待时间过长。邮件压入队列,如果失败1分钟后再次发送
2 parents 473ecbd + da44045 commit 42d8ded

File tree

5 files changed

+216
-53
lines changed

5 files changed

+216
-53
lines changed

controllers/mail.js

+81-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var mailer = require('nodemailer');
22
var config = require('../config').config;
3-
3+
var EventProxy = require('eventproxy').EventProxy;
4+
var util = require('util');
45
mailer.SMTP = {
56
host: config.mail_host,
67
port: config.mail_port,
@@ -9,13 +10,75 @@ mailer.SMTP = {
910
pass: config.mail_pass
1011
};
1112

12-
function send_mail(data,cb){
13-
mailer.send_mail(data,function(err,success){
14-
return cb(err,success);
15-
});
13+
/**
14+
* keep all the mails to send
15+
* @type {Array}
16+
*/
17+
var mails = [];
18+
var timer;
19+
/**
20+
* control mailer
21+
* @type {EventProxy}
22+
*/
23+
var mailEvent = new EventProxy();
24+
/**
25+
* when need to send an email, start to check the mails array and send all of emails.
26+
*/
27+
mailEvent.on("getMail", function() {
28+
if(mails.length === 0) {
29+
return;
30+
} else {
31+
//遍历邮件数组,发送每一封邮件,如果有发送失败的,就再压入数组,同时触发mailEvent事件
32+
var failed = false;
33+
for(var i = 0, len = mails.length; i != len; ++i) {
34+
var message = mails[i];
35+
mails.splice(i, 1);
36+
i--;
37+
len--;
38+
var mail;
39+
try {
40+
message.debug = false;
41+
mail = mailer.send_mail(message, function(error, success) {
42+
if(error) {
43+
mails.push(message);
44+
failed = true;
45+
}
46+
});
47+
} catch(e) {
48+
mails.push(message);
49+
failed = true;
50+
}
51+
if(mail) {
52+
var oldemit = mail.emit;
53+
mail.emit = function() {
54+
oldemit.apply(mail, arguments);
55+
}
56+
}
57+
}
58+
if(failed) {
59+
clearTimeout(timer);
60+
timer = setTimeout(trigger, 60000);
61+
}
62+
}
63+
});
64+
65+
/**
66+
* trigger email event
67+
* @return {[type]}
68+
*/
69+
function trigger() {
70+
mailEvent.trigger("getMail");
71+
}
72+
/**
73+
* send an email
74+
* @param {mail} data [info of an email]
75+
*/
76+
function send_mail (data) {
77+
mails.push(data);
78+
trigger();
1679
}
1780

18-
function send_active_mail(who,token,name,email,cb){
81+
function send_active_mail(who, token, name, email, cb) {
1982
var sender = config.mail_sender;
2083
var to = who;
2184
var subject = config.name + '社区帐号激活';
@@ -31,17 +94,15 @@ function send_active_mail(who,token,name,email,cb){
3194
subject: subject,
3295
html: html
3396
}
34-
35-
send_mail(data,function(err,success){
36-
return cb(err,success);
37-
});
97+
cb (null, true);
98+
send_mail(data);
3899
}
39-
function send_reset_pass_mail(who,token,name,cb){
100+
function send_reset_pass_mail(who, token, name, cb) {
40101
var sender = config.mail_sender;
41102
var to = who;
42103
var subject = config.name + '社区密码重置';
43104
var html = '<p>您好:<p/>' +
44-
'<p>我们收到您在' + config.name + '社区重置密码的请求,请单击下面的链接来重置密码:</p>' +
105+
'<p>我们收到您在' + config.name + '社区重置密码的请求,请在24小时内单击下面的链接来重置密码:</p>' +
45106
'<a href="' + config.host + '/reset_pass?key=' + token + '&name=' + name + '">重置密码链接</a>' +
46107
'<p>若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。</p>' +
47108
'<p>' + config.name +'社区 谨上。</p>'
@@ -53,11 +114,11 @@ function send_reset_pass_mail(who,token,name,cb){
53114
html: html
54115
}
55116

56-
send_mail(data,function(err,success){
57-
return cb(err,success);
58-
});
117+
cb (null, true);
118+
send_mail(data);
59119
}
60-
function send_reply_mail(who,msg){
120+
121+
function send_reply_mail(who, msg) {
61122
var sender = config.mail_sender;
62123
var to = who;
63124
var subject = config.name + ' 新消息';
@@ -76,10 +137,11 @@ function send_reply_mail(who,msg){
76137
html: html
77138
}
78139

79-
send_mail(data,function(err,success){});
140+
send_mail(data);
80141

81142
}
82-
function send_at_mail(who,msg){
143+
144+
function send_at_mail(who, msg) {
83145
var sender = config.mail_sender;
84146
var to = who;
85147
var subject = config.name + ' 新消息';
@@ -98,7 +160,7 @@ function send_at_mail(who,msg){
98160
html: html
99161
}
100162

101-
send_mail(data,function(err,success){});
163+
send_mail(data);
102164
}
103165

104166
exports.send_active_mail = send_active_mail;

controllers/sign.js

+88-33
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,14 @@ exports.showLogin = function(req, res) {
100100
req.session._loginReferer = req.headers.referer;
101101
res.render('sign/signin');
102102
};
103-
103+
/**
104+
* define some page when login just jump to the home page
105+
* @type {Array}
106+
*/
107+
var notJump = [
108+
'/active_account', //active page
109+
'/reset_pass' //reset password page, avoid to reset twice
110+
];
104111
/**
105112
* Handle user login.
106113
*
@@ -131,7 +138,15 @@ exports.login = function(req, res, next) {
131138
}
132139
// store session cookie
133140
gen_session(user, res);
134-
res.redirect(req.session._loginReferer || 'home');
141+
//check at some page just jump to home page
142+
var refer = req.session._loginReferer || 'home';
143+
for (var i=0, len=notJump.length; i!=len; ++i) {
144+
if (refer.indexOf(notJump[i]) >= 0) {
145+
refer = 'home';
146+
break;
147+
}
148+
}
149+
res.redirect(refer);
135150
});
136151
};
137152

@@ -179,35 +194,75 @@ exports.search_pass = function(req,res,next){
179194
return;
180195
}
181196

182-
User.findOne({email:email},function(err,user){
183-
if(!user){
184-
res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email});
185-
return;
186-
}
187-
mail_ctrl.send_reset_pass_mail(email,md5(email+config.session_secret),user.name,function(err,success){
188-
res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请点击里面的链接来重置密码。'});
197+
// User.findOne({email:email},function(err,user){
198+
//动态生成retrive_key和timestamp到users collection,之后重置密码进行验证
199+
var retrieveKey = randomString(15);
200+
var retrieveTime = new Date().getTime();
201+
User.findOne({email : email}, function(err, user) {
202+
if(!user) {
203+
res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email});
204+
return;
205+
}
206+
user.retrieve_key = retrieveKey;
207+
user.retrieve_time = retrieveTime;
208+
user.save(function(err) {
209+
if(err) {
210+
return next(err);
211+
}
212+
mail_ctrl.send_reset_pass_mail(email, retrieveKey, user.name, function(err,success) {
213+
res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请在24小时内点击里面的链接来重置密码。'});
214+
});
189215
});
190216
});
191217
}
192218
}
193-
194-
exports.reset_pass = function(req,res,next){
195-
var key = req.query.key;
196-
var name = req.query.name;
197-
var new_pass = '';
198-
199-
User.findOne({name:name},function(err,user){
200-
if(!user || md5(user.email+config.session_secret) != key){
201-
res.render('notify/notify',{error: '信息有误,密码无法重置。'});
202-
return;
203-
}
204-
new_pass = random_password();
205-
user.pass = md5(new_pass);
206-
user.save(function(err){
207-
res.render('notify/notify',{success: '你的密码已被重置为:' + new_pass + ',请立即用此密码登录后在设置页面更改密码。'});
208-
});
209-
});
210-
219+
/**
220+
* reset password
221+
* 'get' to show the page, 'post' to reset password
222+
* after reset password, retrieve_key&time will be destroy
223+
* @param {http.req} req
224+
* @param {http.res} res
225+
* @param {Function} next
226+
*/
227+
exports.reset_pass = function(req,res,next) {
228+
var method = req.method.toLowerCase();
229+
if(method === 'get') {
230+
var key = req.query.key;
231+
var name = req.query.name;
232+
User.findOne({name:name, retrieve_key:key},function(err,user) {
233+
if(!user) {
234+
return res.render('notify/notify',{error: '信息有误,密码无法重置。'});
235+
}
236+
var now = new Date().getTime();
237+
var oneDay = 1000 * 60 * 60 * 24;
238+
if(!user.retrieve_time || now - user.retrieve_time > oneDay) {
239+
return res.render('notify/notify', {error : '该链接已过期,请重新申请。'});
240+
}
241+
return res.render('sign/reset', {name : name, key : key});
242+
});
243+
} else {
244+
var psw = req.body.psw || '';
245+
var repsw = req.body.repsw || '';
246+
var key = req.body.key || '';
247+
var name = req.body.name || '';
248+
if(psw !== repsw) {
249+
return res.render('sign/reset', {name : name, key : key, error : '两次密码输入不一致。'});
250+
}
251+
User.findOne({name:name, retrieve_key: key}, function(err, user) {
252+
if(!user) {
253+
return res.render('notify/notify', {error : '错误的激活链接'});
254+
}
255+
user.pass = md5(psw);
256+
user.retrieve_key = null;
257+
user.retrieve_time = null;
258+
user.save(function(err) {
259+
if(err) {
260+
return next(err);
261+
}
262+
return res.render('notify/notify', {success: '你的密码已重置。'});
263+
})
264+
})
265+
}
211266
}
212267

213268
// auth_user middleware
@@ -250,30 +305,30 @@ exports.auth_user = function(req,res,next){
250305
};
251306

252307
// private
253-
function gen_session(user,res){
308+
function gen_session(user,res) {
254309
var auth_token = encrypt(user._id + '\t'+user.name + '\t' + user.pass +'\t' + user.email, config.session_secret);
255310
res.cookie(config.auth_cookie_name, auth_token, {path: '/',maxAge: 1000*60*60*24*7}); //cookie 有效期1周
256311
}
257-
function encrypt(str,secret){
312+
function encrypt(str,secret) {
258313
var cipher = crypto.createCipher('aes192', secret);
259314
var enc = cipher.update(str,'utf8','hex');
260315
enc += cipher.final('hex');
261316
return enc;
262317
}
263-
function decrypt(str,secret){
318+
function decrypt(str,secret) {
264319
var decipher = crypto.createDecipher('aes192', secret);
265320
var dec = decipher.update(str,'hex','utf8');
266321
dec += decipher.final('utf8');
267322
return dec;
268323
}
269-
function md5(str){
324+
function md5(str) {
270325
var md5sum = crypto.createHash('md5');
271326
md5sum.update(str);
272327
str = md5sum.digest('hex');
273328
return str;
274329
}
275-
function random_password(passwd_size){
276-
var size = passwd_size || 6;
330+
function randomString(size) {
331+
size = size || 6;
277332
var code_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
278333
var max_num = code_string.length + 1;
279334
var new_pass = '';

models/user.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ var UserSchema = new Schema({
2828

2929
receive_reply_mail: {type: Boolean, default: false },
3030
receive_at_mail: { type: Boolean, default: false },
31-
from_wp: { type: Boolean }
31+
from_wp: { type: Boolean },
32+
33+
retrieve_time : {type: Number},
34+
retrieve_key : {type: String}
3235
});
3336

3437
mongoose.model('User', UserSchema);

routes.js

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ exports = module.exports = function(app) {
3535
app.get('/search_pass', sign.search_pass);
3636
app.post('/search_pass', sign.search_pass);
3737
app.get('/reset_pass', sign.reset_pass);
38+
app.post('/reset_pass', sign.reset_pass);
3839

3940
// user
4041
app.get('/user/:name', user.index);

views/sign/reset.html

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<%- partial('sign/sidebar') %>
2+
3+
<div id='content'>
4+
<div class='panel'>
5+
<div class='header'>
6+
<ul class='breadcrumb'>
7+
<li><a href='/'>主页</a><span class='divider'>/</span></li>
8+
<li class='active'>重置密码</li>
9+
</ul>
10+
</div>
11+
<div class='inner'>
12+
<div class='sep10'></div>
13+
<div class='sep10'></div>
14+
<% if(locals.error){ %>
15+
<div class="alert alert-error">
16+
<a class="close" data-dismiss="alert" href="#">&times;</a>
17+
<strong><%= error %></strong>
18+
</div>
19+
<% } %>
20+
<form id='signin_form' class='form-horizontal' action='/reset_pass' method='post'>
21+
<div class='control-group'>
22+
<label class='control-label' for='psw'>新密码</label>
23+
<div class='controls'>
24+
<input class='input-xlarge' id='psw' name='psw' size='30' type='password' />
25+
</div>
26+
</div>
27+
<div class='control-group'>
28+
<label class='control-label' for='repsw'>确认密码</label>
29+
<div class='controls'>
30+
<input class='input-xlarge' id='repsw' name='repsw' size='30' type='password' />
31+
</div>
32+
</div>
33+
<input type='hidden' name='_csrf' value='<%= csrf %>' />
34+
<input type='hidden' name='name' id='name' value='<%= name%>'>
35+
<input type='hidden' name='key' id='key' value='<%= key%>'>
36+
<div class='form-actions'>
37+
<input type='submit' class='btn' value='确定' />
38+
</div>
39+
</form>
40+
</div>
41+
</div>
42+
</div>

0 commit comments

Comments
 (0)