diff --git a/README.md b/README.md index 6ae8f51..faec80b 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,47 @@ systemctl stop syzoj-judge systemctl restart syzoj systemctl restart syzoj-judge ``` + +## 邮件配置 +### register_mail +是否启用注册邮件验证。 + +### email\_jwt\_secret +用于 Email Token 签名的 secret,脸滚键盘随意填写即可。 + +### email +#### Sendmail 直接发送(成功率低,不推荐) +```js + "email": { + "method": "sendmail", + "options": { + "address": "xxxx", // 发件人地址 + } + }, +``` + +#### 阿里云邮件推送服务(成功率较高) +```js + "email": { + "method": "aliyundm", + "options": { + "AccessKeyId": "xxxx", + "AccessKeySecret": "xxxx", + "AccountName": "xxxx" // 发件邮箱 + } + }, +``` + +#### SMTP 服务 +```js + "email": { + "method": "smtp", + "options": { + "host": "smtp.163.com", + "port": 465, + "username": "xxx@163.com", + "password": "xxx", + "allowUnauthorizedTls": false + } + }, +``` \ No newline at end of file diff --git a/config-example.json b/config-example.json index af57e96..f76f943 100644 --- a/config-example.json +++ b/config-example.json @@ -13,7 +13,6 @@ "register_mail": true, "email": { "method": "aliyundm", - "key": "test", "options": { "AccessKeyId": "xxxx", "AccessKeySecret": "xxxx", @@ -168,5 +167,6 @@ ], "session_secret": "233", "judge_server_addr": "http://127.0.0.1:5284", - "judge_token": "233" + "judge_token": "233", + "email_jwt_token": "test" } \ No newline at end of file diff --git a/libs/email.js b/libs/email.js index 1603e11..1bb9511 100644 --- a/libs/email.js +++ b/libs/email.js @@ -1,6 +1,7 @@ -const Promise = require('bluebird'); -const sendmail = Promise.promisify(require('sendmail')()); +const Bluebird = require('bluebird'); +const sendmail = Bluebird.promisify(require('sendmail')()); const { DM } = require('waliyun'); +const nodemailer = require('nodemailer'); let doSendEmail; @@ -33,6 +34,29 @@ if (syzoj.config.email.method === "sendmail") { throw new Error("阿里云 API 错误:" + JSON.stringify(result)); } } +} else if (syzoj.config.email.method === "smtp") { + const smtpConfig = { + host: syzoj.config.email.options.host, + port: syzoj.config.email.options.port || 465, + secure: (syzoj.config.email.options.port === 465 || !syzoj.config.email.options.port) ? true : false, + auth: { + user: syzoj.config.email.options.username, + pass: syzoj.config.email.options.password, + }, + tls: { + rejectUnauthorized: !syzoj.config.email.options.allowUnauthorizedTls, + }, + }; + const transporter = Bluebird.promisifyAll(nodemailer.createTransport(smtpConfig)); + + doSendEmail = async function send_smtp(to, subject, body) { + await transporter.sendMailAsync({ + from: `"${syzoj.config.title}" <${syzoj.config.email.options.username}>`, + to: to, + subject: subject, + html: body + }); + }; } else { doSendEmail = async () => { throw new Error("邮件发送配置不正确。"); diff --git a/modules/api.js b/modules/api.js index 8a6c770..9e98420 100644 --- a/modules/api.js +++ b/modules/api.js @@ -58,7 +58,7 @@ app.post('/api/forget', async (req, res) => { userId: user.id, }; - const token = jwt.sign(sendObj, syzoj.config.email.key, { + const token = jwt.sign(sendObj, syzoj.config.email_jwt_secret, { subject: 'forget', expiresIn: '12h' }); @@ -76,7 +76,6 @@ app.post('/api/forget', async (req, res) => { }); } - throw 123; res.send({ error_code: 1 }); } catch (e) { syzoj.log(e); @@ -108,7 +107,7 @@ app.post('/api/sign_up', async (req, res) => { email: req.body.email, }; - const token = jwt.sign(sendObj, syzoj.config.email.key, { + const token = jwt.sign(sendObj, syzoj.config.email_jwt_secret, { subject: 'register', expiresIn: '2d' }); @@ -150,7 +149,7 @@ 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' }); + jwt.verify(req.query.token, syzoj.config.email_jwt_secret, { subject: 'forget' }); } catch (e) { throw new ErrorMessage("Token 不正确。"); } @@ -170,7 +169,7 @@ app.post('/api/reset_password', async (req, res) => { res.setHeader('Content-Type', 'application/json'); let obj; try { - obj = jwt.verify(req.body.token, syzoj.config.email.key, { subject: 'forget' }); + obj = jwt.verify(req.body.token, syzoj.config.email_jwt_secret, { subject: 'forget' }); } catch (e) { throw 3001; } @@ -196,7 +195,7 @@ app.get('/api/sign_up_confirm', async (req, res) => { try { let obj; try { - obj = jwt.verify(req.query.token, syzoj.config.email.key, { subject: 'register' }); + obj = jwt.verify(req.query.token, syzoj.config.email_jwt_secret, { subject: 'register' }); } catch (e) { throw new ErrorMessage('无效的注册验证链接: ' + e.toString()); } @@ -238,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.email.key).toString(); + let decrypted = syzoj.utils.decrypt(Buffer.from(req.params.token, 'base64'), syzoj.config.email_jwt_secret).toString(); obj = JSON.parse(decrypted); } catch (e) { throw new ErrorMessage('无效的注册验证链接。'); diff --git a/package-lock.json b/package-lock.json index c2ae5af..71f98a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1863,6 +1863,11 @@ "win-spawn": "2.0.0" } }, + "nodemailer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.1.0.tgz", + "integrity": "sha512-pZg74CNQgnC0gZTfH0btXCxjKj7/2v5pea6hmMJ/iKyT48Z81TXZua7c65clwqKIlWfMfYBQG3OkrKxycIdXTw==" + }, "nodemailer-fetch": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", diff --git a/package.json b/package.json index 142b6c1..e843190 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "multer": "^1.2.0", "mysql": "^2.11.1", "node-7z": "^0.4.0", + "nodemailer": "^4.1.0", "pygmentize-bundled-cached": "^1.1.0", "randomstring": "^1.1.5", "request": "^2.74.0", @@ -55,9 +56,7 @@ "sqlite3": "^3.1.4", "syzoj-divine": "^1.0.2", "tmp-promise": "^1.0.3", + "waliyun": "^3.1.1", "xss": "^0.3.3" - }, - "optionalDependencies": { - "waliyun": "^3.1.1" } }