twikoo-func 1.5.1 → 1.5.4

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 (2) hide show
  1. package/index.js +101 -21
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -50,20 +50,24 @@ const RES_CODE = {
50
50
  UPLOAD_FAILED: 1040
51
51
  }
52
52
  const ADMIN_USER_ID = 'admin'
53
+ const MAX_REQUEST_TIMES = parseInt(process.env.TWIKOO_THROTTLE) || 250
53
54
 
54
55
  // 全局变量 / variables
55
56
  // 警告:全局定义的变量,会被云函数缓存,请慎重定义全局变量
56
57
  // 参考 https://docs.cloudbase.net/cloud-function/deep-principle.html 中的 “实例复用”
57
58
  let config
58
59
  let transporter
60
+ const requestTimes = {}
59
61
 
60
62
  // 云函数入口点 / entry point
61
63
  exports.main = async (event, context) => {
64
+ console.log('请求IP:', auth.getClientIP())
62
65
  console.log('请求方法:', event.event)
63
66
  console.log('请求参数:', event)
64
67
  let res = {}
65
- await readConfig()
66
68
  try {
69
+ protect()
70
+ await readConfig()
67
71
  switch (event.event) {
68
72
  case 'GET_FUNC_VERSION':
69
73
  res = getFuncVersion()
@@ -87,7 +91,7 @@ exports.main = async (event, context) => {
87
91
  res = await commentLike(event)
88
92
  break
89
93
  case 'COMMENT_SUBMIT':
90
- res = await commentSubmit(event)
94
+ res = await commentSubmit(event, context)
91
95
  break
92
96
  case 'POST_SUBMIT':
93
97
  res = await postSubmit(event.comment, context)
@@ -834,7 +838,7 @@ async function like (id, uid) {
834
838
  * @param {String} event.pid 回复的 ID
835
839
  * @param {String} event.rid 评论楼 ID
836
840
  */
837
- async function commentSubmit (event) {
841
+ async function commentSubmit (event, context) {
838
842
  const res = {}
839
843
  // 参数校验
840
844
  validate(event, ['url', 'ua', 'comment'])
@@ -848,7 +852,7 @@ async function commentSubmit (event) {
848
852
  // 异步垃圾检测、发送评论通知
849
853
  try {
850
854
  await app.callFunction({
851
- name: 'twikoo',
855
+ name: context.function_name,
852
856
  data: { event: 'POST_SUBMIT', comment }
853
857
  }, { timeout: 300 }) // 设置较短的 timeout 来实现异步
854
858
  } catch (e) {
@@ -1168,6 +1172,12 @@ async function limitFilter () {
1168
1172
 
1169
1173
  // 预垃圾评论检测
1170
1174
  function preCheckSpam (comment) {
1175
+ // 长度限制
1176
+ let limitLength = parseInt(config.LIMIT_LENGTH)
1177
+ if (Number.isNaN(limitLength)) limitLength = 500
1178
+ if (limitLength && comment.length > limitLength) {
1179
+ throw new Error('评论内容过长')
1180
+ }
1171
1181
  if (config.AKISMET_KEY === 'MANUAL_REVIEW') {
1172
1182
  // 人工审核
1173
1183
  console.log('已使用人工审核模式,评论审核后才会发表~')
@@ -1386,9 +1396,9 @@ async function emailTest (event) {
1386
1396
  const isAdminUser = await isAdmin()
1387
1397
  if (isAdminUser) {
1388
1398
  try {
1389
- if (!transporter) {
1390
- await initMailer({ throwErr: true })
1391
- }
1399
+ // 邮件测试前清除 transporter,保证读取的是最新的配置
1400
+ transporter = null
1401
+ await initMailer({ throwErr: true })
1392
1402
  const sendResult = await transporter.sendMail({
1393
1403
  from: config.SENDER_EMAIL,
1394
1404
  to: event.mail || config.BLOGGER_EMAIL || config.SENDER_EMAIL,
@@ -1410,21 +1420,16 @@ async function uploadImage (event) {
1410
1420
  const { photo, fileName } = event
1411
1421
  const res = {}
1412
1422
  try {
1413
- if (!config.IMAGE_CDN_TOKEN) {
1423
+ if (!config.IMAGE_CDN || !config.IMAGE_CDN_TOKEN) {
1414
1424
  throw new Error('未配置图片上传服务')
1415
1425
  }
1416
- const formData = new FormData()
1417
- formData.append('image', base64UrlToReadStream(photo, fileName))
1418
- const uploadResult = await axios.post('https://7bu.top/api/upload', formData, {
1419
- headers: {
1420
- ...formData.getHeaders(),
1421
- token: config.IMAGE_CDN_TOKEN
1422
- }
1423
- })
1424
- if (uploadResult.data.code === 200) {
1425
- res.data = uploadResult.data.data
1426
- } else {
1427
- throw new Error(uploadResult.data.msg)
1426
+ // tip: qcloud 图床走前端上传,其他图床走后端上传
1427
+ if (config.IMAGE_CDN === '7bu') {
1428
+ await uploadImageTo7Bu({ photo, fileName, config, res })
1429
+ } else if (config.IMAGE_CDN === 'smms') {
1430
+ await uploadImageToSmms({ photo, fileName, config, res })
1431
+ } else if (isUrl(config.IMAGE_CDN)) {
1432
+ await uploadImageToLskyPro({ photo, fileName, config, res })
1428
1433
  }
1429
1434
  } catch (e) {
1430
1435
  console.error(e)
@@ -1434,6 +1439,64 @@ async function uploadImage (event) {
1434
1439
  return res
1435
1440
  }
1436
1441
 
1442
+ async function uploadImageTo7Bu ({ photo, fileName, config, res }) {
1443
+ // 去不图床旧版本 https://7bu.top
1444
+ // TODO: 2022 年 4 月 30 日后去不图床将会升级新版本,此处逻辑要同步更新
1445
+ const formData = new FormData()
1446
+ formData.append('image', base64UrlToReadStream(photo, fileName))
1447
+ const uploadResult = await axios.post('https://7bu.top/api/upload', formData, {
1448
+ headers: {
1449
+ ...formData.getHeaders(),
1450
+ token: config.IMAGE_CDN_TOKEN
1451
+ }
1452
+ })
1453
+ if (uploadResult.data.code === 200) {
1454
+ res.data = uploadResult.data.data
1455
+ } else {
1456
+ throw new Error(uploadResult.data.msg)
1457
+ }
1458
+ }
1459
+
1460
+ async function uploadImageToSmms ({ photo, fileName, config, res }) {
1461
+ // SM.MS 图床 https://sm.ms
1462
+ const formData = new FormData()
1463
+ formData.append('smfile', base64UrlToReadStream(photo, fileName))
1464
+ const uploadResult = await axios.post('https://sm.ms/api/v2/upload', formData, {
1465
+ headers: {
1466
+ ...formData.getHeaders(),
1467
+ Authorization: config.IMAGE_CDN_TOKEN
1468
+ }
1469
+ })
1470
+ if (uploadResult.data.success) {
1471
+ res.data = uploadResult.data.data
1472
+ } else {
1473
+ throw new Error(uploadResult.data.message)
1474
+ }
1475
+ }
1476
+
1477
+ async function uploadImageToLskyPro ({ photo, fileName, config, res }) {
1478
+ // 自定义兰空图床(v2)URL
1479
+ const formData = new FormData()
1480
+ formData.append('file', base64UrlToReadStream(photo, fileName))
1481
+ const url = `${config.IMAGE_CDN}/api/v1/upload`
1482
+ let token = config.IMAGE_CDN_TOKEN
1483
+ if (!token.startsWith('Bearer')) {
1484
+ token = `Bearer ${token}`
1485
+ }
1486
+ const uploadResult = await axios.post(url, formData, {
1487
+ headers: {
1488
+ ...formData.getHeaders(),
1489
+ Authorization: token
1490
+ }
1491
+ })
1492
+ if (uploadResult.data.status) {
1493
+ res.data = uploadResult.data.data
1494
+ res.data.url = res.data.links.url
1495
+ } else {
1496
+ throw new Error(uploadResult.data.message)
1497
+ }
1498
+ }
1499
+
1437
1500
  function base64UrlToReadStream (base64Url, fileName) {
1438
1501
  const base64 = base64Url.split(';base64,').pop()
1439
1502
  const path = `/tmp/${fileName}`
@@ -1441,6 +1504,10 @@ function base64UrlToReadStream (base64Url, fileName) {
1441
1504
  return fs.createReadStream(path)
1442
1505
  }
1443
1506
 
1507
+ function isUrl (s) {
1508
+ return /^http(s)?:\/\//.test(s)
1509
+ }
1510
+
1444
1511
  function getAvatar (comment) {
1445
1512
  if (comment.avatar) {
1446
1513
  return comment.avatar
@@ -1497,7 +1564,8 @@ function getConfig () {
1497
1564
  REQUIRED_FIELDS: config.REQUIRED_FIELDS,
1498
1565
  HIDE_ADMIN_CRYPT: config.HIDE_ADMIN_CRYPT,
1499
1566
  HIGHLIGHT: config.HIGHLIGHT || 'true',
1500
- HIGHLIGHT_THEME: config.HIGHLIGHT_THEME
1567
+ HIGHLIGHT_THEME: config.HIGHLIGHT_THEME,
1568
+ LIMIT_LENGTH: config.LIMIT_LENGTH
1501
1569
  }
1502
1570
  }
1503
1571
  }
@@ -1534,6 +1602,18 @@ async function setConfig (event) {
1534
1602
  }
1535
1603
  }
1536
1604
 
1605
+ function protect () {
1606
+ // 防御
1607
+ const ip = auth.getClientIP()
1608
+ requestTimes[ip] = (requestTimes[ip] || 0) + 1
1609
+ if (requestTimes[ip] > MAX_REQUEST_TIMES) {
1610
+ console.log(`${ip} 当前请求次数为 ${requestTimes[ip]},已超过最大请求次数`)
1611
+ throw new Error('Too Many Requests')
1612
+ } else {
1613
+ console.log(`${ip} 当前请求次数为 ${requestTimes[ip]}`)
1614
+ }
1615
+ }
1616
+
1537
1617
  // 读取配置
1538
1618
  async function readConfig () {
1539
1619
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twikoo-func",
3
- "version": "1.5.1",
3
+ "version": "1.5.4",
4
4
  "description": "A simple comment system based on Tencent CloudBase (tcb).",
5
5
  "author": "imaegoo <hello@imaegoo.com> (https://github.com/imaegoo)",
6
6
  "license": "MIT",