Browse Source

Finish email.

pull/6/head
t123yh 7 years ago
parent
commit
cd3817a3a5
  1. 15
      config-example.json
  2. 47
      libs/email.js
  3. 41
      modules/api.js
  4. 9
      package-lock.json
  5. 3
      package.json
  6. 15
      views/forget.ejs
  7. 38
      views/forget_confirm.ejs
  8. 4
      views/header.ejs
  9. 1
      views/login.ejs
  10. 1
      views/sign_up.ejs

15
config-example.json

@ -10,10 +10,15 @@
"dialect": "sqlite",
"storage": "syzoj.db"
},
"register_mail": {
"enabled": true,
"address": "test@test.domain",
"key": "test"
"register_mail": true,
"email": {
"method": "aliyundm",
"key": "test",
"options": {
"AccessKeyId": "xxxx",
"AccessKeySecret": "xxxx",
"AccountName": "xxxx"
}
},
"upload_dir": "uploads",
"default": {
@ -164,4 +169,4 @@
"session_secret": "233",
"judge_server_addr": "http://127.0.0.1:5284",
"judge_token": "233"
}
}

47
libs/email.js

@ -1,16 +1,41 @@
const Promise = require('bluebird');
const sendmail = Promise.promisify(require('sendmail')());
const { DM } = require('waliyun');
async function send_sendmail(to, subject, body) {
await sendmail({
from: `${syzoj.config.title} <${syzoj.config.register_mail.address}>`,
to: to,
type: 'text/html',
subject: subject,
html: body
let doSendEmail;
if (syzoj.config.email.method === "sendmail") {
doSendEmail = async function send_sendmail(to, subject, body) {
await sendmail({
from: `${syzoj.config.title} <${syzoj.config.email.options.address}>`,
to: to,
type: 'text/html',
subject: subject,
html: body
});
}
} else if (syzoj.config.email.method === "aliyundm") {
const dm = DM({
AccessKeyId: syzoj.config.email.options.AccessKeyId,
AccessKeySecret: syzoj.config.email.options.AccessKeySecret
});
doSendEmail = async function send_aliyundm(to, subject, body) {
const result = await dm.singleSendMail({
AccountName: syzoj.config.email.options.AccountName,
AddressType: 1,
ReplyToAddress: false,
ToAddress: to,
FromAlias: syzoj.config.title,
Subject: subject,
HtmlBody: body
});
if (result.Code != null) {
throw new Error("阿里云 API 错误:" + JSON.stringify(result));
}
}
} else {
doSendEmail = async () => {
throw new Error("邮件发送配置不正确。");
}
}
module.exports.send = async function sendEmail(to, subject, body) {
await send_sendmail(to, subject, body);
}
module.exports.send = doSendEmail;

41
modules/api.js

@ -57,16 +57,16 @@ app.post('/api/forget', async (req, res) => {
userId: user.id,
};
const token = jwt.sign(sendObj, syzoj.config.register_mail.key, {
const token = jwt.sign(sendObj, syzoj.config.email.key, {
subject: 'forget',
expiresIn: '12h'
});
const vurl = req.protocol + '://' + req.get('host') + syzoj.utils.makeUrl(['api', 'forget_confirm'], { token: token });
try {
await Email.send(req.body.email,
`${req.body.username}${syzoj.config.title} 密码重置邮件`,
`<p>请点击该链接来重置密码:</p><p><a href="${vurl}">${vurl}</a></p><p>链接有效期为 12h。如果您不是 ${req.body.username},请忽略此邮件。</p>`
await Email.send(user.email,
`${user.username}${syzoj.config.title} 密码重置邮件`,
`<p>请点击该链接来重置密码:</p><p><a href="${vurl}">${vurl}</a></p><p>链接有效期为 12h。如果您不是 ${user.username},请忽略此邮件。</p>`
);
} catch (e) {
return res.send({
@ -75,7 +75,8 @@ app.post('/api/forget', async (req, res) => {
});
}
if (!user) res.send({ error_code: 1 });
throw 123;
res.send({ error_code: 1 });
} catch (e) {
syzoj.log(e);
res.send(JSON.stringify({ error_code: e }));
@ -99,14 +100,14 @@ app.post('/api/sign_up', async (req, res) => {
if (!(req.body.email = req.body.email.trim())) throw 2006;
if (!syzoj.utils.isValidUsername(req.body.username)) throw 2002;
if (syzoj.config.register_mail.enabled) {
if (syzoj.config.register_mail) {
let sendObj = {
username: req.body.username,
password: req.body.password,
email: req.body.email,
};
const token = jwt.sign(sendObj, syzoj.config.register_mail.key, {
const token = jwt.sign(sendObj, syzoj.config.email.key, {
subject: 'register',
expiresIn: '2d'
});
@ -147,6 +148,11 @@ app.post('/api/sign_up', async (req, res) => {
app.get('/api/forget_confirm', async (req, res) => {
try {
try {
jwt.verify(req.query.token, syzoj.config.email.key, { subject: 'forget' });
} catch (e) {
throw new ErrorMessage("Token 不正确。");
}
res.render('forget_confirm', {
token: req.query.token
});
@ -160,23 +166,28 @@ app.get('/api/forget_confirm', async (req, res) => {
app.post('/api/reset_password', async (req, res) => {
try {
res.setHeader('Content-Type', 'application/json');
let obj;
try {
obj = jwt.verify(req.query.token, syzoj.config.register_mail.key, { subject: 'forget' });
obj = jwt.verify(req.body.token, syzoj.config.email.key, { subject: 'forget' });
} catch (e) {
throw 3001;
}
let syzoj2_xxx_md5 = '59cb65ba6f9ad18de0dcd12d5ae11bd2';
if (req.query.password === syzoj2_xxx_md5) throw new ErrorMessage('密码不能为空。');
const user = await User.fromId(obj.id);
user.password = req.query.password;
if (req.body.password === syzoj2_xxx_md5) throw new ErrorMessage('密码不能为空。');
const user = await User.fromID(obj.userId);
user.password = req.body.password;
await user.save();
res.send(JSON.stringify({ error_code: 1 }));
} catch (e) {
syzoj.log(e);
res.send(JSON.stringify({ error_code: e }));
if (typeof e === 'number') {
res.send(JSON.stringify({ error_code: e }));
} else {
res.send(JSON.stringify({ error_code: 1000 }));
}
}
});
@ -184,9 +195,9 @@ app.get('/api/sign_up_confirm', async (req, res) => {
try {
let obj;
try {
obj = jwt.verify(req.query.token, syzoj.config.register_mail.key, { subject: 'register' });
obj = jwt.verify(req.query.token, syzoj.config.email.key, { subject: 'register' });
} catch (e) {
throw new ErrorMessage('无效的注册验证链接。');
throw new ErrorMessage('无效的注册验证链接: ' + e.toString());
}
let user = await User.fromName(obj.username);
@ -226,7 +237,7 @@ app.get('/api/sign_up/:token', async (req, res) => {
try {
let obj;
try {
let decrypted = syzoj.utils.decrypt(Buffer.from(req.params.token, 'base64'), syzoj.config.register_mail.key).toString();
let decrypted = syzoj.utils.decrypt(Buffer.from(req.params.token, 'base64'), syzoj.config.email.key).toString();
obj = JSON.parse(decrypted);
} catch (e) {
throw new ErrorMessage('无效的注册验证链接。');

9
package-lock.json generated

@ -3925,6 +3925,15 @@
"extsprintf": "1.0.2"
}
},
"waliyun": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/waliyun/-/waliyun-3.1.1.tgz",
"integrity": "sha1-CCl+SSNBytTbRB8ruhUBVtvob1Q=",
"requires": {
"debug": "2.6.7",
"request": "2.81.0"
}
},
"webidl-conversions": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.1.tgz",

3
package.json

@ -56,5 +56,8 @@
"syzoj-divine": "^1.0.2",
"tmp-promise": "^1.0.3",
"xss": "^0.3.3"
},
"optionalDependencies": {
"waliyun": "^3.1.1"
}
}

15
views/forget.ejs

@ -1,4 +1,5 @@
<% this.title = '忘记密码' %>
<% this.noPreserveUrl = true; %>
<% include header %>
<div class="ui message" id="msgBox" hidden>
<p id="msgContent"></p>
@ -11,17 +12,16 @@
忘记密码
</div>
</h2>
<form class="ui large form">
<form class="ui large form" id="forgetForm">
<div class="ui existing segment">
<div class="field">
<div class="ui left icon input">
<i class="at icon"></i>
<input name="email" placeholder="电子邮件地址" type="text" id="email" onkeydown="key_login(event)">
<input name="email" placeholder="电子邮件地址" type="text" id="email">
</div>
</div>
<div class="ui fluid large submit button" id="sendEmail">找回密码</div>
<button type="submit" class="ui fluid large submit button" id="sendEmail">找回密码</div>
</div>
<div class="ui error message"></div>
</form>
</div>
</div>
@ -48,14 +48,14 @@ function submitForm() {
case 1:
showMessage("positive", "找回密码邮件已经发至你电子邮箱的垃圾邮件文件夹。");
return;
case 1002:
case 1001:
showMessage("error", "用户不存在");
break;
default:
showMessage("error", "未知错误" + error_code);
break;
}
$("#resetPassword").removeClass("loading");
$("#sendEmail").removeClass("loading");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
@ -63,8 +63,9 @@ function submitForm() {
});
}
$(document).ready(function() {
$("#sendEmail").click(function() {
$("#forgetForm").submit(function(event) {
submitForm();
event.preventDefault();
});
});
</script>

38
views/forget_confirm.ejs

@ -1,6 +1,9 @@
<% this.title = '重设密码' %>
<% this.title = '重设密码'; %>
<% this.noPreserveUrl = true; %>
<% include header %>
<div class="ui error message" id="error" hidden></div>
<div class="ui message" id="msgBox" hidden>
<p id="msgContent"></p>
</div>
<div class="ui middle aligned center aligned grid">
<div class="row">
<div class="column" style="max-width: 450px">
@ -9,19 +12,21 @@
重设密码
</div>
</h2>
<form class="ui large form">
<form class="ui large form" id="resetForm">
<div class="ui existing segment">
<div class="two fields">
<div class="field">
<label class="ui header">密码</label>
<input type="password" placeholder="" id="password1">
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" placeholder="密码" id="password1">
</div>
<div class="field">
<label class="ui header">确认密码</label>
<input type="password" placeholder="" id="password2">
</div>
<div class="field">
<div class="ui left icon input">
<i class="undo icon"></i>
<input type="password" placeholder="确认密码" id="password2">
</div>
</div>
<div class="ui fluid large submit button" id="reset">重设</div>
<button type="submit" class="ui fluid large submit button" id="resetPassword">重设</div>
</div>
</form>
</div>
@ -46,17 +51,19 @@ function submitForm() {
type: 'POST',
data: {
"token": <%- JSON.stringify(token) %>,
"password": password
"password": password,
"_csrf": document.head.getAttribute('data-csrf-token')
},
async: true,
success: function(data) {
error_code = data.error_code;
alert('fuck' + error_code);
switch (error_code) {
case 1:
showMessage("positive", "密码重置成功。");
return;
break;
default:
showMessage("error", "未知错误" + error_code);
showMessage("error", "未知错误 " + error_code);
break;
}
$("#resetPassword").removeClass("loading");
@ -67,8 +74,9 @@ function submitForm() {
});
}
$(document).ready(function() {
$("#reset").click(function() {
$("#resetForm").submit(function(event) {
submitForm();
event.preventDefault();
});
});
</script>

4
views/header.ejs

@ -43,10 +43,10 @@
</a>
<% } else { %>
<div class="item">
<a class="ui button" style="margin-right: 0.5em; " href="<%= syzoj.utils.makeUrl(['login'], { url: req.originalUrl }) %>">
<a class="ui button" style="margin-right: 0.5em; " href="<%= syzoj.utils.makeUrl(['login'], { url: this.noPreserveUrl ? undefined : req.originalUrl }) %>">
登录
</a>
<a class="ui primary button" href="<%= syzoj.utils.makeUrl(['sign_up'], { url: req.originalUrl }) %>">
<a class="ui primary button" href="<%= syzoj.utils.makeUrl(['sign_up'], { url: this.noPreserveUrl ? undefined : req.originalUrl }) %>">
注册
</a>
</div>

1
views/login.ejs

@ -1,4 +1,5 @@
<% this.title = '登录' %>
<% this.noPreserveUrl = true; %>
<% include header %>
<div class="ui error message" id="error" hidden></div>
<div class="ui middle aligned center aligned grid">

1
views/sign_up.ejs

@ -1,4 +1,5 @@
<% this.title = '注册' %>
<% this.noPreserveUrl = true; %>
<% include header %>
<div class="padding">
<h1>注册</h1>

Loading…
Cancel
Save