wok-server 0.1.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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/cache/cache.js +94 -0
  4. package/dist/cache/config.js +19 -0
  5. package/dist/cache/index.js +27 -0
  6. package/dist/cache/purge-task.js +56 -0
  7. package/dist/cache/stat.js +47 -0
  8. package/dist/config/convert.js +36 -0
  9. package/dist/config/exception.js +14 -0
  10. package/dist/config/index.js +67 -0
  11. package/dist/http-client/index.js +132 -0
  12. package/dist/i18n/ar.js +17 -0
  13. package/dist/i18n/de.js +17 -0
  14. package/dist/i18n/en-us.js +17 -0
  15. package/dist/i18n/es.js +17 -0
  16. package/dist/i18n/fr.js +17 -0
  17. package/dist/i18n/i18n.js +231 -0
  18. package/dist/i18n/index.js +52 -0
  19. package/dist/i18n/ja.js +17 -0
  20. package/dist/i18n/ko.js +17 -0
  21. package/dist/i18n/msg.js +2 -0
  22. package/dist/i18n/pt.js +17 -0
  23. package/dist/i18n/ru.js +17 -0
  24. package/dist/i18n/tag.js +18 -0
  25. package/dist/i18n/zh-HK.js +17 -0
  26. package/dist/i18n/zh-TW.js +17 -0
  27. package/dist/i18n/zh-cn.js +17 -0
  28. package/dist/index.js +13 -0
  29. package/dist/log/config.js +28 -0
  30. package/dist/log/date.js +21 -0
  31. package/dist/log/file.js +79 -0
  32. package/dist/log/index.js +109 -0
  33. package/dist/log/level.js +39 -0
  34. package/dist/log/store.js +16 -0
  35. package/dist/mongodb/collection.js +2 -0
  36. package/dist/mongodb/config.js +34 -0
  37. package/dist/mongodb/doc.js +2 -0
  38. package/dist/mongodb/exception.js +14 -0
  39. package/dist/mongodb/index.js +58 -0
  40. package/dist/mongodb/manager/base.js +563 -0
  41. package/dist/mongodb/manager/index.js +63 -0
  42. package/dist/mongodb/manager/tx-strict.js +84 -0
  43. package/dist/mongodb/manager/tx.js +30 -0
  44. package/dist/mongodb/migration.js +52 -0
  45. package/dist/mvc/access-log.js +31 -0
  46. package/dist/mvc/config.js +20 -0
  47. package/dist/mvc/exchange.js +113 -0
  48. package/dist/mvc/handler/index.js +6 -0
  49. package/dist/mvc/handler/json.js +33 -0
  50. package/dist/mvc/handler/restful.js +35 -0
  51. package/dist/mvc/handler/upload.js +33 -0
  52. package/dist/mvc/index.js +316 -0
  53. package/dist/mvc/interceptor.js +2 -0
  54. package/dist/mvc/query.js +43 -0
  55. package/dist/mvc/render/file.js +177 -0
  56. package/dist/mvc/render/html/html.js +90 -0
  57. package/dist/mvc/render/html/index.js +18 -0
  58. package/dist/mvc/render/html/style.js +2 -0
  59. package/dist/mvc/render/index.js +7 -0
  60. package/dist/mvc/render/json.js +26 -0
  61. package/dist/mvc/render/text.js +16 -0
  62. package/dist/mvc/router.js +2 -0
  63. package/dist/mysql/config.js +49 -0
  64. package/dist/mysql/exception.js +14 -0
  65. package/dist/mysql/index.js +85 -0
  66. package/dist/mysql/manager/base.js +233 -0
  67. package/dist/mysql/manager/index.js +107 -0
  68. package/dist/mysql/manager/ops/count.js +20 -0
  69. package/dist/mysql/manager/ops/criteria.js +326 -0
  70. package/dist/mysql/manager/ops/delete.js +65 -0
  71. package/dist/mysql/manager/ops/exist.js +26 -0
  72. package/dist/mysql/manager/ops/find.js +111 -0
  73. package/dist/mysql/manager/ops/index.js +14 -0
  74. package/dist/mysql/manager/ops/insert.js +101 -0
  75. package/dist/mysql/manager/ops/modify.js +10 -0
  76. package/dist/mysql/manager/ops/paginate.js +23 -0
  77. package/dist/mysql/manager/ops/query.js +9 -0
  78. package/dist/mysql/manager/ops/update.js +201 -0
  79. package/dist/mysql/manager/tx-strict.js +98 -0
  80. package/dist/mysql/manager/tx.js +30 -0
  81. package/dist/mysql/manager/utils.js +56 -0
  82. package/dist/mysql/migration.js +136 -0
  83. package/dist/mysql/table-info.js +8 -0
  84. package/dist/task/daily.js +58 -0
  85. package/dist/task/fixed-delay.js +33 -0
  86. package/dist/task/fixed-rate.js +37 -0
  87. package/dist/task/index.js +9 -0
  88. package/dist/task/task.js +39 -0
  89. package/dist/validation/exception.js +44 -0
  90. package/dist/validation/index.js +29 -0
  91. package/dist/validation/validator/array.js +38 -0
  92. package/dist/validation/validator/enum.js +28 -0
  93. package/dist/validation/validator/index.js +14 -0
  94. package/dist/validation/validator/length.js +40 -0
  95. package/dist/validation/validator/max-length.js +35 -0
  96. package/dist/validation/validator/max.js +29 -0
  97. package/dist/validation/validator/min-length.js +33 -0
  98. package/dist/validation/validator/min.js +29 -0
  99. package/dist/validation/validator/not-blank.js +33 -0
  100. package/dist/validation/validator/not-null.js +21 -0
  101. package/dist/validation/validator/plain-obj.js +32 -0
  102. package/dist/validation/validator/regexp.js +30 -0
  103. package/documentation/en/index.md +1 -0
  104. package/documentation/zh-cn/cache.md +59 -0
  105. package/documentation/zh-cn/config.md +68 -0
  106. package/documentation/zh-cn/http-client.md +33 -0
  107. package/documentation/zh-cn/i18n.md +154 -0
  108. package/documentation/zh-cn/index.md +25 -0
  109. package/documentation/zh-cn/log.md +40 -0
  110. package/documentation/zh-cn/mongodb.md +262 -0
  111. package/documentation/zh-cn/mvc.md +430 -0
  112. package/documentation/zh-cn/mysql.md +389 -0
  113. package/documentation/zh-cn/task.md +50 -0
  114. package/documentation/zh-cn/test.md +57 -0
  115. package/documentation/zh-cn/validate.md +125 -0
  116. package/package.json +46 -0
  117. package/types/cache/cache.d.ts +52 -0
  118. package/types/cache/config.d.ts +32 -0
  119. package/types/cache/index.d.ts +2 -0
  120. package/types/cache/purge-task.d.ts +11 -0
  121. package/types/cache/stat.d.ts +26 -0
  122. package/types/config/convert.d.ts +6 -0
  123. package/types/config/exception.d.ts +7 -0
  124. package/types/config/index.d.ts +15 -0
  125. package/types/http-client/index.d.ts +71 -0
  126. package/types/i18n/ar.d.ts +2 -0
  127. package/types/i18n/de.d.ts +2 -0
  128. package/types/i18n/en-us.d.ts +2 -0
  129. package/types/i18n/es.d.ts +2 -0
  130. package/types/i18n/fr.d.ts +2 -0
  131. package/types/i18n/i18n.d.ts +102 -0
  132. package/types/i18n/index.d.ts +9 -0
  133. package/types/i18n/ja.d.ts +2 -0
  134. package/types/i18n/ko.d.ts +2 -0
  135. package/types/i18n/msg.d.ts +50 -0
  136. package/types/i18n/pt.d.ts +2 -0
  137. package/types/i18n/ru.d.ts +2 -0
  138. package/types/i18n/tag.d.ts +11 -0
  139. package/types/i18n/zh-HK.d.ts +2 -0
  140. package/types/i18n/zh-TW.d.ts +2 -0
  141. package/types/i18n/zh-cn.d.ts +2 -0
  142. package/types/index.d.ts +10 -0
  143. package/types/log/config.d.ts +27 -0
  144. package/types/log/date.d.ts +2 -0
  145. package/types/log/file.d.ts +5 -0
  146. package/types/log/index.d.ts +34 -0
  147. package/types/log/level.d.ts +15 -0
  148. package/types/log/store.d.ts +12 -0
  149. package/types/mongodb/collection.d.ts +25 -0
  150. package/types/mongodb/config.d.ts +45 -0
  151. package/types/mongodb/doc.d.ts +11 -0
  152. package/types/mongodb/exception.d.ts +7 -0
  153. package/types/mongodb/index.d.ts +29 -0
  154. package/types/mongodb/manager/base.d.ts +188 -0
  155. package/types/mongodb/manager/index.d.ts +38 -0
  156. package/types/mongodb/manager/tx-strict.d.ts +41 -0
  157. package/types/mongodb/manager/tx.d.ts +21 -0
  158. package/types/mongodb/migration.d.ts +12 -0
  159. package/types/mvc/access-log.d.ts +7 -0
  160. package/types/mvc/config.d.ts +30 -0
  161. package/types/mvc/exchange.d.ts +72 -0
  162. package/types/mvc/handler/index.d.ts +3 -0
  163. package/types/mvc/handler/json.d.ts +23 -0
  164. package/types/mvc/handler/restful.d.ts +11 -0
  165. package/types/mvc/handler/upload.d.ts +40 -0
  166. package/types/mvc/index.d.ts +49 -0
  167. package/types/mvc/interceptor.d.ts +11 -0
  168. package/types/mvc/query.d.ts +13 -0
  169. package/types/mvc/render/file.d.ts +10 -0
  170. package/types/mvc/render/html/html.d.ts +98 -0
  171. package/types/mvc/render/html/index.d.ts +11 -0
  172. package/types/mvc/render/html/style.d.ts +1201 -0
  173. package/types/mvc/render/index.d.ts +4 -0
  174. package/types/mvc/render/json.d.ts +17 -0
  175. package/types/mvc/render/text.d.ts +10 -0
  176. package/types/mvc/router.d.ts +11 -0
  177. package/types/mysql/config.d.ts +86 -0
  178. package/types/mysql/exception.d.ts +7 -0
  179. package/types/mysql/index.d.ts +16 -0
  180. package/types/mysql/manager/base.d.ts +158 -0
  181. package/types/mysql/manager/index.d.ts +36 -0
  182. package/types/mysql/manager/ops/count.d.ts +13 -0
  183. package/types/mysql/manager/ops/criteria.d.ts +120 -0
  184. package/types/mysql/manager/ops/delete.d.ts +46 -0
  185. package/types/mysql/manager/ops/exist.d.ts +6 -0
  186. package/types/mysql/manager/ops/find.d.ts +66 -0
  187. package/types/mysql/manager/ops/index.d.ts +10 -0
  188. package/types/mysql/manager/ops/insert.d.ts +18 -0
  189. package/types/mysql/manager/ops/modify.d.ts +3 -0
  190. package/types/mysql/manager/ops/paginate.d.ts +36 -0
  191. package/types/mysql/manager/ops/query.d.ts +3 -0
  192. package/types/mysql/manager/ops/update.d.ts +70 -0
  193. package/types/mysql/manager/tx-strict.d.ts +34 -0
  194. package/types/mysql/manager/tx.d.ts +15 -0
  195. package/types/mysql/manager/utils.d.ts +17 -0
  196. package/types/mysql/migration.d.ts +8 -0
  197. package/types/mysql/table-info.d.ts +36 -0
  198. package/types/task/daily.d.ts +15 -0
  199. package/types/task/fixed-delay.d.ts +8 -0
  200. package/types/task/fixed-rate.d.ts +8 -0
  201. package/types/task/index.d.ts +4 -0
  202. package/types/task/task.d.ts +33 -0
  203. package/types/validation/exception.d.ts +43 -0
  204. package/types/validation/index.d.ts +32 -0
  205. package/types/validation/validator/array.d.ts +5 -0
  206. package/types/validation/validator/enum.d.ts +8 -0
  207. package/types/validation/validator/index.d.ts +11 -0
  208. package/types/validation/validator/length.d.ts +10 -0
  209. package/types/validation/validator/max-length.d.ts +8 -0
  210. package/types/validation/validator/max.d.ts +7 -0
  211. package/types/validation/validator/min-length.d.ts +6 -0
  212. package/types/validation/validator/min.d.ts +7 -0
  213. package/types/validation/validator/not-blank.d.ts +7 -0
  214. package/types/validation/validator/not-null.d.ts +6 -0
  215. package/types/validation/validator/plain-obj.d.ts +7 -0
  216. package/types/validation/validator/regexp.d.ts +8 -0
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MongoTxSession = void 0;
4
+ const exception_1 = require("../exception");
5
+ const base_1 = require("./base");
6
+ /**
7
+ * 事务会话
8
+ */
9
+ class MongoTxSession extends base_1.BaseMongoManager {
10
+ /**
11
+ * 中止标识
12
+ */
13
+ #aborted = false;
14
+ constructor(config, db, session) {
15
+ super(config, db, session);
16
+ }
17
+ timingQuery(opts) {
18
+ if (this.#aborted) {
19
+ throw new exception_1.MongoDBException('Session has been aborted!');
20
+ }
21
+ return super.timingQuery(opts);
22
+ }
23
+ /**
24
+ * 中止,被中止后的会话不能再进行任何操作
25
+ */
26
+ abort() {
27
+ this.#aborted = true;
28
+ }
29
+ }
30
+ exports.MongoTxSession = MongoTxSession;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrate = void 0;
4
+ const exception_1 = require("./exception");
5
+ const log_1 = require("../log");
6
+ /**
7
+ * 迁移
8
+ * @param db
9
+ * @param versionList
10
+ */
11
+ async function migrate(db, versionList) {
12
+ let currentVersion = await getCurrentVersion(db);
13
+ // 逐个执行,迁移执行的逻辑是无法提供事务支持的
14
+ // 版本管理代码是自定义的,无法做到强制绑定 session
15
+ // 一旦出错,只能手动处理数据库,然后再重新执行程序,和 mysql 一样
16
+ for (let idx = 0; idx < versionList.length; idx++) {
17
+ if (idx <= currentVersion) {
18
+ continue;
19
+ }
20
+ const migrationVersion = versionList[idx];
21
+ (0, log_1.getLogger)().info(`MongoDB migrating, version: ${idx}`);
22
+ await migrationVersion(db);
23
+ await updateVersion(db, idx);
24
+ }
25
+ }
26
+ exports.migrate = migrate;
27
+ const VERSION_COLLECTION_NAME = 'db_version';
28
+ const VERSION_RECORD_ID = 'db_version';
29
+ async function getCurrentVersion(db) {
30
+ const res = await db
31
+ .collection(VERSION_COLLECTION_NAME)
32
+ .findOne({ _id: VERSION_RECORD_ID });
33
+ return res ? res.version : -1;
34
+ }
35
+ /**
36
+ * 更新版本号
37
+ * @param db
38
+ * @param version
39
+ */
40
+ async function updateVersion(db, version) {
41
+ const collection = db.collection(VERSION_COLLECTION_NAME);
42
+ const res = await collection.findOne({ _id: VERSION_RECORD_ID });
43
+ if (res) {
44
+ const rs = await collection.updateOne({ _id: VERSION_RECORD_ID }, { $set: { version } });
45
+ if (rs.modifiedCount !== 1) {
46
+ throw new exception_1.MongoDBException('Failed to update version.');
47
+ }
48
+ }
49
+ else {
50
+ await collection.insertOne({ _id: VERSION_RECORD_ID, version: version });
51
+ }
52
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.accessLogInterceptor = void 0;
4
+ const log_1 = require("../log");
5
+ const date_1 = require("../log/date");
6
+ /**
7
+ * 访问日志拦截器,记录请求信息
8
+ * @param exchange
9
+ * @param next
10
+ */
11
+ const accessLogInterceptor = async (exchange, next) => {
12
+ const start = new Date().getTime();
13
+ const userAgent = exchange.request.headers['user-agent'];
14
+ const ip = exchange.request.socket.remoteAddress;
15
+ const { url, method } = exchange.request;
16
+ exchange.response.once('close', () => {
17
+ const status = exchange.response.statusCode;
18
+ const rt = new Date().getTime() - start;
19
+ (0, log_1.getLogger)().info(`[access-log]${JSON.stringify({
20
+ method,
21
+ url,
22
+ ip,
23
+ userAgent,
24
+ start: (0, date_1.formatDateTime)(new Date(start)),
25
+ rt,
26
+ status
27
+ })}`);
28
+ });
29
+ await next();
30
+ };
31
+ exports.accessLogInterceptor = accessLogInterceptor;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ const config_1 = require("../config");
5
+ const validation_1 = require("../validation");
6
+ exports.config = (0, config_1.registerConfig)({
7
+ port: 8080,
8
+ timeout: 30000,
9
+ accessLog: false,
10
+ corsAllowHeaders: '*',
11
+ corsAllowMethods: '*',
12
+ corsAllowOrigin: '*'
13
+ }, 'SERVER', {
14
+ port: [(0, validation_1.notNull)(), (0, validation_1.min)(80), (0, validation_1.max)(65535)],
15
+ timeout: [(0, validation_1.notNull)(), (0, validation_1.min)(1000), (0, validation_1.max)(60000)],
16
+ accessLog: [(0, validation_1.notNull)()],
17
+ corsAllowOrigin: [(0, validation_1.notBlank)()],
18
+ corsAllowHeaders: [(0, validation_1.notBlank)()],
19
+ corsAllowMethods: [(0, validation_1.notBlank)()]
20
+ });
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServerExchange = void 0;
4
+ const query_1 = require("./query");
5
+ const render_1 = require("./render");
6
+ const text_1 = require("./render/text");
7
+ /**
8
+ * 服务的数据交换对象.
9
+ */
10
+ class ServerExchange {
11
+ request;
12
+ response;
13
+ #bufferPromise;
14
+ constructor(request, response) {
15
+ this.request = request;
16
+ this.response = response;
17
+ }
18
+ bodyBuffer() {
19
+ if (this.#bufferPromise) {
20
+ return this.#bufferPromise;
21
+ }
22
+ this.#bufferPromise = new Promise((resolve, reject) => {
23
+ if (this.request.readableEnded) {
24
+ throw new Error('Request has ended!');
25
+ }
26
+ let body = [];
27
+ this.request
28
+ .resume()
29
+ .on('error', reject)
30
+ .on('data', chunk => body.push(chunk))
31
+ .on('end', () => resolve(Buffer.concat(body)));
32
+ });
33
+ return this.#bufferPromise;
34
+ }
35
+ async bodyText() {
36
+ const buffer = await this.bodyBuffer();
37
+ return buffer.toString('utf-8');
38
+ }
39
+ async bodyJson() {
40
+ const buffer = await this.bodyBuffer();
41
+ const bodyText = buffer.toString('utf-8');
42
+ if (!bodyText || !bodyText.trim()) {
43
+ return {};
44
+ }
45
+ return JSON.parse(bodyText);
46
+ }
47
+ /**
48
+ * 响应纯文本
49
+ * @param text 文本内容
50
+ * @param status 状态码,可选,默认 200
51
+ */
52
+ respondText(text, status) {
53
+ (0, text_1.renderText)(this.response, text, status);
54
+ }
55
+ /**
56
+ * 响应 json
57
+ * @param json 任意可被 json 序列化的对象
58
+ * @param status 状态码,可选,默认 200
59
+ */
60
+ respondJson(json, status) {
61
+ (0, render_1.renderJson)(this.response, json, status);
62
+ }
63
+ /**
64
+ * 响应错误信息提示,提供一个统一的格式封装,json 格式
65
+ * @param message 消息
66
+ * @param status 状态码,默认 400 ,表示业务错误
67
+ * @returns
68
+ */
69
+ respondErrMsg(message, status) {
70
+ (0, render_1.renderError)(this.response, message, status);
71
+ }
72
+ /**
73
+ * 响应文件
74
+ * @param filePath 文件路径,绝对路径
75
+ * @param download 是否下载模式
76
+ * @returns
77
+ */
78
+ respondFile(filePath, download) {
79
+ return (0, render_1.renderFile)(this.request, this.response, filePath, download);
80
+ }
81
+ /**
82
+ * 响应 html
83
+ * @param html html 内容,一个特定结构的对象或者是字符串
84
+ * @param status 状态码,可选,默认 200
85
+ */
86
+ respondHtml(html, status) {
87
+ (0, render_1.renderHtml)(this.response, html, status);
88
+ }
89
+ /**
90
+ * 响应
91
+ * @param opts
92
+ */
93
+ respond(opts) {
94
+ this.response.statusCode = opts.statusCode;
95
+ if (opts.headers) {
96
+ for (const key in opts.headers) {
97
+ this.response.setHeader(key, opts.headers[key]);
98
+ }
99
+ }
100
+ if (opts.body) {
101
+ this.response.write(opts.body);
102
+ }
103
+ this.response.end();
104
+ }
105
+ /**
106
+ * 解析 queryString
107
+ * @returns
108
+ */
109
+ parseQueryString() {
110
+ return new query_1.QueryString(this.request.url || '');
111
+ }
112
+ }
113
+ exports.ServerExchange = ServerExchange;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./json"), exports);
5
+ tslib_1.__exportStar(require("./upload"), exports);
6
+ tslib_1.__exportStar(require("./restful"), exports);
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJsonHandler = void 0;
4
+ const i18n_1 = require("../../i18n");
5
+ const validation_1 = require("../../validation");
6
+ /**
7
+ * 创建 json 处理器..
8
+ * @param <REQ> 表示请求的 json 数据格式类型
9
+ * @param <RES> 表示响应的类型,可选,如果不需要响应 json 数据,则方法可以不返回任何值
10
+ * @param opts
11
+ * @returns
12
+ */
13
+ function createJsonHandler(opts) {
14
+ return async function (exchange) {
15
+ if (!exchange.request.method || exchange.request.method.toUpperCase() !== 'POST') {
16
+ exchange.respondErrMsg('Method Not Allowed', 405);
17
+ return;
18
+ }
19
+ const body = await exchange.bodyJson();
20
+ if (opts.validation) {
21
+ // 切换语言
22
+ (0, i18n_1.getI18n)().switchByRequest(exchange.request.headers);
23
+ (0, validation_1.validate)(body, opts.validation);
24
+ }
25
+ const res = await opts.handle(body, exchange);
26
+ if (!res) {
27
+ exchange.respond({ statusCode: 200 });
28
+ return;
29
+ }
30
+ exchange.respondJson(res);
31
+ };
32
+ }
33
+ exports.createJsonHandler = createJsonHandler;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.restful = void 0;
4
+ /**
5
+ * 构建 restful 风格路由
6
+ */
7
+ function restful(opts) {
8
+ return async (exchange) => {
9
+ const method = (exchange.request.method || '').toLowerCase();
10
+ let handler;
11
+ switch (method) {
12
+ case 'get':
13
+ handler = opts.get;
14
+ break;
15
+ case 'post':
16
+ handler = opts.post;
17
+ break;
18
+ case 'put':
19
+ handler = opts.put;
20
+ break;
21
+ case 'patch':
22
+ handler = opts.patch;
23
+ break;
24
+ case 'delete':
25
+ handler = opts.delete;
26
+ break;
27
+ }
28
+ if (!handler) {
29
+ exchange.respondErrMsg(`${method} ${exchange.request.url} not found`, 404);
30
+ return;
31
+ }
32
+ await handler(exchange);
33
+ };
34
+ }
35
+ exports.restful = restful;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createUploadHandler = void 0;
4
+ /**
5
+ * 创建上传处理器. 上传要求一次只能传输一个文件,并且请求正文就是文件二进制内容。
6
+ * 必须是 post 请求,额外的参数只能通过 queryString 或 header 来传递。
7
+ * 和 postman 中的 binary 模式是一致的,这样上传文件性能会好一些,但是局限性也很大。
8
+ *
9
+ * @param opts
10
+ * @param <RES> 响应json数据类型
11
+ * @returns
12
+ */
13
+ function createUploadHandler(opts) {
14
+ return async function (exchange) {
15
+ if (!exchange.request.method || exchange.request.method.toUpperCase() !== 'POST') {
16
+ exchange.respondErrMsg('Method Not Allowed', 405);
17
+ return;
18
+ }
19
+ const body = await exchange.bodyBuffer();
20
+ const { url, headers } = exchange.request;
21
+ const res = await opts.handle(body, {
22
+ url: url || '',
23
+ headers,
24
+ query: exchange.parseQueryString()
25
+ });
26
+ if (!res) {
27
+ exchange.respond({ statusCode: 200 });
28
+ return;
29
+ }
30
+ exchange.respondJson(res);
31
+ };
32
+ }
33
+ exports.createUploadHandler = createUploadHandler;
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopWebServer = exports.startWebServer = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs_1 = require("fs");
6
+ const http_1 = require("http");
7
+ const os_1 = require("os");
8
+ const path_1 = require("path");
9
+ const log_1 = require("../log");
10
+ const access_log_1 = require("./access-log");
11
+ const config_1 = require("./config");
12
+ const exchange_1 = require("./exchange");
13
+ const render_1 = require("./render");
14
+ /**
15
+ * 处理请求,完成拦截器和路由的流程.
16
+ *
17
+ * @param interceptors 拦截器
18
+ * @param routers 路由
19
+ * @param req
20
+ * @param res
21
+ * @param staticSettings
22
+ */
23
+ async function handleRequest(interceptors, routers, req, res, staticSettings) {
24
+ req.socket.remoteAddress;
25
+ const { method } = req;
26
+ // cros 支持
27
+ res.setHeader('Access-Control-Allow-Origin', config_1.config.corsAllowOrigin);
28
+ res.setHeader('Access-Control-Allow-Headers', config_1.config.corsAllowHeaders);
29
+ res.setHeader('Access-Control-Allow-Methods', config_1.config.corsAllowMethods);
30
+ if (method === 'OPTIONS') {
31
+ res.statusCode = 200;
32
+ res.end();
33
+ return;
34
+ }
35
+ const exchange = new exchange_1.ServerExchange(req, res);
36
+ // 顺序执行拦截器
37
+ await handleInterceptor(interceptors, 0, exchange, req, res, routers, staticSettings);
38
+ }
39
+ /**
40
+ * 处理拦截器.
41
+ * @param interceptors 拦截器
42
+ * @param idx 当前要执行的拦截器下标
43
+ * @param exchange 传输对象
44
+ * @param req
45
+ * @param res
46
+ * @param routers 路由
47
+ * @param staticSettings
48
+ */
49
+ async function handleInterceptor(interceptors, idx, exchange, req, res, routers, staticSettings) {
50
+ const interceptor = interceptors[idx];
51
+ // 到最后一个了,那么执行路由处理
52
+ if (!interceptor) {
53
+ await handleRouter(exchange, routers, staticSettings);
54
+ return;
55
+ }
56
+ await interceptor(exchange, () => handleInterceptor(interceptors, idx + 1, exchange, req, res, routers, staticSettings));
57
+ }
58
+ /**
59
+ * 处理路由.
60
+ * @param exchange
61
+ * @param routers
62
+ * @param staticSettings
63
+ * @returns
64
+ */
65
+ async function handleRouter(exchange, routers, staticSettings) {
66
+ const url = exchange.request.url;
67
+ if (url === undefined) {
68
+ return;
69
+ }
70
+ // 判定路由
71
+ const idx = url.indexOf('?');
72
+ let path = idx === -1 ? url : url.substring(0, idx);
73
+ const router = routers[path];
74
+ if (!router) {
75
+ // 路由找不不到,尝试静态文件
76
+ if (staticSettings.length) {
77
+ await handleStatic(exchange, routers, path, staticSettings);
78
+ }
79
+ else {
80
+ respond404(exchange, routers, path);
81
+ }
82
+ return;
83
+ }
84
+ // 执行路由
85
+ await router(exchange);
86
+ // 在路由顺利处理的情况下,如果 res 没有 end ,就表示响应没有完成
87
+ // 也就是说路由没有做响应处理,或处理没有完成就结束了,给予错误提示
88
+ if (!exchange.response.writableEnded) {
89
+ throw new Error(`RouterHandler unresponsive, url: ${url}`);
90
+ }
91
+ }
92
+ /**
93
+ * 处理静态文件
94
+ * @param exchange
95
+ * @param routers
96
+ * @param path
97
+ * @param staticDir
98
+ * @returns
99
+ */
100
+ async function handleStatic(exchange, routers, path, staticSettings) {
101
+ // 匹配
102
+ let matchedSetting;
103
+ for (const setting of staticSettings) {
104
+ if (setting.path === '/') {
105
+ matchedSetting = setting;
106
+ break;
107
+ }
108
+ if (path.startsWith(setting.path)) {
109
+ matchedSetting = setting;
110
+ break;
111
+ }
112
+ }
113
+ if (!matchedSetting) {
114
+ respond404(exchange, routers, path);
115
+ return;
116
+ }
117
+ let finalPath = matchedSetting.path === '/' ? path : path.substring(matchedSetting.path.length);
118
+ if (finalPath.startsWith('/')) {
119
+ finalPath = finalPath.substring(1);
120
+ }
121
+ const fullPath = (0, path_1.resolve)(matchedSetting.dir, finalPath);
122
+ if (!(0, fs_1.existsSync)(fullPath)) {
123
+ respond404(exchange, routers, path);
124
+ return;
125
+ }
126
+ const stat = (0, fs_1.statSync)(fullPath);
127
+ // 目录,寻找 index.html
128
+ if (stat.isDirectory()) {
129
+ const indexPath = (0, path_1.resolve)(fullPath, 'index.html');
130
+ if (!(0, fs_1.existsSync)(indexPath)) {
131
+ respond404(exchange, routers, path);
132
+ return;
133
+ }
134
+ const indexStat = (0, fs_1.statSync)(indexPath);
135
+ if (!indexStat.isFile()) {
136
+ respond404(exchange, routers, path);
137
+ return;
138
+ }
139
+ // Cache-Control
140
+ if (matchedSetting.cacheAge >= 0) {
141
+ exchange.response.setHeader('Cache-Control', matchedSetting.cacheAge === 0 ? 'no-store' : `max-age=${matchedSetting.cacheAge}`);
142
+ }
143
+ await exchange.respondFile(indexPath, false);
144
+ return;
145
+ }
146
+ // 文件直接渲染
147
+ if (stat.isFile()) {
148
+ // Cache-Control
149
+ if (matchedSetting.cacheAge >= 0) {
150
+ exchange.response.setHeader('Cache-Control', matchedSetting.cacheAge === 0 ? 'no-store' : `max-age=${matchedSetting.cacheAge}`);
151
+ }
152
+ await exchange.respondFile(fullPath, false);
153
+ return;
154
+ }
155
+ // 其它类型,404
156
+ respond404(exchange, routers, path);
157
+ }
158
+ /**
159
+ * 404响应
160
+ *
161
+ * @param exchange
162
+ * @param routers
163
+ * @param path
164
+ */
165
+ async function respond404(exchange, routers, path) {
166
+ const handler = routers['*'];
167
+ if (handler) {
168
+ await handler(exchange);
169
+ }
170
+ else {
171
+ exchange.respondErrMsg(`${path} not found`, 404);
172
+ }
173
+ }
174
+ /**
175
+ * 获取 ipv4 地址列表
176
+ * @returns
177
+ */
178
+ function getIpv4List() {
179
+ const ifs = (0, os_1.networkInterfaces)();
180
+ const res = [];
181
+ for (const name in ifs) {
182
+ const list = ifs[name];
183
+ if (!list) {
184
+ continue;
185
+ }
186
+ res.push(...list.filter(info => info.family === 'IPv4').map(info => info.address));
187
+ }
188
+ return res;
189
+ }
190
+ /**
191
+ * 服务实例.
192
+ */
193
+ let SERVER;
194
+ /**
195
+ * 启动 web服务
196
+ * @param opts
197
+ */
198
+ async function startWebServer(opts) {
199
+ if (SERVER) {
200
+ throw new Error('The server has already been started!');
201
+ }
202
+ // 检查静态文件配置,做一些预处理的操作,在处理静态资源的请求时不必再做这些处理
203
+ const staticSettings = [];
204
+ if (opts.static) {
205
+ // 重复记录表 ,作用是为了路径去重判定,可以提示哪些路径是重复的
206
+ const duplicateMap = new Map();
207
+ for (const entry of Object.entries(opts.static)) {
208
+ const [path, setting] = entry;
209
+ const dir = (0, path_1.isAbsolute)(setting.dir) ? setting.dir : (0, path_1.resolve)(process.cwd(), setting.dir);
210
+ if (!(0, fs_1.existsSync)(dir)) {
211
+ throw new Error(`Static file configuration error,path ${dir} does not exist,config dir:${setting.dir}`);
212
+ }
213
+ const stat = (0, fs_1.statSync)(dir);
214
+ if (!stat.isDirectory()) {
215
+ throw new Error(`Static file configuration error,path ${dir} is not a directory,config dir:${setting.dir}`);
216
+ }
217
+ let finalPath = path.startsWith('/') ? path : '/' + path;
218
+ // 保持以 / 结尾,为了匹配方便
219
+ if (!finalPath.endsWith('/')) {
220
+ finalPath += '/';
221
+ }
222
+ if (duplicateMap.has(finalPath)) {
223
+ throw new Error(`Static path duplicated: ${duplicateMap.get(finalPath)} and ${path}`);
224
+ }
225
+ duplicateMap.set(finalPath, path);
226
+ staticSettings.push({ path: finalPath, dir, cacheAge: setting.cacheAge || 0 });
227
+ }
228
+ // 优先级排序
229
+ staticSettings.sort((o1, o2) => {
230
+ let priority1 = o1.path === '/' ? -1 : o1.path.split('/').length;
231
+ let priority2 = o2.path === '/' ? -1 : o2.path.split('/').length;
232
+ // 如果 o1 优先级高,就应该排前面,返回小于0的值,反之亦然\
233
+ // 前面的优先级值是值越大优先级越高,反过来减
234
+ return priority2 - priority1;
235
+ });
236
+ }
237
+ // 如果启用请求日志,增加拦截器
238
+ let interceptors = [];
239
+ if (config_1.config.accessLog) {
240
+ interceptors.push(access_log_1.accessLogInterceptor);
241
+ }
242
+ if (opts.interceptors) {
243
+ interceptors.push(...opts.interceptors);
244
+ }
245
+ SERVER = (0, http_1.createServer)((req, res) => {
246
+ res.on('error', error => {
247
+ // 如果响应流发生错误,只能把信息记录下来
248
+ (0, log_1.getLogger)().error(`Response Error:${req.url}`, error);
249
+ });
250
+ handleRequest(interceptors, opts.routers, req, res, staticSettings).catch(error => {
251
+ (0, log_1.getLogger)().error(`Handle request failed:${req.url}`, error);
252
+ if (!res.writableEnded) {
253
+ // 响应 500
254
+ (0, render_1.renderError)(res, error.message ? error.message : 'Internal Server Error', 500);
255
+ }
256
+ });
257
+ });
258
+ SERVER.setTimeout(config_1.config.timeout);
259
+ SERVER.on('timeout', (socket) => {
260
+ socket.end('HTTP/1.1 408 Timeout\ncontent-type: application/json; charset=utf-8\n\n{"message":"Request timeout"}');
261
+ });
262
+ if (opts.preHandler) {
263
+ await opts.preHandler(SERVER);
264
+ }
265
+ const server = SERVER;
266
+ await new Promise((resolve, reject) => {
267
+ server.on('error', e => {
268
+ if (e.code === 'EADDRINUSE') {
269
+ reject(`端口号 ${config_1.config.port} 已经被占用`);
270
+ }
271
+ else {
272
+ reject(e);
273
+ }
274
+ });
275
+ server.listen(config_1.config.port, resolve);
276
+ });
277
+ console.log('App running at: ');
278
+ getIpv4List().forEach(ip => {
279
+ console.log(`http://${ip}:${config_1.config.port}`);
280
+ });
281
+ process.on('beforeExit', () => {
282
+ if (server.listening) {
283
+ server.close();
284
+ }
285
+ });
286
+ }
287
+ exports.startWebServer = startWebServer;
288
+ /**
289
+ * 停止 web 服务
290
+ * @returns
291
+ */
292
+ async function stopWebServer() {
293
+ if (!SERVER) {
294
+ return;
295
+ }
296
+ if (SERVER.listening) {
297
+ const server = SERVER;
298
+ await new Promise((res, rej) => {
299
+ server.close(err => {
300
+ if (err) {
301
+ rej(err);
302
+ }
303
+ else {
304
+ res();
305
+ }
306
+ });
307
+ });
308
+ }
309
+ SERVER = undefined;
310
+ }
311
+ exports.stopWebServer = stopWebServer;
312
+ tslib_1.__exportStar(require("./exchange"), exports);
313
+ tslib_1.__exportStar(require("./handler"), exports);
314
+ tslib_1.__exportStar(require("./render"), exports);
315
+ tslib_1.__exportStar(require("./router"), exports);
316
+ tslib_1.__exportStar(require("./interceptor"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });