@waline/vercel 1.18.7 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/__tests__/katex.spec.js +2 -0
  2. package/index.js +2 -0
  3. package/package.json +10 -10
  4. package/src/config/adapter.js +16 -0
  5. package/src/config/config.js +2 -1
  6. package/src/config/extend.js +6 -4
  7. package/src/controller/article.js +1 -0
  8. package/src/controller/comment.js +29 -0
  9. package/src/controller/db.js +54 -2
  10. package/src/controller/oauth.js +14 -8
  11. package/src/controller/rest.js +1 -0
  12. package/src/controller/token/2fa.js +4 -0
  13. package/src/controller/token.js +4 -0
  14. package/src/controller/user/password.js +3 -1
  15. package/src/controller/user.js +4 -1
  16. package/src/controller/verification.js +3 -0
  17. package/src/extend/controller.js +4 -0
  18. package/src/extend/think.js +8 -1
  19. package/src/locales/en.json +2 -2
  20. package/src/locales/zh-CN.json +2 -2
  21. package/src/locales/zh-TW.json +2 -2
  22. package/src/logic/base.js +15 -0
  23. package/src/logic/comment.js +5 -0
  24. package/src/logic/db.js +1 -0
  25. package/src/logic/token/2fa.js +1 -0
  26. package/src/logic/user.js +2 -0
  27. package/src/service/akismet.js +1 -0
  28. package/src/service/avatar.js +5 -0
  29. package/src/service/markdown/katex.js +2 -0
  30. package/src/service/markdown/mathCommon.js +5 -0
  31. package/src/service/markdown/mathjax.js +3 -0
  32. package/src/service/notify.js +109 -80
  33. package/src/service/storage/cloudbase.js +21 -0
  34. package/src/service/storage/deta.js +28 -0
  35. package/src/service/storage/github.js +77 -49
  36. package/src/service/storage/leancloud.js +42 -1
  37. package/src/service/storage/mongodb.js +17 -0
  38. package/src/service/storage/mysql.js +11 -0
  39. package/src/service/storage/postgresql.js +23 -2
  40. package/vanilla.js +1 -0
@@ -23,6 +23,7 @@ module.exports = class extends BaseRest {
23
23
  offset: Math.max((page - 1) * pageSize, 0),
24
24
  }
25
25
  );
