diff --git a/config-example.json b/config-example.json index 84dd2fb..af57e96 100644 --- a/config-example.json +++ b/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" -} +} \ No newline at end of file diff --git a/libs/email.js b/libs/email.js index 02633d7..1603e11 100644 --- a/libs/email.js +++ b/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); -} \ No newline at end of file +module.exports.send = doSendEmail; \ No newline at end of file diff --git a/modules/api.js b/modules/api.js index f05263d..a7af04b 100644 --- a/modules/api.js +++ b/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} 密码重置邮件`, - `

请点击该链接来重置密码:

${vurl}

链接有效期为 12h。如果您不是 ${req.body.username},请忽略此邮件。

` + await Email.send(user.email, + `${user.username} 的 ${syzoj.config.title} 密码重置邮件`, + `

请点击该链接来重置密码:

${vurl}

链接有效期为 12h。如果您不是 ${user.username},请忽略此邮件。

` ); } 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('无效的注册验证链接。'); diff --git a/package-lock.json b/package-lock.json index e95175d..c2ae5af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index e5942a8..142b6c1 100644 --- a/package.json +++ b/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" } } diff --git a/views/forget.ejs b/views/forget.ejs index 58f5e92..95220d4 100644 --- a/views/forget.ejs +++ b/views/forget.ejs @@ -1,4 +1,5 @@ <% this.title = '忘记密码' %> +<% this.noPreserveUrl = true; %> <% include header %> -
+
- +
-
找回密码
+
-
@@ -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(); }); }); diff --git a/views/forget_confirm.ejs b/views/forget_confirm.ejs index dc70252..e4c5c61 100644 --- a/views/forget_confirm.ejs +++ b/views/forget_confirm.ejs @@ -1,6 +1,9 @@ -<% this.title = '重设密码' %> +<% this.title = '重设密码'; %> +<% this.noPreserveUrl = true; %> <% include header %> - +
@@ -9,19 +12,21 @@ 重设密码
-
+
-
-
- - +
+
+ +
-
- - +
+
+
+ +
-
重设
+
@@ -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(); }); }); \ No newline at end of file diff --git a/views/header.ejs b/views/header.ejs index b0d1a87..7713ffd 100644 --- a/views/header.ejs +++ b/views/header.ejs @@ -43,10 +43,10 @@ <% } else { %> diff --git a/views/login.ejs b/views/login.ejs index 972ce8f..2c033b7 100644 --- a/views/login.ejs +++ b/views/login.ejs @@ -1,4 +1,5 @@ <% this.title = '登录' %> +<% this.noPreserveUrl = true; %> <% include header %>
diff --git a/views/sign_up.ejs b/views/sign_up.ejs index 8391e36..aa61491 100644 --- a/views/sign_up.ejs +++ b/views/sign_up.ejs @@ -1,4 +1,5 @@ <% this.title = '注册' %> +<% this.noPreserveUrl = true; %> <% include header %>

注册