twikoo-vercel 1.7.14 → 1.7.16
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.
- package/api/index.js +82 -51
- package/package.json +2 -2
package/api/index.js
CHANGED
|
@@ -42,6 +42,13 @@ const {
|
|
|
42
42
|
checkCommentOwnership,
|
|
43
43
|
isValidEmail
|
|
44
44
|
} = require('twikoo-func/utils')
|
|
45
|
+
const {
|
|
46
|
+
createCap,
|
|
47
|
+
mongoStorage,
|
|
48
|
+
createChallenge,
|
|
49
|
+
redeemChallenge,
|
|
50
|
+
isBuiltinCap
|
|
51
|
+
} = require('twikoo-func/utils/cap')
|
|
45
52
|
const {
|
|
46
53
|
jsonParse,
|
|
47
54
|
commentImportValine,
|
|
@@ -68,10 +75,10 @@ const { RES_CODE, MAX_REQUEST_TIMES } = require('twikoo-func/utils/constants')
|
|
|
68
75
|
// 全局变量 / variables
|
|
69
76
|
let db = null
|
|
70
77
|
let config
|
|
71
|
-
let accessToken
|
|
72
78
|
const requestTimes = {}
|
|
73
79
|
|
|
74
80
|
module.exports = async (request, response) => {
|
|
81
|
+
let accessToken
|
|
75
82
|
const event = request.body || {}
|
|
76
83
|
logger.log('请求 IP:', getIp(request))
|
|
77
84
|
logger.log('请求函数:', event.event)
|
|
@@ -92,28 +99,28 @@ module.exports = async (request, response) => {
|
|
|
92
99
|
res = getFuncVersion({ VERSION })
|
|
93
100
|
break
|
|
94
101
|
case 'COMMENT_GET':
|
|
95
|
-
res = await commentGet(event)
|
|
102
|
+
res = await commentGet(event, accessToken)
|
|
96
103
|
break
|
|
97
104
|
case 'COMMENT_GET_FOR_ADMIN':
|
|
98
|
-
res = await commentGetForAdmin(event)
|
|
105
|
+
res = await commentGetForAdmin(event, accessToken)
|
|
99
106
|
break
|
|
100
107
|
case 'COMMENT_SET_FOR_ADMIN':
|
|
101
|
-
res = await commentSetForAdmin(event)
|
|
108
|
+
res = await commentSetForAdmin(event, accessToken)
|
|
102
109
|
break
|
|
103
110
|
case 'COMMENT_DELETE_FOR_ADMIN':
|
|
104
|
-
res = await commentDeleteForAdmin(event)
|
|
111
|
+
res = await commentDeleteForAdmin(event, accessToken)
|
|
105
112
|
break
|
|
106
113
|
case 'COMMENT_DELETE_FOR_USER':
|
|
107
|
-
res = await commentDeleteForUser(event)
|
|
114
|
+
res = await commentDeleteForUser(event, accessToken)
|
|
108
115
|
break
|
|
109
116
|
case 'COMMENT_IMPORT_FOR_ADMIN':
|
|
110
|
-
res = await commentImportForAdmin(event)
|
|
117
|
+
res = await commentImportForAdmin(event, accessToken)
|
|
111
118
|
break
|
|
112
119
|
case 'COMMENT_LIKE':
|
|
113
|
-
res = await commentLike(event)
|
|
120
|
+
res = await commentLike(event, accessToken)
|
|
114
121
|
break
|
|
115
122
|
case 'COMMENT_SUBMIT':
|
|
116
|
-
res = await commentSubmit(event, request)
|
|
123
|
+
res = await commentSubmit(event, request, accessToken)
|
|
117
124
|
break
|
|
118
125
|
case 'POST_SUBMIT':
|
|
119
126
|
res = await postSubmit(event.comment, request)
|
|
@@ -125,16 +132,16 @@ module.exports = async (request, response) => {
|
|
|
125
132
|
res = await getPasswordStatus(config, VERSION)
|
|
126
133
|
break
|
|
127
134
|
case 'SET_PASSWORD':
|
|
128
|
-
res = await setPassword(event)
|
|
135
|
+
res = await setPassword(event, accessToken)
|
|
129
136
|
break
|
|
130
137
|
case 'GET_CONFIG':
|
|
131
|
-
res = await getConfig({ config, VERSION, isAdmin: isAdmin() })
|
|
138
|
+
res = await getConfig({ config, VERSION, isAdmin: isAdmin(accessToken) })
|
|
132
139
|
break
|
|
133
140
|
case 'GET_CONFIG_FOR_ADMIN':
|
|
134
|
-
res = await getConfigForAdmin({ config, isAdmin: isAdmin() })
|
|
141
|
+
res = await getConfigForAdmin({ config, isAdmin: isAdmin(accessToken) })
|
|
135
142
|
break
|
|
136
143
|
case 'SET_CONFIG':
|
|
137
|
-
res = await setConfig(event)
|
|
144
|
+
res = await setConfig(event, accessToken)
|
|
138
145
|
break
|
|
139
146
|
case 'LOGIN':
|
|
140
147
|
res = await login(event.password)
|
|
@@ -146,7 +153,7 @@ module.exports = async (request, response) => {
|
|
|
146
153
|
res = await getRecentComments(event)
|
|
147
154
|
break
|
|
148
155
|
case 'EMAIL_TEST': // >= 1.4.6
|
|
149
|
-
res = await emailTest(event, config, isAdmin())
|
|
156
|
+
res = await emailTest(event, config, isAdmin(accessToken))
|
|
150
157
|
break
|
|
151
158
|
case 'UPLOAD_IMAGE': // >= 1.5.0
|
|
152
159
|
res = await uploadImage(event, config)
|
|
@@ -155,7 +162,13 @@ module.exports = async (request, response) => {
|
|
|
155
162
|
res = await qqNickGet(event)
|
|
156
163
|
break
|
|
157
164
|
case 'COMMENT_EXPORT_FOR_ADMIN': // >= 1.6.13
|
|
158
|
-
res = await commentExportForAdmin(event)
|
|
165
|
+
res = await commentExportForAdmin(event, accessToken)
|
|
166
|
+
break
|
|
167
|
+
case 'CAP_CHALLENGE':
|
|
168
|
+
res = await capChallenge()
|
|
169
|
+
break
|
|
170
|
+
case 'CAP_REDEEM':
|
|
171
|
+
res = await capRedeem(event)
|
|
159
172
|
break
|
|
160
173
|
default:
|
|
161
174
|
if (event.event) {
|
|
@@ -247,8 +260,8 @@ async function connectToDatabase (uri) {
|
|
|
247
260
|
}
|
|
248
261
|
|
|
249
262
|
// 写入管理密码
|
|
250
|
-
async function setPassword (event) {
|
|
251
|
-
const isAdminUser = isAdmin()
|
|
263
|
+
async function setPassword (event, accessToken) {
|
|
264
|
+
const isAdminUser = isAdmin(accessToken)
|
|
252
265
|
// 如果数据库里没有密码,则写入密码
|
|
253
266
|
// 如果数据库里有密码,则只有管理员可以写入密码
|
|
254
267
|
if (config.ADMIN_PASS && !isAdminUser) {
|
|
@@ -278,12 +291,12 @@ async function login (password) {
|
|
|
278
291
|
}
|
|
279
292
|
|
|
280
293
|
// 读取评论
|
|
281
|
-
async function commentGet (event) {
|
|
294
|
+
async function commentGet (event, accessToken) {
|
|
282
295
|
const res = {}
|
|
283
296
|
try {
|
|
284
297
|
validate(event, ['url'])
|
|
285
|
-
const uid =
|
|
286
|
-
const isAdminUser = isAdmin()
|
|
298
|
+
const uid = accessToken
|
|
299
|
+
const isAdminUser = isAdmin(accessToken)
|
|
287
300
|
const limit = parseInt(config.COMMENT_PAGE_SIZE) || 8
|
|
288
301
|
const sort = event.sort || 'newest'
|
|
289
302
|
let more = false
|
|
@@ -377,9 +390,9 @@ function getCommentQuery ({ condition, uid, isAdminUser }) {
|
|
|
377
390
|
}
|
|
378
391
|
|
|
379
392
|
// 管理员读取评论
|
|
380
|
-
async function commentGetForAdmin (event) {
|
|
393
|
+
async function commentGetForAdmin (event, accessToken) {
|
|
381
394
|
const res = {}
|
|
382
|
-
const isAdminUser = isAdmin()
|
|
395
|
+
const isAdminUser = isAdmin(accessToken)
|
|
383
396
|
if (isAdminUser) {
|
|
384
397
|
validate(event, ['per', 'page'])
|
|
385
398
|
const collection = db
|
|
@@ -435,9 +448,9 @@ function getCommentSearchCondition (event) {
|
|
|
435
448
|
}
|
|
436
449
|
|
|
437
450
|
// 管理员修改评论
|
|
438
|
-
async function commentSetForAdmin (event) {
|
|
451
|
+
async function commentSetForAdmin (event, accessToken) {
|
|
439
452
|
const res = {}
|
|
440
|
-
const isAdminUser = isAdmin()
|
|
453
|
+
const isAdminUser = isAdmin(accessToken)
|
|
441
454
|
if (isAdminUser) {
|
|
442
455
|
validate(event, ['id', 'set'])
|
|
443
456
|
const data = await db
|
|
@@ -458,9 +471,9 @@ async function commentSetForAdmin (event) {
|
|
|
458
471
|
}
|
|
459
472
|
|
|
460
473
|
// 管理员删除评论
|
|
461
|
-
async function commentDeleteForAdmin (event) {
|
|
474
|
+
async function commentDeleteForAdmin (event, accessToken) {
|
|
462
475
|
const res = {}
|
|
463
|
-
const isAdminUser = isAdmin()
|
|
476
|
+
const isAdminUser = isAdmin(accessToken)
|
|
464
477
|
if (isAdminUser) {
|
|
465
478
|
validate(event, ['id'])
|
|
466
479
|
const data = await db
|
|
@@ -476,10 +489,10 @@ async function commentDeleteForAdmin (event) {
|
|
|
476
489
|
}
|
|
477
490
|
|
|
478
491
|
// 用户删除自己的评论
|
|
479
|
-
async function commentDeleteForUser (event) {
|
|
492
|
+
async function commentDeleteForUser (event, accessToken) {
|
|
480
493
|
const res = {}
|
|
481
494
|
try {
|
|
482
|
-
const uid =
|
|
495
|
+
const uid = accessToken
|
|
483
496
|
await checkCommentOwnership(event.id, uid, async (id) => {
|
|
484
497
|
return db.collection('comment').findOne({ _id: id })
|
|
485
498
|
})
|
|
@@ -494,13 +507,13 @@ async function commentDeleteForUser (event) {
|
|
|
494
507
|
}
|
|
495
508
|
|
|
496
509
|
// 管理员导入评论
|
|
497
|
-
async function commentImportForAdmin (event) {
|
|
510
|
+
async function commentImportForAdmin (event, accessToken) {
|
|
498
511
|
const res = {}
|
|
499
512
|
let logText = ''
|
|
500
513
|
const log = (message) => {
|
|
501
514
|
logText += `${new Date().toLocaleString()} ${message}\n`
|
|
502
515
|
}
|
|
503
|
-
const isAdminUser = isAdmin()
|
|
516
|
+
const isAdminUser = isAdmin(accessToken)
|
|
504
517
|
if (isAdminUser) {
|
|
505
518
|
try {
|
|
506
519
|
validate(event, ['source', 'file'])
|
|
@@ -550,9 +563,9 @@ async function commentImportForAdmin (event) {
|
|
|
550
563
|
return res
|
|
551
564
|
}
|
|
552
565
|
|
|
553
|
-
async function commentExportForAdmin (event) {
|
|
566
|
+
async function commentExportForAdmin (event, accessToken) {
|
|
554
567
|
const res = {}
|
|
555
|
-
const isAdminUser = isAdmin()
|
|
568
|
+
const isAdminUser = isAdmin(accessToken)
|
|
556
569
|
if (isAdminUser) {
|
|
557
570
|
const collection = event.collection || 'comment'
|
|
558
571
|
const data = await db
|
|
@@ -595,11 +608,11 @@ async function bulkSaveComments (comments) {
|
|
|
595
608
|
}
|
|
596
609
|
|
|
597
610
|
// 点赞 / 反对 / 取消
|
|
598
|
-
async function commentLike (event) {
|
|
611
|
+
async function commentLike (event, accessToken) {
|
|
599
612
|
const res = {}
|
|
600
613
|
validate(event, ['id'])
|
|
601
614
|
const type = event.type || 'up'
|
|
602
|
-
res.updated = await like(event.id,
|
|
615
|
+
res.updated = await like(event.id, accessToken, type)
|
|
603
616
|
return res
|
|
604
617
|
}
|
|
605
618
|
|
|
@@ -654,7 +667,7 @@ async function like (id, uid, type) {
|
|
|
654
667
|
* @param {String} event.pid 回复的 ID
|
|
655
668
|
* @param {String} event.rid 评论楼 ID
|
|
656
669
|
*/
|
|
657
|
-
async function commentSubmit (event, request) {
|
|
670
|
+
async function commentSubmit (event, request, accessToken) {
|
|
658
671
|
const res = {}
|
|
659
672
|
// 参数校验
|
|
660
673
|
validate(event, ['url', 'ua', 'comment'])
|
|
@@ -663,7 +676,7 @@ async function commentSubmit (event, request) {
|
|
|
663
676
|
// 验证码
|
|
664
677
|
await checkCaptcha(event, request)
|
|
665
678
|
// 预检测、转换
|
|
666
|
-
const data = await parse(event, request)
|
|
679
|
+
const data = await parse(event, request, accessToken)
|
|
667
680
|
// 保存
|
|
668
681
|
const comment = await save(data)
|
|
669
682
|
res.id = comment.id
|
|
@@ -714,16 +727,16 @@ async function postSubmit (comment, request) {
|
|
|
714
727
|
}
|
|
715
728
|
|
|
716
729
|
// 将评论转为数据库存储格式
|
|
717
|
-
async function parse (comment, request) {
|
|
730
|
+
async function parse (comment, request, accessToken) {
|
|
718
731
|
const timestamp = Date.now()
|
|
719
|
-
const isAdminUser = isAdmin()
|
|
732
|
+
const isAdminUser = isAdmin(accessToken)
|
|
720
733
|
const isBloggerMail = equalsMail(comment.mail, config.BLOGGER_EMAIL)
|
|
721
734
|
if (isBloggerMail && !isAdminUser) throw new Error('请先登录管理面板,再使用博主身份发送评论')
|
|
722
735
|
if (comment.mail && !isValidEmail(comment.mail)) throw new Error('邮箱格式不合法')
|
|
723
736
|
const hashMethod = config.GRAVATAR_CDN === 'cravatar.cn' ? md5 : sha256
|
|
724
737
|
const commentDo = {
|
|
725
738
|
_id: uuidv4().replace(/-/g, ''),
|
|
726
|
-
uid:
|
|
739
|
+
uid: accessToken,
|
|
727
740
|
nick: comment.nick ? comment.nick : '匿名',
|
|
728
741
|
mail: comment.mail ? comment.mail : '',
|
|
729
742
|
mailMd5: comment.mail ? hashMethod(normalizeMail(comment.mail)) : '',
|
|
@@ -796,6 +809,14 @@ async function checkCaptcha (comment, request) {
|
|
|
796
809
|
geeTestPassToken: comment.geeTestPassToken,
|
|
797
810
|
geeTestGenTime: comment.geeTestGenTime
|
|
798
811
|
})
|
|
812
|
+
} else if (provider === 'Cap' && isBuiltinCap(config)) {
|
|
813
|
+
if (!comment.capToken) {
|
|
814
|
+
throw new Error('验证码 token 缺失,请刷新页面重试')
|
|
815
|
+
}
|
|
816
|
+
await checkCapCaptcha({
|
|
817
|
+
capToken: comment.capToken,
|
|
818
|
+
cap: createCap(mongoStorage(db))
|
|
819
|
+
})
|
|
799
820
|
} else if (provider === 'Cap' && config.CAP_API_ENDPOINT && config.CAP_SECRET_KEY) {
|
|
800
821
|
if (!comment.capToken) {
|
|
801
822
|
throw new Error('验证码 token 缺失,请刷新页面重试')
|
|
@@ -806,7 +827,7 @@ async function checkCaptcha (comment, request) {
|
|
|
806
827
|
capApiEndpoint: config.CAP_API_ENDPOINT
|
|
807
828
|
})
|
|
808
829
|
} else if (provider === 'Cap') {
|
|
809
|
-
throw new Error('Cap
|
|
830
|
+
throw new Error('Cap 验证码配置不完整:内嵌模式无需额外配置,外部模式需填写 CAP_API_ENDPOINT 与 CAP_SECRET_KEY')
|
|
810
831
|
} else if (provider) {
|
|
811
832
|
throw new Error(`不支持的验证码类型: ${provider}`)
|
|
812
833
|
}
|
|
@@ -975,8 +996,8 @@ async function qqNickGet (event) {
|
|
|
975
996
|
}
|
|
976
997
|
|
|
977
998
|
// 修改配置
|
|
978
|
-
async function setConfig (event) {
|
|
979
|
-
const isAdminUser = isAdmin()
|
|
999
|
+
async function setConfig (event, accessToken) {
|
|
1000
|
+
const isAdminUser = isAdmin(accessToken)
|
|
980
1001
|
if (isAdminUser) {
|
|
981
1002
|
writeConfig(event.config)
|
|
982
1003
|
return {
|
|
@@ -1043,15 +1064,9 @@ async function writeConfig (newConfig) {
|
|
|
1043
1064
|
}
|
|
1044
1065
|
}
|
|
1045
1066
|
|
|
1046
|
-
// 获取用户 ID
|
|
1047
|
-
function getUid () {
|
|
1048
|
-
return accessToken
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
1067
|
// 判断用户是否管理员
|
|
1052
|
-
function isAdmin () {
|
|
1053
|
-
|
|
1054
|
-
return config.ADMIN_PASS === md5(uid)
|
|
1068
|
+
function isAdmin (accessToken) {
|
|
1069
|
+
return config.ADMIN_PASS === md5(accessToken)
|
|
1055
1070
|
}
|
|
1056
1071
|
|
|
1057
1072
|
// 判断是否为递归调用(即云函数调用自身)
|
|
@@ -1061,7 +1076,7 @@ function isRecursion (request) {
|
|
|
1061
1076
|
|
|
1062
1077
|
// 建立数据库 collections
|
|
1063
1078
|
async function createCollections () {
|
|
1064
|
-
const collections = ['comment', 'config', 'counter']
|
|
1079
|
+
const collections = ['comment', 'config', 'counter', 'cap_challenges', 'cap_tokens']
|
|
1065
1080
|
const res = {}
|
|
1066
1081
|
for (const collection of collections) {
|
|
1067
1082
|
try {
|
|
@@ -1073,6 +1088,22 @@ async function createCollections () {
|
|
|
1073
1088
|
return res
|
|
1074
1089
|
}
|
|
1075
1090
|
|
|
1091
|
+
async function capChallenge () {
|
|
1092
|
+
if (!isBuiltinCap(config)) {
|
|
1093
|
+
return { code: RES_CODE.FAIL, message: '内嵌 Cap 未启用' }
|
|
1094
|
+
}
|
|
1095
|
+
const data = await createChallenge(createCap(mongoStorage(db)))
|
|
1096
|
+
return { code: RES_CODE.SUCCESS, ...data }
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
async function capRedeem (event) {
|
|
1100
|
+
if (!isBuiltinCap(config)) {
|
|
1101
|
+
return { code: RES_CODE.FAIL, message: '内嵌 Cap 未启用' }
|
|
1102
|
+
}
|
|
1103
|
+
const data = await redeemChallenge(createCap(mongoStorage(db)), event)
|
|
1104
|
+
return { code: RES_CODE.SUCCESS, ...data }
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1076
1107
|
function getIp (request) {
|
|
1077
1108
|
try {
|
|
1078
1109
|
const { TWIKOO_IP_HEADERS } = process.env
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twikoo-vercel",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.16",
|
|
4
4
|
"description": "A simple comment system.",
|
|
5
5
|
"author": "imaegoo <hello@imaegoo.com> (https://github.com/imaegoo)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"get-user-ip": "^1.0.1",
|
|
15
15
|
"mongodb": "^6.3.0",
|
|
16
|
-
"twikoo-func": "1.7.
|
|
16
|
+
"twikoo-func": "1.7.16",
|
|
17
17
|
"uuid": "^8.3.2"
|
|
18
18
|
}
|
|
19
19
|
}
|