26
+
26
27
  return this.success({
27
28
  page,
28
29
  totalPages: Math.ceil(count / pageSize),
@@ -89,7 +90,7 @@ module.exports = class extends BaseRest {
89
90
  ? `"${SENDER_NAME}" <${SENDER_EMAIL}>`
90
91
  : SMTP_USER,
91
92
  to: data.email,
92
- subject: this.locale('[{{name}}] Registration Confirm Mail', {
93
+ subject: this.locale('[{{name | safe}}] Registration Confirm Mail', {
93
94
  name: SITE_NAME || 'Waline',
94
95
  }),
95
96
  html: this.locale(
@@ -147,8 +148,10 @@ module.exports = class extends BaseRest {
147
148
  }
148
149
 
149
150
  const socials = ['github', 'twitter', 'facebook', 'google', 'weibo', 'qq'];
151
+
150
152
  socials.forEach((social) => {
151
153
  const nextSocial = this.post(social);
154
+
152
155
  if (think.isString(nextSocial)) {
153
156
  updateData[social] = nextSocial;
154
157
  }
@@ -12,18 +12,21 @@ module.exports = class extends BaseRest {
12
12
  async getAction() {
13
13
  const { token, email } = this.get();
14
14
  const users = await this.modelInstance.select({ email });
15
+
15
16
  if (think.isEmpty(users)) {
16
17
  return this.fail(this.locale('USER_NOT_EXIST'));
17
18
  }
18
19
 
19
20
  const user = users[0];
20
21
  const match = user.type.match(/^verify:(\d{4}):(\d+)$/i);
22
+
21
23
  if (!match) {
22
24
  return this.fail(this.locale('USER_REGISTED'));
23
25
  }
24
26
 
25
27
  if (token === match[1] && Date.now() < parseInt(match[2])) {
26
28
  await this.modelInstance.update({ type: 'guest' }, { email });
29
+
27
30
  return this.redirect('/ui/login');
28
31
  }
29
32
 
@@ -4,18 +4,22 @@ const locales = require('../locales');
4
4
  module.exports = {
5
5
  success(...args) {
6
6
  this.ctx.success(...args);
7
+
7
8
  return think.prevent();
8
9
  },
9
10
  fail(...args) {
10
11
  this.ctx.fail(...args);
12
+
11
13
  return think.prevent();
12
14
  },
13
15
  locale(message, variables) {
14
16
  const { lang } = this.get();
15
17
  const locale = locales[(lang || '').toLowerCase()];
18
+
16
19
  if (locale && locale[message]) {
17
20
  message = locale[message];
18
21
  }
22
+
19
23
  return nunjucks.renderString(message, variables);
20
24
  },
21
25
  };
@@ -14,9 +14,11 @@ module.exports = {
14
14
  findLastIndex(arr, fn) {
15
15
  for (let i = arr.length - 1; i >= 0; i--) {
16
16
  const ret = fn(arr[i], i, arr);
17
+
17
18
  if (!ret) {
18
19
  continue;
19
20
  }
21
+
20
22
  return i;
21
23
  }
22
24
 
@@ -34,6 +36,7 @@ module.exports = {
34
36
 
35
37
  function runTask() {
36
38
  const idx = index;
39
+
37
40
  index += 1;
38
41
  if (index > promises.length) {
39
42
  return Promise.resolve();
@@ -45,6 +48,7 @@ module.exports = {
45
48
  if (count === promises.length) {
46
49
  resolve(ret);
47
50
  }
51
+
48
52
  return runTask();
49
53
  }, reject);
50
54
  }
@@ -55,20 +59,23 @@ module.exports = {
55
59
  });
56
60
  },
57
61
  async ip2region(ip, { depth = 1 }) {
58
- if (!ip) return '';
62
+ if (!ip || ip.includes(':')) return '';
59
63
 
60
64
  try {
61
65
  const search = helper.promisify(regionSearch.btreeSearch, regionSearch);
62
66
  const result = await search(ip);
67
+
63
68
  if (!result) {
64
69
  return '';
65
70
  }
66
71
  const { region } = result;
67
72
  const [, , province, city, isp] = region.split('|');
68
73
  const address = Array.from(new Set([province, city, isp]));
74
+
69
75
  return address.slice(0, depth).join(' ');
70
76
  } catch (e) {
71
77
  console.log(e);
78
+
72
79
  return '';
73
80
  }
74
81
  },
@@ -5,9 +5,9 @@
5
5
  "USER_REGISTED": "USER_REGISTED",
6
6
  "TOKEN_EXPIRED": "TOKEN_EXPIRED",
7
7
  "TWO_FACTOR_AUTH_ERROR_DETAIL": "TWO_FACTOR_AUTH_ERROR_DETAIL",
8
- "[{{name}}] Registration Confirm Mail": "[{{name}}] Registration Confirm Mail",
8
+ "[{{name | safe}}] Registration Confirm Mail": "[{{name | safe}}] Registration Confirm Mail",
9
9
  "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.",
10
- "[{{name}}] Reset Password": "[{{name}}] Reset Password",
10
+ "[{{name | safe}}] Reset Password": "[{{name | safe}}] Reset Password",
11
11
  "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!",
12
12
  "Duplicate Content": "Duplicate Content",
13
13
  "Comment too fast": "Comment too fast",
@@ -5,9 +5,9 @@
5
5
  "USER_REGISTED": "用户已注册",
6
6
  "TOKEN_EXPIRED": "密钥已过期",
7
7
  "TWO_FACTOR_AUTH_ERROR_DETAIL": "二步验证失败",
8
- "[{{name}}] Registration Confirm Mail": "【{{name}}】注册确认邮件",
8
+ "[{{name | safe}}] Registration Confirm Mail": "【{{name | safe}}】注册确认邮件",
9
9
  "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "请点击 <a href=\"{{url}}\">{{url}}</a> 确认注册,链接有效时间为 1 个小时。如果不是你在注册,请忽略这封邮件。",
10
- "[{{name}}] Reset Password": "【{{name}}】重置密码",
10
+ "[{{name | safe}}] Reset Password": "【{{name | safe}}】重置密码",
11
11
  "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "请尽快点击链接 <a href=\"{{url}}\">{{url}}</a> 登录并修改你的密码!",
12
12
  "Duplicate Content": "发送的内容之前已经发过",
13
13
  "Comment too fast": "评论太快啦,请慢点!",
@@ -5,9 +5,9 @@
5
5
  "USER_REGISTED": "用戶已註冊",
6
6
  "TOKEN_EXPIRED": "密鑰已過期",
7
7
  "TWO_FACTOR_AUTH_ERROR_DETAIL": "二步驗證失敗",
8
- "[{{name}}] Registration Confirm Mail": "『{{name}}』註冊確認郵件",
8
+ "[{{name | safe}}] Registration Confirm Mail": "『{{name | safe}}』註冊確認郵件",
9
9
  "Please click <a href=\"{{url}}\">{{url}}<a/> to confirm registration, the link is valid for 1 hour. If you are not registering, please ignore this email.": "請點擊 <a href=\"{{url}}\">{{url}}</a> 確認註冊,鏈接有效時間為 1 個小時。如果不是你在註冊,請忽略這封郵件。",
10
- "[{{name}}] Reset Password": "『{{name}}』重置密碼",
10
+ "[{{name | safe}}] Reset Password": "『{{name | safe}}』重置密碼",
11
11
  "Please click <a href=\"{{url}}\">{{url}}</a> to login and change your password as soon as possible!": "請盡快點擊鏈接 <a href=\"{{url}}\">{{url}}</a> 登錄並修改你的密碼!",
12
12
  "Duplicate Content": "發送的內容之前已經發過",
13
13
  "Comment too fast": "評論太快啦,請慢點!",
package/src/logic/base.js CHANGED
@@ -1,3 +1,4 @@
1
+ const path = require('path');
1
2
  const jwt = require('jsonwebtoken');
2
3
  const helper = require('think-helper');
3
4
 
@@ -8,12 +9,14 @@ module.exports = class extends think.Logic {
8
9
  `storage/${this.config('storage')}`,
9
10
  'Users'
10
11
  );
12
+ this.resource = this.getResource();
11
13
  this.id = this.getId();
12
14
  }
13
15
 
14
16
  async __before() {
15
17
  const referrer = this.ctx.referrer(true);
16
18
  let { secureDomains } = this.config();
19
+
17
20
  if (secureDomains && referrer && this.ctx.host.indexOf(referrer) !== 0) {
18
21
  secureDomains = think.isArray(secureDomains)
19
22
  ? secureDomains
@@ -31,6 +34,7 @@ module.exports = class extends think.Logic {
31
34
  ? domain.test(referrer)
32
35
  : domain === referrer
33
36
  );
37
+
34
38
  if (!match) {
35
39
  return this.ctx.throw(403);
36
40
  }
@@ -39,11 +43,13 @@ module.exports = class extends think.Logic {
39
43
  this.ctx.state.userInfo = {};
40
44
  const { authorization } = this.ctx.req.headers;
41
45
  const { state } = this.get();
46
+
42
47
  if (!authorization && !state) {
43
48
  return;
44
49
  }
45
50
  const token = state || authorization.replace(/^Bearer /, '');
46
51
  const userMail = jwt.verify(token, think.config('jwtKey'));
52
+
47
53
  if (think.isEmpty(userMail) || !think.isString(userMail)) {
48
54
  return;
49
55
  }
@@ -69,6 +75,7 @@ module.exports = class extends think.Logic {
69
75
  ],
70
76
  }
71
77
  );
78
+
72
79
  if (think.isEmpty(user)) {
73
80
  return;
74
81
  }
@@ -83,6 +90,7 @@ module.exports = class extends think.Logic {
83
90
  link: userInfo.url,
84
91
  });
85
92
  const { avatarProxy } = think.config();
93
+
86
94
  if (avatarProxy) {
87
95
  avatarUrl = avatarProxy + '?url=' + encodeURIComponent(avatarUrl);
88
96
  }
@@ -92,6 +100,13 @@ module.exports = class extends think.Logic {
92
100
  this.ctx.state.token = token;
93
101
  }
94
102
 
103
+ getResource() {
104
+ const filename = this.__filename || __filename;
105
+ const last = filename.lastIndexOf(path.sep);
106
+
107
+ return filename.substr(last + 1, filename.length - last - 4);
108
+ }
109
+
95
110
  getId() {
96
111
  const id = this.get('id');
97
112
 
@@ -1,4 +1,5 @@
1
1
  const Base = require('./base');
2
+
2
3
  module.exports = class extends Base {
3
4
  async __before() {
4
5
  await super.__before();
@@ -7,6 +8,7 @@ module.exports = class extends Base {
7
8
  const { like } = this.post();
8
9
  const isAllowedGet = this.isGet && (type !== 'list' || path);
9
10
  const isAllowedPut = this.ctx.isMethod('PUT') && think.isBoolean(like);
11
+
10
12
  if (this.isPost || isAllowedGet || isAllowedPut) {
11
13
  return;
12
14
  }
@@ -114,6 +116,7 @@ module.exports = class extends Base {
114
116
  */
115
117
  getAction() {
116
118
  const { type } = this.get();
119
+
117
120
  switch (type) {
118
121
  case 'recent':
119
122
  this.rules = {
@@ -135,6 +138,7 @@ module.exports = class extends Base {
135
138
 
136
139
  case 'list': {
137
140
  const { userInfo } = this.ctx.state;
141
+
138
142
  if (userInfo.type !== 'administrator') {
139
143
  return this.fail();
140
144
  }
@@ -224,6 +228,7 @@ module.exports = class extends Base {
224
228
  */
225
229
  putAction() {
226
230
  const { userInfo } = this.ctx.state;
231
+
227
232
  if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
228
233
  this.rules = {
229
234
  like: {
package/src/logic/db.js CHANGED
@@ -5,6 +5,7 @@ module.exports = class extends Base {
5
5
  await super.__before(...args);
6
6
 
7
7
  const { userInfo } = this.ctx.state;
8
+
8
9
  if (think.isEmpty(userInfo)) {
9
10
  return this.fail(401);
10
11
  }
@@ -12,6 +12,7 @@ module.exports = class extends Base {
12
12
 
13
13
  async postAction() {
14
14
  const { userInfo } = this.ctx.state;
15
+
15
16
  if (think.isEmpty(userInfo)) {
16
17
  return this.fail(401);
17
18
  }
package/src/logic/user.js CHANGED
@@ -3,6 +3,7 @@ const Base = require('./base');
3
3
  module.exports = class extends Base {
4
4
  getAction() {
5
5
  const { userInfo } = this.ctx.state;
6
+
6
7
  if (think.isEmpty(userInfo) || userInfo.type !== 'administrator') {
7
8
  return this.fail();
8
9
  }
@@ -50,6 +51,7 @@ module.exports = class extends Base {
50
51
  putAction() {
51
52
  // you need login to update yourself profile
52
53
  const { userInfo } = this.ctx.state;
54
+
53
55
  if (think.isEmpty(userInfo)) {
54
56
  return this.fail();
55
57
  }
@@ -3,6 +3,7 @@ const DEFAULT_KEY = '70542d86693e';
3
3
 
4
4
  module.exports = function (comment, blog) {
5
5
  let { AKISMET_KEY, SITE_URL } = process.env;
6
+
6
7
  if (!AKISMET_KEY) {
7
8
  AKISMET_KEY = DEFAULT_KEY;
8
9
  }
@@ -3,6 +3,7 @@ const helper = require('think-helper');
3
3
  const { GRAVATAR_STR } = process.env;
4
4
 
5
5
  const env = new nunjucks.Environment();
6
+
6
7
  env.addFilter('md5', (str) => helper.md5(str));
7
8
 
8
9
  const DEFAULT_GRAVATAR_STR = `{%- set numExp = r/^[0-9]+$/g -%}
@@ -14,17 +15,21 @@ const DEFAULT_GRAVATAR_STR = `{%- set numExp = r/^[0-9]+$/g -%}
14
15
  {%- else -%}
15
16
  https://seccdn.libravatar.org/avatar/{{mail|md5}}
16
17
  {%- endif -%}`;
18
+
17
19
  module.exports = class extends think.Service {
18
20
  async stringify(comment) {
19
21
  const fn = think.config('avatarUrl');
22
+
20
23
  if (think.isFunction(fn)) {
21
24
  const ret = await fn(comment);
25
+
22
26
  if (think.isString(ret) && ret) {
23
27
  return ret;
24
28
  }
25
29
  }
26
30
 
27
31
  const gravatarStr = GRAVATAR_STR || DEFAULT_GRAVATAR_STR;
32
+
28
33
  return env.renderString(gravatarStr, comment);
29
34
  }
30
35
  };
@@ -9,6 +9,7 @@ const katexInline = (tex, options) => {
9
9
  return katex.renderToString(tex, options);
10
10
  } catch (error) {
11
11
  if (options.throwOnError) console.warn(error);
12
+
12
13
  return `<span class='katex-error' title='${escapeHtml(
13
14
  error.toString()
14
15
  )}'>${escapeHtml(tex)}</span>`;
@@ -21,6 +22,7 @@ const katexBlock = (tex, options) => {
21
22
  return `<p class='katex-block'>${katex.renderToString(tex, options)}</p>`;
22
23
  } catch (error) {
23
24
  if (options.throwOnError) console.warn(error);
25
+
24
26
  return `<p class='katex-block katex-error' title='${escapeHtml(
25
27
  error.toString()
26
28
  )}'>${escapeHtml(tex)}</p>`;
@@ -5,6 +5,7 @@
5
5
  const isValidDelim = (state, pos) => {
6
6
  const prevChar = pos > 0 ? state.src.charAt(pos - 1) : '';
7
7
  const nextChar = pos + 1 <= state.posMax ? state.src.charAt(pos + 1) : '';
8
+
8
9
  return {
9
10
  canOpen: nextChar !== ' ' && nextChar !== '\t',
10
11
  /*
@@ -31,6 +32,7 @@ const inlineTex = (state, silent) => {
31
32
  if (!res.canOpen) {
32
33
  if (!silent) state.pending += '$';
33
34
  state.pos += 1;
35
+
34
36
  return true;
35
37
  }
36
38
  /*
@@ -58,6 +60,7 @@ const inlineTex = (state, silent) => {
58
60
  if (match === -1) {
59
61
  if (!silent) state.pending += '$';
60
62
  state.pos = start;
63
+
61
64
  return true;
62
65
  }
63
66
 
@@ -65,6 +68,7 @@ const inlineTex = (state, silent) => {
65
68
  if (match - start === 0) {
66
69
  if (!silent) state.pending += '$$';
67
70
  state.pos = start + 1;
71
+
68
72
  return true;
69
73
  }
70
74
 
@@ -74,6 +78,7 @@ const inlineTex = (state, silent) => {
74
78
  if (!res.canClose) {
75
79
  if (!silent) state.pending += '$';
76
80
  state.pos = start;
81
+
77
82
  return true;
78
83
  }
79
84
 
@@ -12,6 +12,7 @@ const { inlineTex, blockTex } = require('./mathCommon');
12
12
  class MathToSvg {
13
13
  constructor() {
14
14
  const adaptor = liteAdaptor();
15
+
15
16
  RegisterHTMLHandler(adaptor);
16
17
 
17
18
  const packages = AllPackages.sort();
@@ -27,6 +28,7 @@ class MathToSvg {
27
28
 
28
29
  if (svg.includes('data-mml-node="merror"')) {
29
30
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
31
+
30
32
  svg = `<span class='katex-error' title='${escapeHtml(
31
33
  errorTitle
32
34
  )}'>${escapeHtml(tex)}</span>`;
@@ -41,6 +43,7 @@ class MathToSvg {
41
43
 
42
44
  if (svg.includes('data-mml-node="merror"')) {
43
45
  const errorTitle = svg.match(/<title>(.*?)<\/title>/)[1];
46
+
44
47
  svg = `<p class='katex-block katex-error' title='${escapeHtml(
45
48
  errorTitle
46
49
  )}'>${escapeHtml(tex)}</p>`;