wok-server 0.5.0 → 0.7.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.
- package/README.en.md +61 -0
- package/README.md +44 -29
- package/dist/cache/cache.js +98 -98
- package/dist/cache/config.js +19 -19
- package/dist/cache/index.js +27 -27
- package/dist/cache/purge-task.js +46 -46
- package/dist/cache/stat.js +47 -47
- package/dist/config/convert.js +36 -36
- package/dist/config/exception.js +14 -14
- package/dist/config/index.js +81 -81
- package/dist/http-client/index.js +136 -136
- package/dist/i18n/ar.js +17 -17
- package/dist/i18n/de.js +17 -17
- package/dist/i18n/en-us.js +17 -17
- package/dist/i18n/es.js +17 -17
- package/dist/i18n/fr.js +17 -17
- package/dist/i18n/i18n.js +231 -231
- package/dist/i18n/index.js +52 -52
- package/dist/i18n/ja.js +17 -17
- package/dist/i18n/ko.js +17 -17
- package/dist/i18n/msg.js +2 -2
- package/dist/i18n/pt.js +17 -17
- package/dist/i18n/ru.js +17 -17
- package/dist/i18n/tag.js +18 -18
- package/dist/i18n/zh-HK.js +17 -17
- package/dist/i18n/zh-TW.js +17 -17
- package/dist/i18n/zh-cn.js +17 -17
- package/dist/index.js +14 -14
- package/dist/lock/index.js +114 -114
- package/dist/log/config.js +35 -35
- package/dist/log/date.js +21 -21
- package/dist/log/file.js +198 -198
- package/dist/log/index.js +135 -135
- package/dist/log/level.js +33 -33
- package/dist/log/log.js +56 -56
- package/dist/log/store.js +19 -19
- package/dist/mongodb/collection.js +2 -2
- package/dist/mongodb/config.js +34 -34
- package/dist/mongodb/doc.js +2 -2
- package/dist/mongodb/exception.js +14 -14
- package/dist/mongodb/index.js +58 -58
- package/dist/mongodb/manager/base.js +563 -563
- package/dist/mongodb/manager/index.js +63 -63
- package/dist/mongodb/manager/tx-strict.js +84 -84
- package/dist/mongodb/manager/tx.js +30 -30
- package/dist/mongodb/migration.js +52 -52
- package/dist/mvc/access-log.js +33 -33
- package/dist/mvc/config.js +27 -27
- package/dist/mvc/exchange.js +113 -113
- package/dist/mvc/handler/index.js +7 -6
- package/dist/mvc/handler/json.js +65 -65
- package/dist/mvc/handler/restful.js +35 -35
- package/dist/mvc/handler/sse.js +65 -0
- package/dist/mvc/handler/upload.js +31 -31
- package/dist/mvc/index.js +50 -50
- package/dist/mvc/interceptor.js +2 -2
- package/dist/mvc/query.js +43 -43
- package/dist/mvc/render/file.js +132 -132
- package/dist/mvc/render/html/html.js +90 -90
- package/dist/mvc/render/html/index.js +18 -18
- package/dist/mvc/render/html/style.js +2 -2
- package/dist/mvc/render/index.js +7 -7
- package/dist/mvc/render/json.js +26 -26
- package/dist/mvc/render/text.js +16 -16
- package/dist/mvc/router.js +2 -2
- package/dist/mvc/server.js +272 -272
- package/dist/mvc/static/header.js +67 -67
- package/dist/mvc/static/index.js +6 -6
- package/dist/mvc/static/mime-type.js +84 -84
- package/dist/mvc/static/server-cache-config.js +66 -66
- package/dist/mvc/static/server-cache.js +133 -133
- package/dist/mvc/static/static-handler.js +372 -372
- package/dist/mysql/config.js +51 -51
- package/dist/mysql/exception.js +14 -14
- package/dist/mysql/index.js +87 -87
- package/dist/mysql/manager/base.js +278 -239
- package/dist/mysql/manager/index.js +107 -107
- package/dist/mysql/manager/ops/count.js +20 -20
- package/dist/mysql/manager/ops/criteria.js +381 -356
- package/dist/mysql/manager/ops/delete.js +59 -65
- package/dist/mysql/manager/ops/exist.js +26 -26
- package/dist/mysql/manager/ops/find.js +149 -169
- package/dist/mysql/manager/ops/index.js +16 -14
- package/dist/mysql/manager/ops/insert.js +132 -106
- package/dist/mysql/manager/ops/modify.js +10 -10
- package/dist/mysql/manager/ops/order-by.js +28 -0
- package/dist/mysql/manager/ops/paginate.js +48 -23
- package/dist/mysql/manager/ops/query.js +9 -9
- package/dist/mysql/manager/ops/update.js +222 -216
- package/dist/mysql/manager/ops/upsert.js +178 -0
- package/dist/mysql/manager/ops/utils.js +28 -24
- package/dist/mysql/manager/tx-strict.js +103 -103
- package/dist/mysql/manager/tx.js +30 -30
- package/dist/mysql/manager/utils.js +56 -56
- package/dist/mysql/migration.js +136 -136
- package/dist/mysql/table-info.js +8 -8
- package/dist/task/daily.js +59 -59
- package/dist/task/fixed-delay.js +38 -38
- package/dist/task/fixed-rate.js +42 -42
- package/dist/task/index.js +9 -9
- package/dist/task/task.js +56 -56
- package/dist/validation/exception.js +36 -36
- package/dist/validation/index.js +40 -40
- package/dist/validation/validator/array.js +34 -34
- package/dist/validation/validator/enum.js +28 -28
- package/dist/validation/validator/index.js +14 -14
- package/dist/validation/validator/length.js +40 -40
- package/dist/validation/validator/max-length.js +35 -35
- package/dist/validation/validator/max.js +29 -29
- package/dist/validation/validator/min-length.js +33 -33
- package/dist/validation/validator/min.js +29 -29
- package/dist/validation/validator/not-blank.js +33 -33
- package/dist/validation/validator/not-null.js +21 -21
- package/dist/validation/validator/plain-obj.js +32 -32
- package/dist/validation/validator/regexp.js +34 -34
- package/documentation/en/cache.md +56 -0
- package/documentation/en/config.md +96 -0
- package/documentation/en/engineering.md +256 -0
- package/documentation/en/http-client.md +32 -0
- package/documentation/en/i18n.md +143 -0
- package/documentation/en/index.md +24 -0
- package/documentation/en/lock.md +51 -0
- package/documentation/en/log.md +109 -0
- package/documentation/en/mongodb.md +256 -0
- package/documentation/en/mvc.md +688 -0
- package/documentation/en/mysql.md +682 -0
- package/documentation/en/task.md +45 -0
- package/documentation/en/test.md +56 -0
- package/documentation/en/validate.md +130 -0
- package/documentation/zh-cn/mvc.md +66 -24
- package/documentation/zh-cn/mysql.md +146 -17
- package/package.json +4 -1
- package/skills/wok-server-api-rules/SKILL.md +350 -0
- package/skills/wok-server-cache/SKILL.md +216 -0
- package/skills/wok-server-code-navigation/SKILL.md +153 -0
- package/skills/wok-server-config/SKILL.md +200 -0
- package/skills/wok-server-getting-started/SKILL.md +123 -0
- package/skills/wok-server-getting-started/references/engineering.md +169 -0
- package/skills/wok-server-http-client/SKILL.md +164 -0
- package/skills/wok-server-i18n/SKILL.md +214 -0
- package/skills/wok-server-lock/SKILL.md +144 -0
- package/skills/wok-server-log/SKILL.md +218 -0
- package/skills/wok-server-mongodb/SKILL.md +235 -0
- package/skills/wok-server-mvc/SKILL.md +251 -0
- package/skills/wok-server-mvc/references/respond-html.md +157 -0
- package/skills/wok-server-mvc/references/sse.md +121 -0
- package/skills/wok-server-mvc/references/static-files.md +47 -0
- package/skills/wok-server-mvc/references/upload.md +62 -0
- package/skills/wok-server-mvc/references/websocket.md +30 -0
- package/skills/wok-server-mysql/SKILL.md +388 -0
- package/skills/wok-server-mysql/references/multi-datasource.md +76 -0
- package/skills/wok-server-mysql/references/version-control.md +22 -0
- package/skills/wok-server-task/SKILL.md +158 -0
- package/skills/wok-server-validate/SKILL.md +167 -0
- package/src/cache/cache.ts +118 -0
- package/src/cache/config.ts +53 -0
- package/src/cache/index.ts +27 -0
- package/src/cache/purge-task.ts +53 -0
- package/src/cache/stat.ts +47 -0
- package/src/config/convert.ts +27 -0
- package/src/config/exception.ts +8 -0
- package/src/config/index.ts +92 -0
- package/src/http-client/index.ts +202 -0
- package/src/i18n/ar.ts +16 -0
- package/src/i18n/de.ts +16 -0
- package/src/i18n/en-us.ts +16 -0
- package/src/i18n/es.ts +16 -0
- package/src/i18n/fr.ts +16 -0
- package/src/i18n/i18n.ts +230 -0
- package/src/i18n/index.ts +50 -0
- package/src/i18n/ja.ts +16 -0
- package/src/i18n/ko.ts +16 -0
- package/src/i18n/msg.ts +50 -0
- package/src/i18n/pt.ts +16 -0
- package/src/i18n/ru.ts +16 -0
- package/src/i18n/tag.ts +18 -0
- package/src/i18n/zh-HK.ts +16 -0
- package/src/i18n/zh-TW.ts +16 -0
- package/src/i18n/zh-cn.ts +16 -0
- package/src/index.ts +11 -0
- package/src/lock/index.ts +164 -0
- package/src/log/config.ts +71 -0
- package/src/log/date.ts +19 -0
- package/src/log/file.ts +215 -0
- package/src/log/index.ts +136 -0
- package/src/log/level.ts +29 -0
- package/src/log/log.ts +77 -0
- package/src/log/store.ts +31 -0
- package/src/mongodb/collection.ts +25 -0
- package/src/mongodb/config.ts +69 -0
- package/src/mongodb/doc.ts +12 -0
- package/src/mongodb/exception.ts +8 -0
- package/src/mongodb/index.ts +71 -0
- package/src/mongodb/manager/base.ts +674 -0
- package/src/mongodb/manager/index.ts +80 -0
- package/src/mongodb/manager/tx-strict.ts +153 -0
- package/src/mongodb/manager/tx.ts +34 -0
- package/src/mongodb/migration.ts +66 -0
- package/src/mvc/access-log.ts +33 -0
- package/src/mvc/config.ts +70 -0
- package/src/mvc/exchange.ts +126 -0
- package/src/mvc/handler/index.ts +4 -0
- package/src/mvc/handler/json.ts +96 -0
- package/src/mvc/handler/restful.ts +39 -0
- package/src/mvc/handler/sse.ts +90 -0
- package/src/mvc/handler/upload.ts +54 -0
- package/src/mvc/index.ts +48 -0
- package/src/mvc/interceptor.ts +12 -0
- package/src/mvc/query.ts +36 -0
- package/src/mvc/render/file.ts +148 -0
- package/src/mvc/render/html/html.ts +187 -0
- package/src/mvc/render/html/index.ts +16 -0
- package/src/mvc/render/html/style.ts +1201 -0
- package/src/mvc/render/index.ts +4 -0
- package/src/mvc/render/json.ts +24 -0
- package/src/mvc/render/text.ts +14 -0
- package/src/mvc/router.ts +13 -0
- package/src/mvc/server.ts +315 -0
- package/src/mvc/static/header.ts +86 -0
- package/src/mvc/static/index.ts +3 -0
- package/src/mvc/static/mime-type.ts +81 -0
- package/src/mvc/static/server-cache-config.ts +92 -0
- package/src/mvc/static/server-cache.ts +171 -0
- package/src/mvc/static/static-handler.ts +445 -0
- package/src/mysql/config.ts +130 -0
- package/src/mysql/exception.ts +8 -0
- package/src/mysql/index.ts +88 -0
- package/src/mysql/manager/base.ts +332 -0
- package/src/mysql/manager/index.ts +112 -0
- package/src/mysql/manager/ops/count.ts +30 -0
- package/src/mysql/manager/ops/criteria.ts +446 -0
- package/src/mysql/manager/ops/delete.ts +91 -0
- package/src/mysql/manager/ops/exist.ts +41 -0
- package/src/mysql/manager/ops/find.ts +209 -0
- package/src/mysql/manager/ops/index.ts +13 -0
- package/src/mysql/manager/ops/insert.ts +158 -0
- package/src/mysql/manager/ops/modify.ts +14 -0
- package/src/mysql/manager/ops/order-by.ts +58 -0
- package/src/mysql/manager/ops/paginate.ts +100 -0
- package/src/mysql/manager/ops/query.ts +13 -0
- package/src/mysql/manager/ops/update.ts +318 -0
- package/src/mysql/manager/ops/upsert.ts +224 -0
- package/src/mysql/manager/ops/utils.ts +24 -0
- package/src/mysql/manager/tx-strict.ts +138 -0
- package/src/mysql/manager/tx.ts +31 -0
- package/src/mysql/manager/utils.ts +75 -0
- package/src/mysql/migration.ts +149 -0
- package/src/mysql/table-info.ts +41 -0
- package/src/task/daily.ts +70 -0
- package/src/task/fixed-delay.ts +45 -0
- package/src/task/fixed-rate.ts +49 -0
- package/src/task/index.ts +4 -0
- package/src/task/task.ts +70 -0
- package/src/validation/exception.ts +27 -0
- package/src/validation/index.ts +61 -0
- package/src/validation/validator/array.ts +32 -0
- package/src/validation/validator/enum.ts +25 -0
- package/src/validation/validator/index.ts +11 -0
- package/src/validation/validator/length.ts +42 -0
- package/src/validation/validator/max-length.ts +33 -0
- package/src/validation/validator/max.ts +26 -0
- package/src/validation/validator/min-length.ts +31 -0
- package/src/validation/validator/min.ts +26 -0
- package/src/validation/validator/not-blank.ts +31 -0
- package/src/validation/validator/not-null.ts +19 -0
- package/src/validation/validator/plain-obj.ts +30 -0
- package/src/validation/validator/regexp.ts +32 -0
- package/types/cache/cache.d.ts +52 -52
- package/types/cache/config.d.ts +32 -32
- package/types/cache/index.d.ts +2 -2
- package/types/cache/purge-task.d.ts +11 -11
- package/types/cache/stat.d.ts +26 -26
- package/types/config/convert.d.ts +6 -6
- package/types/config/exception.d.ts +7 -7
- package/types/config/index.d.ts +25 -25
- package/types/http-client/index.d.ts +71 -71
- package/types/i18n/ar.d.ts +2 -2
- package/types/i18n/de.d.ts +2 -2
- package/types/i18n/en-us.d.ts +2 -2
- package/types/i18n/es.d.ts +2 -2
- package/types/i18n/fr.d.ts +2 -2
- package/types/i18n/i18n.d.ts +102 -102
- package/types/i18n/index.d.ts +9 -9
- package/types/i18n/ja.d.ts +2 -2
- package/types/i18n/ko.d.ts +2 -2
- package/types/i18n/msg.d.ts +50 -50
- package/types/i18n/pt.d.ts +2 -2
- package/types/i18n/ru.d.ts +2 -2
- package/types/i18n/tag.d.ts +11 -11
- package/types/i18n/zh-HK.d.ts +2 -2
- package/types/i18n/zh-TW.d.ts +2 -2
- package/types/i18n/zh-cn.d.ts +2 -2
- package/types/index.d.ts +11 -11
- package/types/lock/index.d.ts +64 -64
- package/types/log/config.d.ts +35 -35
- package/types/log/date.d.ts +2 -2
- package/types/log/file.d.ts +13 -13
- package/types/log/index.d.ts +53 -53
- package/types/log/level.d.ts +14 -14
- package/types/log/log.d.ts +40 -40
- package/types/log/store.d.ts +19 -19
- package/types/mongodb/collection.d.ts +25 -25
- package/types/mongodb/config.d.ts +45 -45
- package/types/mongodb/doc.d.ts +11 -11
- package/types/mongodb/exception.d.ts +7 -7
- package/types/mongodb/index.d.ts +29 -29
- package/types/mongodb/manager/base.d.ts +188 -188
- package/types/mongodb/manager/index.d.ts +38 -38
- package/types/mongodb/manager/tx-strict.d.ts +41 -41
- package/types/mongodb/manager/tx.d.ts +21 -21
- package/types/mongodb/migration.d.ts +12 -12
- package/types/mvc/access-log.d.ts +7 -7
- package/types/mvc/config.d.ts +42 -42
- package/types/mvc/exchange.d.ts +72 -72
- package/types/mvc/handler/index.d.ts +4 -3
- package/types/mvc/handler/json.d.ts +44 -44
- package/types/mvc/handler/restful.d.ts +11 -11
- package/types/mvc/handler/sse.d.ts +34 -0
- package/types/mvc/handler/upload.d.ts +36 -36
- package/types/mvc/index.d.ts +22 -22
- package/types/mvc/interceptor.d.ts +11 -11
- package/types/mvc/query.d.ts +13 -13
- package/types/mvc/render/file.d.ts +10 -10
- package/types/mvc/render/html/html.d.ts +98 -98
- package/types/mvc/render/html/index.d.ts +11 -11
- package/types/mvc/render/html/style.d.ts +1201 -1201
- package/types/mvc/render/index.d.ts +4 -4
- package/types/mvc/render/json.d.ts +17 -17
- package/types/mvc/render/text.d.ts +10 -10
- package/types/mvc/router.d.ts +11 -11
- package/types/mvc/server.d.ts +90 -90
- package/types/mvc/static/header.d.ts +27 -27
- package/types/mvc/static/index.d.ts +3 -3
- package/types/mvc/static/mime-type.d.ts +2 -2
- package/types/mvc/static/server-cache-config.d.ts +30 -30
- package/types/mvc/static/server-cache.d.ts +76 -76
- package/types/mvc/static/static-handler.d.ts +77 -77
- package/types/mysql/config.d.ts +90 -90
- package/types/mysql/exception.d.ts +7 -7
- package/types/mysql/index.d.ts +16 -16
- package/types/mysql/manager/base.d.ts +196 -165
- package/types/mysql/manager/index.d.ts +36 -36
- package/types/mysql/manager/ops/count.d.ts +13 -13
- package/types/mysql/manager/ops/criteria.d.ts +144 -134
- package/types/mysql/manager/ops/delete.d.ts +47 -46
- package/types/mysql/manager/ops/exist.d.ts +6 -6
- package/types/mysql/manager/ops/find.d.ts +87 -86
- package/types/mysql/manager/ops/index.d.ts +12 -10
- package/types/mysql/manager/ops/insert.d.ts +32 -18
- package/types/mysql/manager/ops/modify.d.ts +3 -3
- package/types/mysql/manager/ops/order-by.d.ts +38 -0
- package/types/mysql/manager/ops/paginate.d.ts +53 -36
- package/types/mysql/manager/ops/query.d.ts +3 -3
- package/types/mysql/manager/ops/update.d.ts +99 -76
- package/types/mysql/manager/ops/upsert.d.ts +36 -0
- package/types/mysql/manager/ops/utils.d.ts +5 -5
- package/types/mysql/manager/tx-strict.d.ts +36 -36
- package/types/mysql/manager/tx.d.ts +15 -15
- package/types/mysql/manager/utils.d.ts +17 -17
- package/types/mysql/migration.d.ts +8 -8
- package/types/mysql/table-info.d.ts +36 -36
- package/types/task/daily.d.ts +16 -16
- package/types/task/fixed-delay.d.ts +9 -9
- package/types/task/fixed-rate.d.ts +9 -9
- package/types/task/index.d.ts +4 -4
- package/types/task/task.d.ts +34 -34
- package/types/validation/exception.d.ts +38 -38
- package/types/validation/index.d.ts +32 -32
- package/types/validation/validator/array.d.ts +5 -5
- package/types/validation/validator/enum.d.ts +8 -8
- package/types/validation/validator/index.d.ts +11 -11
- package/types/validation/validator/length.d.ts +10 -10
- package/types/validation/validator/max-length.d.ts +8 -8
- package/types/validation/validator/max.d.ts +7 -7
- package/types/validation/validator/min-length.d.ts +6 -6
- package/types/validation/validator/min.d.ts +7 -7
- package/types/validation/validator/not-blank.d.ts +7 -7
- package/types/validation/validator/not-null.d.ts +6 -6
- package/types/validation/validator/plain-obj.d.ts +7 -7
- package/types/validation/validator/regexp.d.ts +8 -8
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Db, MongoClient, ReadConcernLike, ReadPreferenceLike, WriteConcern } from 'mongodb'
|
|
2
|
+
import { MongoDBConfig } from '../config'
|
|
3
|
+
import { MongoDBException } from '../exception'
|
|
4
|
+
import { BaseMongoManager } from './base'
|
|
5
|
+
import { MongoTxSession } from './tx'
|
|
6
|
+
import { MongoStrictTxSession } from './tx-strict'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* mysql 分页查询结果
|
|
10
|
+
*/
|
|
11
|
+
export interface MongoPage<T> {
|
|
12
|
+
total: number
|
|
13
|
+
list: T[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* mongodb 管理器,目前尚未实现事务的处理.
|
|
18
|
+
*/
|
|
19
|
+
export class MongoDBManager extends BaseMongoManager {
|
|
20
|
+
/**
|
|
21
|
+
* mongodb 管理器构建
|
|
22
|
+
* @param db 库
|
|
23
|
+
*/
|
|
24
|
+
constructor(config: MongoDBConfig, db: Db, private readonly client: MongoClient) {
|
|
25
|
+
super(config, db)
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 事务操作。mongodb 的版本必须在 4.0 以上,连接的必须是副本集节点或分片集群的mongos节点。
|
|
29
|
+
* @param ops 操作逻辑,所有事务相关的操作都必须使用函数提供的 session 对象
|
|
30
|
+
* @param opts mongo 事务选项
|
|
31
|
+
*/
|
|
32
|
+
async tx<T>(
|
|
33
|
+
ops: (session: MongoTxSession) => Promise<T>,
|
|
34
|
+
opts?: {
|
|
35
|
+
/** 超时时间,单位毫秒,设置后会覆盖全局设置,设置为0表示不限制 */
|
|
36
|
+
timeout?: number
|
|
37
|
+
/** A default read concern for commands in this transaction */
|
|
38
|
+
readConcern?: ReadConcernLike
|
|
39
|
+
/** A default writeConcern for commands in this transaction */
|
|
40
|
+
writeConcern?: WriteConcern
|
|
41
|
+
/** A default read preference for commands in this transaction */
|
|
42
|
+
readPreference?: ReadPreferenceLike
|
|
43
|
+
}
|
|
44
|
+
): Promise<T> {
|
|
45
|
+
const timeout =
|
|
46
|
+
opts && typeof opts.timeout === 'number' ? opts.timeout : this.config.transactionTimeout
|
|
47
|
+
const nativeSession = this.client.startSession()
|
|
48
|
+
nativeSession.startTransaction(opts)
|
|
49
|
+
const txSesssion = this.config.transactionStrict
|
|
50
|
+
? new MongoStrictTxSession(this.config, this.db, nativeSession)
|
|
51
|
+
: new MongoTxSession(this.config, this.db, nativeSession)
|
|
52
|
+
try {
|
|
53
|
+
const result =
|
|
54
|
+
timeout > 0
|
|
55
|
+
? await Promise.race([
|
|
56
|
+
ops(txSesssion),
|
|
57
|
+
new Promise<T>((resolve, reject) => {
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
// 立即中止会话,防止再有后续操作
|
|
60
|
+
txSesssion.abort()
|
|
61
|
+
reject(new MongoDBException('Transaction timeout !'))
|
|
62
|
+
}, timeout)
|
|
63
|
+
})
|
|
64
|
+
])
|
|
65
|
+
: await ops(txSesssion)
|
|
66
|
+
await nativeSession.commitTransaction()
|
|
67
|
+
return result
|
|
68
|
+
} catch (error) {
|
|
69
|
+
await nativeSession.abortTransaction()
|
|
70
|
+
// 将原错误抛出,如果有需要,调用处可对异常做进一步的处理
|
|
71
|
+
throw error
|
|
72
|
+
} finally {
|
|
73
|
+
// 无论如何中止会话,离开事务,会话就不能再被使用
|
|
74
|
+
txSesssion.abort()
|
|
75
|
+
await nativeSession.endSession()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { BaseMongoManager, MongoTxSession }
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ClientSession, Db, Document, Filter, UpdateFilter } from 'mongodb'
|
|
2
|
+
import { MongoCollection } from '../collection'
|
|
3
|
+
import { MongoDBConfig } from '../config'
|
|
4
|
+
import { MongoDocId, MongoDocWithId } from '../doc'
|
|
5
|
+
import { MongoDBException } from '../exception'
|
|
6
|
+
import { MongoPage } from './base'
|
|
7
|
+
import { MongoTxSession } from './tx'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 严格事务会话
|
|
11
|
+
*/
|
|
12
|
+
export class MongoStrictTxSession extends MongoTxSession {
|
|
13
|
+
#opsCount = 0
|
|
14
|
+
constructor(config: MongoDBConfig, db: Db, session: ClientSession) {
|
|
15
|
+
super(config, db, session)
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 为操作计数,检查是否操作次数过多.
|
|
19
|
+
*/
|
|
20
|
+
#checkAndAddOpsCount() {
|
|
21
|
+
if (this.#opsCount >= 10) {
|
|
22
|
+
throw new MongoDBException('Too many operations in a strict transaction.')
|
|
23
|
+
}
|
|
24
|
+
this.#opsCount++
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
findById<T extends Document>(
|
|
28
|
+
coll: MongoCollection<T>,
|
|
29
|
+
id: MongoDocId
|
|
30
|
+
): Promise<MongoDocWithId<T> | null> {
|
|
31
|
+
this.#checkAndAddOpsCount()
|
|
32
|
+
return super.findById(coll, id)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
findByIdIn<T extends Document>(
|
|
36
|
+
coll: MongoCollection<T>,
|
|
37
|
+
ids: MongoDocId[]
|
|
38
|
+
): Promise<MongoDocWithId<T>[]> {
|
|
39
|
+
if (ids.length > 100) {
|
|
40
|
+
throw new MongoDBException(
|
|
41
|
+
`The augument ids length(${ids.length}) passed to findByIdIn in a strict transaction is too large .`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
this.#checkAndAddOpsCount()
|
|
45
|
+
return super.findByIdIn(coll, ids)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
existsBy<T extends Document>(
|
|
49
|
+
coll: MongoCollection<T>,
|
|
50
|
+
filter: Filter<MongoDocWithId<T>>
|
|
51
|
+
): Promise<boolean> {
|
|
52
|
+
this.#checkAndAddOpsCount()
|
|
53
|
+
return super.existsBy(coll, filter)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
deleteById<T extends Document>(coll: MongoCollection<T>, id: MongoDocId): Promise<boolean> {
|
|
57
|
+
this.#checkAndAddOpsCount()
|
|
58
|
+
return super.deleteById(coll, id)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
deleteOne<T extends Document>(coll: MongoCollection<T>, filter: Partial<T>): Promise<boolean> {
|
|
62
|
+
this.#checkAndAddOpsCount()
|
|
63
|
+
return super.deleteOne(coll, filter)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
deleteMany<T extends Document>(
|
|
67
|
+
coll: MongoCollection<T>,
|
|
68
|
+
filter: Filter<MongoDocWithId<T>>
|
|
69
|
+
): Promise<number> {
|
|
70
|
+
throw new MongoDBException('Prohibited to use deleteMany in a strict transaction.')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
findAll<T extends Document>(coll: MongoCollection<T>): Promise<MongoDocWithId<T>[]> {
|
|
74
|
+
throw new MongoDBException('Prohibited to use findAll in a strict transaction.')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
findFirst<T extends Document>(
|
|
78
|
+
coll: MongoCollection<T>,
|
|
79
|
+
filter: Filter<MongoDocWithId<T>>
|
|
80
|
+
): Promise<MongoDocWithId<T> | null> {
|
|
81
|
+
this.#checkAndAddOpsCount()
|
|
82
|
+
return super.findFirst(coll, filter)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
insert<T extends Document>(
|
|
86
|
+
coll: MongoCollection<T>,
|
|
87
|
+
data: T & { _id?: MongoDocId | undefined }
|
|
88
|
+
): Promise<MongoDocWithId<T>> {
|
|
89
|
+
this.#checkAndAddOpsCount()
|
|
90
|
+
return super.insert(coll, data)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
insertMany<T extends Document>(
|
|
94
|
+
coll: MongoCollection<T>,
|
|
95
|
+
list: (T & { _id?: MongoDocId | undefined })[]
|
|
96
|
+
): Promise<MongoDocWithId<T>[]> {
|
|
97
|
+
throw new MongoDBException('Prohibited to use insertMany in a strict transaction.')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
update<T extends Document>(
|
|
101
|
+
coll: MongoCollection<T>,
|
|
102
|
+
data: MongoDocWithId<T>
|
|
103
|
+
): Promise<MongoDocWithId<T>> {
|
|
104
|
+
this.#checkAndAddOpsCount()
|
|
105
|
+
return super.update(coll, data)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
partialUpdate<T extends Document>(
|
|
109
|
+
coll: MongoCollection<T>,
|
|
110
|
+
id: MongoDocId,
|
|
111
|
+
updator: UpdateFilter<T>
|
|
112
|
+
): Promise<boolean> {
|
|
113
|
+
this.#checkAndAddOpsCount()
|
|
114
|
+
return super.partialUpdate(coll, id, updator)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
updateMany<T extends Document>(
|
|
118
|
+
coll: MongoCollection<T>,
|
|
119
|
+
filter: Filter<MongoDocWithId<T>>,
|
|
120
|
+
updator: UpdateFilter<T>
|
|
121
|
+
): Promise<number> {
|
|
122
|
+
throw new MongoDBException('Prohibited to use updateMany in a strict transaction.')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
find<T extends Document>(
|
|
126
|
+
coll: MongoCollection<T>,
|
|
127
|
+
filter: Filter<MongoDocWithId<T>>,
|
|
128
|
+
opts?:
|
|
129
|
+
| {
|
|
130
|
+
offset?: number | undefined
|
|
131
|
+
limit?: number | undefined
|
|
132
|
+
orderBy?: ['_id' | keyof T, 'asc' | 'desc'][] | undefined
|
|
133
|
+
}
|
|
134
|
+
| undefined
|
|
135
|
+
): Promise<MongoDocWithId<T>[]> {
|
|
136
|
+
throw new MongoDBException('Prohibited to use find in a strict transaction.')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
count<T extends Document>(
|
|
140
|
+
coll: MongoCollection<T>,
|
|
141
|
+
filter: Filter<MongoDocWithId<T>>
|
|
142
|
+
): Promise<number> {
|
|
143
|
+
throw new MongoDBException('Prohibited to use count in a strict transaction.')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
paginate<T extends Document>(
|
|
147
|
+
coll: MongoCollection<T>,
|
|
148
|
+
filter: Filter<MongoDocWithId<T>>,
|
|
149
|
+
opts: { pn: number; pz: number; orderBy?: ['_id' | keyof T, 'asc' | 'desc'][] | undefined }
|
|
150
|
+
): Promise<MongoPage<MongoDocWithId<T>>> {
|
|
151
|
+
throw new MongoDBException('Prohibited to use paginate in a strict transaction.')
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ClientSession, Db } from 'mongodb'
|
|
2
|
+
import { MongoDBConfig } from '../config'
|
|
3
|
+
import { MongoDBException } from '../exception'
|
|
4
|
+
import { BaseMongoManager } from './base'
|
|
5
|
+
/**
|
|
6
|
+
* 事务会话
|
|
7
|
+
*/
|
|
8
|
+
export class MongoTxSession extends BaseMongoManager {
|
|
9
|
+
/**
|
|
10
|
+
* 中止标识
|
|
11
|
+
*/
|
|
12
|
+
#aborted = false
|
|
13
|
+
|
|
14
|
+
constructor(config: MongoDBConfig, db: Db, session: ClientSession) {
|
|
15
|
+
super(config, db, session)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected timingQuery<T, D extends { op: string; coll: string }>(opts: {
|
|
19
|
+
query: () => Promise<T>
|
|
20
|
+
desc: () => D
|
|
21
|
+
}): Promise<T> {
|
|
22
|
+
if (this.#aborted) {
|
|
23
|
+
throw new MongoDBException('Session has been aborted!')
|
|
24
|
+
}
|
|
25
|
+
return super.timingQuery(opts)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 中止,被中止后的会话不能再进行任何操作
|
|
30
|
+
*/
|
|
31
|
+
abort() {
|
|
32
|
+
this.#aborted = true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Db } from 'mongodb'
|
|
2
|
+
import { MongoDBException } from './exception'
|
|
3
|
+
import { getLogger } from '../log'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 迁移版本,是一个函数,可以在函数的内部通过 mongodb 驱动提升的 api 来完成操作。
|
|
7
|
+
* 注:驱动里有些 api 是没有的,和 mongodb 的脚本有区分,但是有些是可以通过 db.command() 来替代的。
|
|
8
|
+
*/
|
|
9
|
+
export type MongoMigrationVersion = (db: Db) => Promise<void>
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 迁移
|
|
13
|
+
* @param db
|
|
14
|
+
* @param versionList
|
|
15
|
+
*/
|
|
16
|
+
export async function migrate(db: Db, versionList: MongoMigrationVersion[]) {
|
|
17
|
+
let currentVersion = await getCurrentVersion(db)
|
|
18
|
+
// 逐个执行,迁移执行的逻辑是无法提供事务支持的
|
|
19
|
+
// 版本管理代码是自定义的,无法做到强制绑定 session
|
|
20
|
+
// 一旦出错,只能手动处理数据库,然后再重新执行程序,和 mysql 一样
|
|
21
|
+
for (let idx = 0; idx < versionList.length; idx++) {
|
|
22
|
+
if (idx <= currentVersion) {
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
const migrationVersion = versionList[idx]
|
|
26
|
+
getLogger().info(`MongoDB migrating, version: ${idx}`)
|
|
27
|
+
await migrationVersion(db)
|
|
28
|
+
await updateVersion(db, idx)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 版本管理集合.
|
|
34
|
+
*/
|
|
35
|
+
interface VersionColl {
|
|
36
|
+
_id: string
|
|
37
|
+
version: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const VERSION_COLLECTION_NAME = 'db_version'
|
|
41
|
+
const VERSION_RECORD_ID = 'db_version'
|
|
42
|
+
|
|
43
|
+
async function getCurrentVersion(db: Db): Promise<number> {
|
|
44
|
+
const res = await db
|
|
45
|
+
.collection<VersionColl>(VERSION_COLLECTION_NAME)
|
|
46
|
+
.findOne({ _id: VERSION_RECORD_ID })
|
|
47
|
+
return res ? res.version : -1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 更新版本号
|
|
52
|
+
* @param db
|
|
53
|
+
* @param version
|
|
54
|
+
*/
|
|
55
|
+
async function updateVersion(db: Db, version: number) {
|
|
56
|
+
const collection = db.collection<VersionColl>(VERSION_COLLECTION_NAME)
|
|
57
|
+
const res = await collection.findOne({ _id: VERSION_RECORD_ID })
|
|
58
|
+
if (res) {
|
|
59
|
+
const rs = await collection.updateOne({ _id: VERSION_RECORD_ID }, { $set: { version } })
|
|
60
|
+
if (rs.modifiedCount !== 1) {
|
|
61
|
+
throw new MongoDBException('Failed to update version.')
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
await collection.insertOne({ _id: VERSION_RECORD_ID, version: version })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getLogger } from '../log'
|
|
2
|
+
import { formatDateTime } from '../log/date'
|
|
3
|
+
import { Interceptor } from './interceptor'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 访问日志拦截器,记录请求信息
|
|
7
|
+
* @param exchange
|
|
8
|
+
* @param next
|
|
9
|
+
*/
|
|
10
|
+
export const accessLogInterceptor: Interceptor = async (exchange, next) => {
|
|
11
|
+
const start = new Date().getTime()
|
|
12
|
+
exchange.response.once('close', () => {
|
|
13
|
+
const userAgent = exchange.request.headers['user-agent']
|
|
14
|
+
const referer = exchange.request.headers['referer']
|
|
15
|
+
const ip = exchange.request.socket.remoteAddress
|
|
16
|
+
const { url, method } = exchange.request
|
|
17
|
+
const status = exchange.response.statusCode
|
|
18
|
+
const rt = new Date().getTime() - start
|
|
19
|
+
getLogger().info(
|
|
20
|
+
`[access-log]${JSON.stringify({
|
|
21
|
+
method,
|
|
22
|
+
url,
|
|
23
|
+
ip,
|
|
24
|
+
userAgent,
|
|
25
|
+
referer,
|
|
26
|
+
start: formatDateTime(new Date(start)),
|
|
27
|
+
rt,
|
|
28
|
+
status
|
|
29
|
+
})}`
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
await next()
|
|
33
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { generateConfig } from '../config'
|
|
2
|
+
import { max, min, notBlank, notNull } from '../validation'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* mvc 模块配置.
|
|
6
|
+
*/
|
|
7
|
+
export interface WebConfig {
|
|
8
|
+
/**
|
|
9
|
+
* 端口号
|
|
10
|
+
*/
|
|
11
|
+
port: number
|
|
12
|
+
/**
|
|
13
|
+
* 超时时间
|
|
14
|
+
*/
|
|
15
|
+
timeout: number
|
|
16
|
+
/**
|
|
17
|
+
* 是否开启访问日志.
|
|
18
|
+
*/
|
|
19
|
+
accessLog: boolean
|
|
20
|
+
/**
|
|
21
|
+
* 跨域允许的源域名
|
|
22
|
+
*/
|
|
23
|
+
corsAllowOrigin: string
|
|
24
|
+
/**
|
|
25
|
+
* 跨域允许的消息头
|
|
26
|
+
*/
|
|
27
|
+
corsAllowHeaders: string
|
|
28
|
+
/**
|
|
29
|
+
* 跨域允许的请求方法
|
|
30
|
+
*/
|
|
31
|
+
corsAllowMethods: string
|
|
32
|
+
/**
|
|
33
|
+
* 是否激活安全传输层
|
|
34
|
+
*/
|
|
35
|
+
tlsEnable: boolean
|
|
36
|
+
/**
|
|
37
|
+
* pem 格式证书公钥文件路径
|
|
38
|
+
*/
|
|
39
|
+
tlsCert: string
|
|
40
|
+
/**
|
|
41
|
+
* pem 格式证书私钥文件路径
|
|
42
|
+
*/
|
|
43
|
+
tlsKey: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getConfig() {
|
|
47
|
+
return generateConfig<WebConfig>(
|
|
48
|
+
{
|
|
49
|
+
port: 8080,
|
|
50
|
+
timeout: 30000,
|
|
51
|
+
accessLog: false,
|
|
52
|
+
corsAllowHeaders: '*',
|
|
53
|
+
corsAllowMethods: '*',
|
|
54
|
+
corsAllowOrigin: '*',
|
|
55
|
+
tlsEnable: false,
|
|
56
|
+
tlsKey: '',
|
|
57
|
+
tlsCert: ''
|
|
58
|
+
},
|
|
59
|
+
'SERVER',
|
|
60
|
+
{
|
|
61
|
+
port: [notNull(), min(80), max(65535)],
|
|
62
|
+
timeout: [notNull(), min(1000), max(600000)],
|
|
63
|
+
accessLog: [notNull()],
|
|
64
|
+
corsAllowOrigin: [notBlank()],
|
|
65
|
+
corsAllowHeaders: [notBlank()],
|
|
66
|
+
corsAllowMethods: [notBlank()],
|
|
67
|
+
tlsEnable: [notNull()]
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'http'
|
|
2
|
+
import { QueryString } from './query'
|
|
3
|
+
import { HtmlStuct, renderError, renderFile, renderHtml, renderJson } from './render'
|
|
4
|
+
import { renderText } from './render/text'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 服务的数据交换对象.
|
|
8
|
+
*/
|
|
9
|
+
export class ServerExchange {
|
|
10
|
+
#bufferPromise?: Promise<Buffer>
|
|
11
|
+
|
|
12
|
+
constructor(readonly request: IncomingMessage, readonly response: ServerResponse) {}
|
|
13
|
+
|
|
14
|
+
bodyBuffer(): Promise<Buffer> {
|
|
15
|
+
if (this.#bufferPromise) {
|
|
16
|
+
return this.#bufferPromise
|
|
17
|
+
}
|
|
18
|
+
this.#bufferPromise = new Promise<Buffer>((resolve, reject) => {
|
|
19
|
+
if (this.request.readableEnded) {
|
|
20
|
+
throw new Error('Request has ended!')
|
|
21
|
+
}
|
|
22
|
+
let body: Buffer[] = []
|
|
23
|
+
this.request
|
|
24
|
+
.resume()
|
|
25
|
+
.on('error', reject)
|
|
26
|
+
.on('data', chunk => body.push(chunk))
|
|
27
|
+
.on('end', () => resolve(Buffer.concat(body)))
|
|
28
|
+
})
|
|
29
|
+
return this.#bufferPromise
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async bodyText(): Promise<string> {
|
|
33
|
+
const buffer = await this.bodyBuffer()
|
|
34
|
+
return buffer.toString('utf-8')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async bodyJson<T>(): Promise<T> {
|
|
38
|
+
const buffer = await this.bodyBuffer()
|
|
39
|
+
const bodyText = buffer.toString('utf-8')
|
|
40
|
+
if (!bodyText || !bodyText.trim()) {
|
|
41
|
+
return {} as any
|
|
42
|
+
}
|
|
43
|
+
return JSON.parse(bodyText)
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 响应纯文本
|
|
47
|
+
* @param text 文本内容
|
|
48
|
+
* @param status 状态码,可选,默认 200
|
|
49
|
+
*/
|
|
50
|
+
respondText(text: string, status?: number) {
|
|
51
|
+
renderText(this.response, text, status)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 响应 json
|
|
56
|
+
* @param json 任意可被 json 序列化的对象
|
|
57
|
+
* @param status 状态码,可选,默认 200
|
|
58
|
+
*/
|
|
59
|
+
respondJson(json: any, status?: number) {
|
|
60
|
+
renderJson(this.response, json, status)
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 响应错误信息提示,提供一个统一的格式封装,json 格式
|
|
64
|
+
* @param message 消息
|
|
65
|
+
* @param status 状态码,默认 400 ,表示业务错误
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
respondErrMsg(message: string, status?: number) {
|
|
69
|
+
renderError(this.response, message, status)
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 响应文件
|
|
73
|
+
* @param filePath 文件路径,绝对路径
|
|
74
|
+
* @param download 是否下载模式
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
respondFile(filePath: string, download?: boolean) {
|
|
78
|
+
return renderFile(this.request, this.response, filePath, download)
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 响应 html
|
|
82
|
+
* @param html html 内容,一个特定结构的对象或者是字符串
|
|
83
|
+
* @param status 状态码,可选,默认 200
|
|
84
|
+
*/
|
|
85
|
+
respondHtml(html: HtmlStuct | string, status?: number) {
|
|
86
|
+
renderHtml(this.response, html, status)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 响应
|
|
91
|
+
* @param opts
|
|
92
|
+
*/
|
|
93
|
+
respond(opts: {
|
|
94
|
+
/**
|
|
95
|
+
* 状态码.
|
|
96
|
+
*/
|
|
97
|
+
statusCode: number
|
|
98
|
+
/**
|
|
99
|
+
* 响应正文内容.
|
|
100
|
+
*/
|
|
101
|
+
body?: string | Buffer | Uint8Array
|
|
102
|
+
/**
|
|
103
|
+
* 自定义消息头
|
|
104
|
+
*/
|
|
105
|
+
headers?: Record<string, string>
|
|
106
|
+
}) {
|
|
107
|
+
this.response.statusCode = opts.statusCode
|
|
108
|
+
if (opts.headers) {
|
|
109
|
+
for (const key in opts.headers) {
|
|
110
|
+
this.response.setHeader(key, opts.headers[key])
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (opts.body) {
|
|
114
|
+
this.response.write(opts.body)
|
|
115
|
+
}
|
|
116
|
+
this.response.end()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 解析 queryString
|
|
121
|
+
* @returns
|
|
122
|
+
*/
|
|
123
|
+
parseQueryString() {
|
|
124
|
+
return new QueryString(this.request.url || '')
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { IncomingMessage } from 'http'
|
|
2
|
+
import { getCache } from '../../cache'
|
|
3
|
+
import { getI18n } from '../../i18n'
|
|
4
|
+
import { validate, ValidationOpts } from '../../validation'
|
|
5
|
+
import { ServerExchange } from '../exchange'
|
|
6
|
+
import { RouterHandler } from '../router'
|
|
7
|
+
|
|
8
|
+
export interface JsonHandlerExchange {
|
|
9
|
+
/**
|
|
10
|
+
* 请求信息
|
|
11
|
+
*/
|
|
12
|
+
request: IncomingMessage
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 创建 json 处理器..
|
|
17
|
+
* @param <REQ> 表示请求的 json 数据格式类型
|
|
18
|
+
* @param <RES> 表示响应的类型,可选,如果不需要响应 json 数据,则方法可以不返回任何值
|
|
19
|
+
* @param opts
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
export function createJsonHandler<REQ, RES = void>(opts: {
|
|
23
|
+
/**
|
|
24
|
+
* 缓存设置,通过框架的 cache 模块来缓存响应信息,只能缓存有效的响应信息,如果没有响应正文则不会进行缓存,
|
|
25
|
+
* 参数和 handle 是一样的
|
|
26
|
+
* @param body
|
|
27
|
+
* @param exchange
|
|
28
|
+
* @returns 返回缓存的 key 和缓存秒数
|
|
29
|
+
*/
|
|
30
|
+
cache?: (
|
|
31
|
+
body: REQ,
|
|
32
|
+
exchange: JsonHandlerExchange
|
|
33
|
+
) =>
|
|
34
|
+
| Promise<{ key: string; expiresInSeconds?: number }>
|
|
35
|
+
| { key: string; expiresInSeconds?: number }
|
|
36
|
+
/**
|
|
37
|
+
* 校验信息,可选,用于检查请求信息.对于一些特殊情况,无法使用校验器的,可以在 handle 中继续处理.
|
|
38
|
+
*/
|
|
39
|
+
validation?: ValidationOpts<REQ> | (() => ValidationOpts<REQ>)
|
|
40
|
+
/**
|
|
41
|
+
* 处理请求.
|
|
42
|
+
* @param body 正文内容
|
|
43
|
+
* @param exchange 请求传输对象,用于获取请求的基本信息
|
|
44
|
+
* @returns
|
|
45
|
+
*/
|
|
46
|
+
handle: (body: REQ, exchange: JsonHandlerExchange) => Promise<RES>
|
|
47
|
+
}): RouterHandler {
|
|
48
|
+
return async function (exchange: ServerExchange) {
|
|
49
|
+
if (!exchange.request.method || exchange.request.method.toUpperCase() !== 'POST') {
|
|
50
|
+
exchange.respondErrMsg('Method Not Allowed', 405)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const body: REQ = await exchange.bodyJson()
|
|
54
|
+
if (opts.validation) {
|
|
55
|
+
// 切换语言
|
|
56
|
+
getI18n().switchByRequest(exchange.request.headers)
|
|
57
|
+
if (typeof opts.validation === 'function') {
|
|
58
|
+
validate(body, opts.validation())
|
|
59
|
+
} else {
|
|
60
|
+
validate(body, opts.validation)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 缓存处理
|
|
64
|
+
if (opts.cache) {
|
|
65
|
+
const cacheInfo = await opts.cache(body, exchange)
|
|
66
|
+
const buffer = getCache().get(cacheInfo.key)
|
|
67
|
+
if (buffer && buffer instanceof Buffer) {
|
|
68
|
+
renderJsonBuffer(exchange, buffer)
|
|
69
|
+
} else {
|
|
70
|
+
const res = await opts.handle(body, { request: exchange.request })
|
|
71
|
+
if (!res) {
|
|
72
|
+
// 无结果不缓存
|
|
73
|
+
exchange.respond({ statusCode: 200 })
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
const buffer = Buffer.from(JSON.stringify(res), 'utf-8')
|
|
77
|
+
getCache().put(cacheInfo.key, buffer, cacheInfo.expiresInSeconds)
|
|
78
|
+
renderJsonBuffer(exchange, buffer)
|
|
79
|
+
}
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
const res = await opts.handle(body, { request: exchange.request })
|
|
83
|
+
if (!res) {
|
|
84
|
+
exchange.respond({ statusCode: 200 })
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
exchange.respondJson(res)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderJsonBuffer(exchange: ServerExchange, buffer: Buffer) {
|
|
92
|
+
const { response } = exchange
|
|
93
|
+
response.setHeader('content-type', 'application/json; charset=UTF-8')
|
|
94
|
+
response.statusCode = 200
|
|
95
|
+
response.end(buffer)
|
|
96
|
+
}
|