tkserver 1.7.8 → 1.7.10
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/README.md +1 -0
- package/index.js +53 -2
- package/mongo.js +41 -3
- package/package.json +2 -2
- package/server.js +86 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ tkserver
|
|
|
19
19
|
| `MONGODB_URI` | MongoDB 数据库连接字符串,不传则使用 lokijs | `null` |
|
|
20
20
|
| `MONGO_URL` | MongoDB 数据库连接字符串,不传则使用 lokijs | `null` |
|
|
21
21
|
| `TWIKOO_DATA` | lokijs 数据库存储路径 | `./data` |
|
|
22
|
+
| `TWIKOO_HOST` | 自定义监听的主机名或IP地址(例如 0.0.0.0 或 127.0.0.1),设置该值则会忽略 TWIKOO_LOCALHOST_ONLY,默认值为 null 但实际行为会回退到 `::` | `null` |
|
|
22
23
|
| `TWIKOO_PORT` | 端口号 | `8080` |
|
|
23
24
|
| `TWIKOO_THROTTLE` | IP 请求限流,当同一 IP 短时间内请求次数超过阈值将对该 IP 返回错误 | `250` |
|
|
24
25
|
| `TWIKOO_LOCALHOST_ONLY` | 为`true`时只监听本地请求,使得 nginx 等服务器反代之后不暴露原始端口 | `null` |
|
package/index.js
CHANGED
|
@@ -39,7 +39,8 @@ const {
|
|
|
39
39
|
checkCapCaptcha,
|
|
40
40
|
getConfig,
|
|
41
41
|
getConfigForAdmin,
|
|
42
|
-
validate
|
|
42
|
+
validate,
|
|
43
|
+
checkCommentOwnership
|
|
43
44
|
} = require('twikoo-func/utils')
|
|
44
45
|
const {
|
|
45
46
|
jsonParse,
|
|
@@ -68,6 +69,7 @@ const TWIKOO_REQ_TIMES_CLEAR_TIME = parseInt(process.env.TWIKOO_REQ_TIMES_CLEAR_
|
|
|
68
69
|
let db = null
|
|
69
70
|
let config
|
|
70
71
|
let requestTimes = {}
|
|
72
|
+
let requestTimesTimer = null
|
|
71
73
|
|
|
72
74
|
connectToDatabase()
|
|
73
75
|
|
|
@@ -103,6 +105,9 @@ module.exports = async (request, response) => {
|
|
|
103
105
|
case 'COMMENT_DELETE_FOR_ADMIN':
|
|
104
106
|
res = await commentDeleteForAdmin(event)
|
|
105
107
|
break
|
|
108
|
+
case 'COMMENT_DELETE_FOR_USER':
|
|
109
|
+
res = await commentDeleteForUser(event)
|
|
110
|
+
break
|
|
106
111
|
case 'COMMENT_IMPORT_FOR_ADMIN':
|
|
107
112
|
res = await commentImportForAdmin(event)
|
|
108
113
|
break
|
|
@@ -474,6 +479,24 @@ async function commentDeleteForAdmin (event) {
|
|
|
474
479
|
return res
|
|
475
480
|
}
|
|
476
481
|
|
|
482
|
+
// 用户删除自己的评论
|
|
483
|
+
async function commentDeleteForUser (event) {
|
|
484
|
+
const res = {}
|
|
485
|
+
try {
|
|
486
|
+
const uid = event.accessToken
|
|
487
|
+
await checkCommentOwnership(event.id, uid, (id) => {
|
|
488
|
+
return db.getCollection('comment').findOne({ _id: id })
|
|
489
|
+
})
|
|
490
|
+
db.getCollection('comment').findAndRemove({ _id: event.id })
|
|
491
|
+
res.code = RES_CODE.SUCCESS
|
|
492
|
+
res.deleted = 1
|
|
493
|
+
} catch (e) {
|
|
494
|
+
res.code = RES_CODE.FAIL
|
|
495
|
+
res.message = e.message
|
|
496
|
+
}
|
|
497
|
+
return res
|
|
498
|
+
}
|
|
499
|
+
|
|
477
500
|
// 管理员导入评论
|
|
478
501
|
async function commentImportForAdmin (event) {
|
|
479
502
|
const res = {}
|
|
@@ -1043,8 +1066,36 @@ function getIp (request) {
|
|
|
1043
1066
|
return getUserIP(request)
|
|
1044
1067
|
}
|
|
1045
1068
|
|
|
1069
|
+
async function closeDatabase () {
|
|
1070
|
+
if (!db) return
|
|
1071
|
+
try {
|
|
1072
|
+
await new Promise((resolve, reject) => {
|
|
1073
|
+
db.saveDatabase((err) => {
|
|
1074
|
+
if (err) {
|
|
1075
|
+
reject(err)
|
|
1076
|
+
} else {
|
|
1077
|
+
resolve()
|
|
1078
|
+
}
|
|
1079
|
+
})
|
|
1080
|
+
})
|
|
1081
|
+
} finally {
|
|
1082
|
+
db.close()
|
|
1083
|
+
db = null
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
async function shutdown () {
|
|
1088
|
+
if (requestTimesTimer) {
|
|
1089
|
+
clearInterval(requestTimesTimer)
|
|
1090
|
+
requestTimesTimer = null
|
|
1091
|
+
}
|
|
1092
|
+
await closeDatabase()
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1046
1095
|
function clearRequestTimes () {
|
|
1047
1096
|
requestTimes = {}
|
|
1048
1097
|
}
|
|
1049
1098
|
|
|
1050
|
-
setInterval(clearRequestTimes, TWIKOO_REQ_TIMES_CLEAR_TIME)
|
|
1099
|
+
requestTimesTimer = setInterval(clearRequestTimes, TWIKOO_REQ_TIMES_CLEAR_TIME)
|
|
1100
|
+
|
|
1101
|
+
module.exports.shutdown = shutdown
|
package/mongo.js
CHANGED
|
@@ -36,7 +36,8 @@ const {
|
|
|
36
36
|
checkCapCaptcha,
|
|
37
37
|
getConfig,
|
|
38
38
|
getConfigForAdmin,
|
|
39
|
-
validate
|
|
39
|
+
validate,
|
|
40
|
+
checkCommentOwnership
|
|
40
41
|
} = require('twikoo-func/utils')
|
|
41
42
|
const {
|
|
42
43
|
jsonParse,
|
|
@@ -65,6 +66,8 @@ const TWIKOO_REQ_TIMES_CLEAR_TIME = parseInt(process.env.TWIKOO_REQ_TIMES_CLEAR_
|
|
|
65
66
|
let db = null
|
|
66
67
|
let config
|
|
67
68
|
let requestTimes = {}
|
|
69
|
+
let client = null
|
|
70
|
+
let requestTimesTimer = null
|
|
68
71
|
|
|
69
72
|
module.exports = async (request, response) => {
|
|
70
73
|
let accessToken
|
|
@@ -99,6 +102,9 @@ module.exports = async (request, response) => {
|
|
|
99
102
|
case 'COMMENT_DELETE_FOR_ADMIN':
|
|
100
103
|
res = await commentDeleteForAdmin(event)
|
|
101
104
|
break
|
|
105
|
+
case 'COMMENT_DELETE_FOR_USER':
|
|
106
|
+
res = await commentDeleteForUser(event)
|
|
107
|
+
break
|
|
102
108
|
case 'COMMENT_IMPORT_FOR_ADMIN':
|
|
103
109
|
res = await commentImportForAdmin(event)
|
|
104
110
|
break
|
|
@@ -221,7 +227,7 @@ async function connectToDatabase (uri) {
|
|
|
221
227
|
if (!uri) throw new Error('未设置环境变量 MONGODB_URI | MONGO_URL')
|
|
222
228
|
// If no connection is cached, create a new one
|
|
223
229
|
logger.info('Connecting to database...')
|
|
224
|
-
|
|
230
|
+
client = await MongoClient.connect(uri, {})
|
|
225
231
|
// Select the database through the connection,
|
|
226
232
|
// using the database path of the connection string
|
|
227
233
|
const dbName = (new URL(uri)).pathname.substring(1) || 'twikoo'
|
|
@@ -460,6 +466,24 @@ async function commentDeleteForAdmin (event) {
|
|
|
460
466
|
return res
|
|
461
467
|
}
|
|
462
468
|
|
|
469
|
+
// 用户删除自己的评论
|
|
470
|
+
async function commentDeleteForUser (event) {
|
|
471
|
+
const res = {}
|
|
472
|
+
try {
|
|
473
|
+
const uid = event.accessToken
|
|
474
|
+
await checkCommentOwnership(event.id, uid, async (id) => {
|
|
475
|
+
return db.collection('comment').findOne({ _id: id })
|
|
476
|
+
})
|
|
477
|
+
const data = await db.collection('comment').deleteOne({ _id: event.id })
|
|
478
|
+
res.code = RES_CODE.SUCCESS
|
|
479
|
+
res.deleted = data.deletedCount
|
|
480
|
+
} catch (e) {
|
|
481
|
+
res.code = RES_CODE.FAIL
|
|
482
|
+
res.message = e.message
|
|
483
|
+
}
|
|
484
|
+
return res
|
|
485
|
+
}
|
|
486
|
+
|
|
463
487
|
// 管理员导入评论
|
|
464
488
|
async function commentImportForAdmin (event) {
|
|
465
489
|
const res = {}
|
|
@@ -1015,8 +1039,22 @@ function getIp (request) {
|
|
|
1015
1039
|
return getUserIP(request)
|
|
1016
1040
|
}
|
|
1017
1041
|
|
|
1042
|
+
async function shutdown () {
|
|
1043
|
+
if (requestTimesTimer) {
|
|
1044
|
+
clearInterval(requestTimesTimer)
|
|
1045
|
+
requestTimesTimer = null
|
|
1046
|
+
}
|
|
1047
|
+
if (client) {
|
|
1048
|
+
await client.close()
|
|
1049
|
+
client = null
|
|
1050
|
+
db = null
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1018
1054
|
function clearRequestTimes () {
|
|
1019
1055
|
requestTimes = {}
|
|
1020
1056
|
}
|
|
1021
1057
|
|
|
1022
|
-
setInterval(clearRequestTimes, TWIKOO_REQ_TIMES_CLEAR_TIME)
|
|
1058
|
+
requestTimesTimer = setInterval(clearRequestTimes, TWIKOO_REQ_TIMES_CLEAR_TIME)
|
|
1059
|
+
|
|
1060
|
+
module.exports.shutdown = shutdown
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tkserver",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.10",
|
|
4
4
|
"description": "A simple comment system.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twikoo",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"get-user-ip": "^1.0.1",
|
|
32
32
|
"lokijs": "^1.5.12",
|
|
33
33
|
"mongodb": "^6.3.0",
|
|
34
|
-
"twikoo-func": "1.7.
|
|
34
|
+
"twikoo-func": "1.7.10",
|
|
35
35
|
"uuid": "^8.3.2"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/server.js
CHANGED
|
@@ -6,8 +6,20 @@ const logger = require('twikoo-func/utils/logger')
|
|
|
6
6
|
const dbUrl = process.env.MONGODB_URI || process.env.MONGO_URL || null
|
|
7
7
|
const twikoo = dbUrl ? require('./mongo') : require('./index')
|
|
8
8
|
const server = http.createServer()
|
|
9
|
+
const sockets = new Set()
|
|
10
|
+
const TWIKOO_SHUTDOWN_TIMEOUT = parseInt(process.env.TWIKOO_SHUTDOWN_TIMEOUT) || 5000
|
|
11
|
+
let isShuttingDown = false
|
|
12
|
+
let shuttingDownPromise = null
|
|
9
13
|
|
|
10
14
|
server.on('request', async function (request, response) {
|
|
15
|
+
if (isShuttingDown) {
|
|
16
|
+
response.writeHead(503, {
|
|
17
|
+
Connection: 'close',
|
|
18
|
+
'Content-Type': 'application/json'
|
|
19
|
+
})
|
|
20
|
+
response.end(JSON.stringify({ code: 503, message: 'Twikoo server is shutting down' }))
|
|
21
|
+
return
|
|
22
|
+
}
|
|
11
23
|
try {
|
|
12
24
|
const buffers = []
|
|
13
25
|
for await (const chunk of request) {
|
|
@@ -31,10 +43,83 @@ server.on('request', async function (request, response) {
|
|
|
31
43
|
return await twikoo(request, response)
|
|
32
44
|
})
|
|
33
45
|
|
|
46
|
+
server.on('connection', (socket) => {
|
|
47
|
+
sockets.add(socket)
|
|
48
|
+
socket.on('close', () => sockets.delete(socket))
|
|
49
|
+
})
|
|
50
|
+
|
|
34
51
|
const port = parseInt(process.env.TWIKOO_PORT) || 8080
|
|
35
|
-
const host = process.env.TWIKOO_LOCALHOST_ONLY === 'true' ? 'localhost' : '::'
|
|
52
|
+
const host = process.env.TWIKOO_HOST || (process.env.TWIKOO_LOCALHOST_ONLY === 'true' ? 'localhost' : '::')
|
|
36
53
|
|
|
37
54
|
server.listen(port, host, function () {
|
|
38
55
|
logger.info(`Twikoo is using ${dbUrl ? 'mongo' : 'loki'} database`)
|
|
39
56
|
logger.info(`Twikoo function started on host ${host} port ${port}`)
|
|
40
57
|
})
|
|
58
|
+
|
|
59
|
+
function closeServer () {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
server.close((err) => {
|
|
62
|
+
if (err) {
|
|
63
|
+
reject(err)
|
|
64
|
+
} else {
|
|
65
|
+
resolve()
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function destroySockets () {
|
|
72
|
+
for (const socket of sockets) {
|
|
73
|
+
socket.destroy()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function shutdownWithTimeout () {
|
|
78
|
+
let timeoutId
|
|
79
|
+
const closeTask = closeServer()
|
|
80
|
+
const timeoutTask = new Promise((resolve) => {
|
|
81
|
+
timeoutId = setTimeout(() => {
|
|
82
|
+
logger.warn(`Twikoo server shutdown timed out after ${TWIKOO_SHUTDOWN_TIMEOUT}ms, destroying open connections`)
|
|
83
|
+
destroySockets()
|
|
84
|
+
resolve()
|
|
85
|
+
}, TWIKOO_SHUTDOWN_TIMEOUT)
|
|
86
|
+
})
|
|
87
|
+
await Promise.race([closeTask, timeoutTask])
|
|
88
|
+
clearTimeout(timeoutId)
|
|
89
|
+
await closeTask
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function runShutdown (signal) {
|
|
93
|
+
isShuttingDown = true
|
|
94
|
+
logger.info(`Received ${signal}, shutting down Twikoo server...`)
|
|
95
|
+
try {
|
|
96
|
+
await shutdownWithTimeout()
|
|
97
|
+
} catch (e) {
|
|
98
|
+
logger.error('Twikoo HTTP server shutdown failed:', e)
|
|
99
|
+
} finally {
|
|
100
|
+
if (typeof twikoo.shutdown === 'function') {
|
|
101
|
+
await twikoo.shutdown()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
logger.info('Twikoo server stopped')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function shutdown (signal) {
|
|
108
|
+
if (!shuttingDownPromise) {
|
|
109
|
+
shuttingDownPromise = runShutdown(signal)
|
|
110
|
+
}
|
|
111
|
+
return await shuttingDownPromise
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function handleSignal (signal) {
|
|
115
|
+
try {
|
|
116
|
+
await shutdown(signal)
|
|
117
|
+
process.exitCode = 0
|
|
118
|
+
} catch (e) {
|
|
119
|
+
logger.error('Twikoo server shutdown failed:', e)
|
|
120
|
+
process.exitCode = 1
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
process.on('SIGTERM', () => handleSignal('SIGTERM'))
|
|
125
|
+
process.on('SIGINT', () => handleSignal('SIGINT'))
|