kb-server 0.0.1-beta.38 → 0.0.1-beta.39
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 +27 -3
- package/dist/common/api-middleware.js +6 -6
- package/dist/common/create-sse.d.ts +11 -3
- package/dist/common/create-sse.js +2 -2
- package/dist/common/sse-middleware.d.ts +1 -1
- package/dist/common/sse-middleware.js +66 -20
- package/dist/helper/logger.d.ts +3 -4
- package/dist/helper/logger.js +8 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# KB Server
|
|
2
2
|
|
|
3
|
-
快速创建一个 Node 服务
|
|
3
|
+
快速创建一个 `Node` 服务 - 基于 `Express`
|
|
4
4
|
|
|
5
|
-
快速创建 API
|
|
5
|
+
快速创建 `API`、标准错误码、`Server`、支持统一鉴权函数(API 级别)、自定义中间件、日志、支持 `SSE`
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```typescript
|
|
8
8
|
import { createServer } from "kb-server";
|
|
9
9
|
import * as apis from "./apis";
|
|
10
10
|
|
|
@@ -18,3 +18,27 @@ server.listen(3000);
|
|
|
18
18
|
return createServer({ apis });
|
|
19
19
|
})().then((app) => app.listen(3000));
|
|
20
20
|
```
|
|
21
|
+
|
|
22
|
+
## SSE 支持
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { createServer } from "kb-server";
|
|
26
|
+
import * as apis from "./apis";
|
|
27
|
+
import * as sse from "./sse";
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
(async () => {
|
|
31
|
+
// 其他的异步操作,例如:初始化数据库
|
|
32
|
+
return createServer({
|
|
33
|
+
apis,
|
|
34
|
+
sse: {
|
|
35
|
+
handlers: sse, // ...SSE处理函数
|
|
36
|
+
},
|
|
37
|
+
authFn: (action, req) => {
|
|
38
|
+
// ...统一鉴权函数
|
|
39
|
+
},
|
|
40
|
+
middlewares: [], // ...中间键列表
|
|
41
|
+
log: true, // ...框架日志
|
|
42
|
+
});
|
|
43
|
+
})().then((app) => app.listen(3000));
|
|
44
|
+
```
|
|
@@ -27,7 +27,7 @@ const packAPI = (apis, options) => {
|
|
|
27
27
|
// API 解析
|
|
28
28
|
const { Action, ...params } = req.body || {};
|
|
29
29
|
if (log) {
|
|
30
|
-
logger_1.logger.
|
|
30
|
+
logger_1.logger.info(`请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
|
|
31
31
|
}
|
|
32
32
|
// 接口未定义
|
|
33
33
|
if (!Action) {
|
|
@@ -63,12 +63,12 @@ const packAPI = (apis, options) => {
|
|
|
63
63
|
};
|
|
64
64
|
// 完成响应
|
|
65
65
|
took = Date.now() - start;
|
|
66
|
-
logger_1.logger.
|
|
67
|
-
logger_1.logger.
|
|
66
|
+
logger_1.logger.info(`响应:${JSON.stringify(response)}`);
|
|
67
|
+
logger_1.logger.info(`耗时:${took} ms - RequestId: ${requestId}`);
|
|
68
68
|
return res.send(response);
|
|
69
69
|
}
|
|
70
70
|
catch (rawError) {
|
|
71
|
-
logger_1.logger.
|
|
71
|
+
logger_1.logger.error(rawError);
|
|
72
72
|
// 未知错误
|
|
73
73
|
let error = new create_errors_1.CommonErrors.InternalError.UnknownError();
|
|
74
74
|
// 可控错误
|
|
@@ -90,8 +90,8 @@ const packAPI = (apis, options) => {
|
|
|
90
90
|
};
|
|
91
91
|
// 完成响应
|
|
92
92
|
took = Date.now() - start;
|
|
93
|
-
logger_1.logger.
|
|
94
|
-
logger_1.logger.
|
|
93
|
+
logger_1.logger.info(`响应:${JSON.stringify(response)}`);
|
|
94
|
+
logger_1.logger.info(`耗时:${took} ms - RequestId: ${requestId}`);
|
|
95
95
|
return res.send(response);
|
|
96
96
|
}
|
|
97
97
|
};
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { Class } from "utility-types";
|
|
2
2
|
import { ServerContext } from "./create-api";
|
|
3
|
-
|
|
3
|
+
export type PushHandler = (data: Record<string, any>) => void;
|
|
4
|
+
export type CloseHandler = () => void;
|
|
5
|
+
export type SSEHandlers = {
|
|
6
|
+
/** 数据推送 */
|
|
7
|
+
push: PushHandler;
|
|
8
|
+
/** 关闭函数(调用结束要显式关闭) */
|
|
9
|
+
close: CloseHandler;
|
|
10
|
+
abortController: AbortController;
|
|
11
|
+
};
|
|
4
12
|
export type API<P, R> = (param: P) => Promise<R>;
|
|
5
13
|
export type SseExecution<P, R, A> = (
|
|
6
14
|
/** 请求入参 */
|
|
7
15
|
params: P,
|
|
8
|
-
/** SSE
|
|
9
|
-
|
|
16
|
+
/** SSE相关操作函数 */
|
|
17
|
+
sse: SSEHandlers,
|
|
10
18
|
/** 请求上下文 */
|
|
11
19
|
ctx: ServerContext<A>) => Promise<R>;
|
|
12
20
|
export type AnySseExecution = SseExecution<any, any, any>;
|
|
@@ -6,7 +6,7 @@ const class_validator_1 = require("class-validator");
|
|
|
6
6
|
const create_errors_1 = require("./create-errors");
|
|
7
7
|
const create_api_1 = require("./create-api");
|
|
8
8
|
function createSseAPI(ParamsClass, execution) {
|
|
9
|
-
const runtime = async (params,
|
|
9
|
+
const runtime = async (params, sse, ctx) => {
|
|
10
10
|
if (!params) {
|
|
11
11
|
throw new create_errors_1.CommonErrors.InvalidParameter.EmptyParameter();
|
|
12
12
|
}
|
|
@@ -20,7 +20,7 @@ function createSseAPI(ParamsClass, execution) {
|
|
|
20
20
|
throw new create_errors_1.CommonErrors.InvalidParameter.ValidationError(errorMessages.join("\n"));
|
|
21
21
|
}
|
|
22
22
|
// 执行函数
|
|
23
|
-
return await execution(params,
|
|
23
|
+
return await execution(params, sse, ctx);
|
|
24
24
|
};
|
|
25
25
|
return runtime;
|
|
26
26
|
}
|
|
@@ -7,5 +7,5 @@ interface IPackSSEOptions {
|
|
|
7
7
|
log?: boolean;
|
|
8
8
|
}
|
|
9
9
|
export declare const packSSE: (sseHandlers: SseHandlers, options?: IPackSSEOptions) => express.RequestHandler;
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const createSSEMsg: (event: string, message: string) => string;
|
|
11
11
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const better_sse_1 = require("better-sse");
|
|
3
|
+
exports.createSSEMsg = exports.packSSE = void 0;
|
|
5
4
|
const uuid_1 = require("uuid");
|
|
6
5
|
const logger_1 = require("../helper/logger");
|
|
7
6
|
const create_errors_1 = require("./create-errors");
|
|
@@ -38,7 +37,7 @@ const packSSE = (sseHandlers, options) => {
|
|
|
38
37
|
// API 解析
|
|
39
38
|
const { Action, ...params } = req.body || {};
|
|
40
39
|
if (log) {
|
|
41
|
-
logger_1.logger.
|
|
40
|
+
logger_1.logger.info(`请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
|
|
42
41
|
}
|
|
43
42
|
// 接口未定义
|
|
44
43
|
if (!Action) {
|
|
@@ -63,17 +62,55 @@ const packSSE = (sseHandlers, options) => {
|
|
|
63
62
|
if (typeof execution !== "function") {
|
|
64
63
|
throw new create_errors_1.CommonErrors.ResourceNotFound.APINotFound();
|
|
65
64
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
// 写头
|
|
66
|
+
res.writeHead(200, {
|
|
67
|
+
"Content-Type": "text/event-stream",
|
|
68
|
+
"Cache-Control": "no-cache",
|
|
69
|
+
Connection: "keep-alive",
|
|
70
|
+
});
|
|
71
|
+
const abortController = new AbortController();
|
|
72
|
+
// 连接断开
|
|
73
|
+
let isConnected = true;
|
|
74
|
+
res.on("close", () => {
|
|
75
|
+
isConnected = false;
|
|
76
|
+
abortController.abort();
|
|
77
|
+
// 完成响应
|
|
78
|
+
took = Date.now() - start;
|
|
79
|
+
logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
|
|
80
|
+
return res.end();
|
|
81
|
+
});
|
|
82
|
+
// 推送消息
|
|
83
|
+
const push = (data) => {
|
|
84
|
+
if (!isConnected) {
|
|
85
|
+
logger_1.logger.warning(`连接已关闭: ${requestId}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const msg = createResponseMsg(data);
|
|
89
|
+
try {
|
|
90
|
+
res.write(msg);
|
|
91
|
+
logger_1.logger.info(`推送数据: ${msg}`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error.code === "EPIPE" || error.code === "ECONNRESET") {
|
|
95
|
+
logger_1.logger.warning(`连接已关闭: ${requestId}`);
|
|
96
|
+
isConnected = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
// 主动关闭
|
|
101
|
+
const close = () => {
|
|
102
|
+
logger_1.logger.info(`主动关闭连接: ${requestId}`);
|
|
103
|
+
// 完成响应
|
|
104
|
+
took = Date.now() - start;
|
|
105
|
+
logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
|
|
106
|
+
res.end();
|
|
107
|
+
};
|
|
108
|
+
await execution(params, { push, close, abortController }, ctx);
|
|
72
109
|
}
|
|
73
110
|
catch (rawError) {
|
|
74
|
-
logger_1.logger.
|
|
111
|
+
logger_1.logger.error(rawError);
|
|
75
112
|
if (!res.headersSent) {
|
|
76
|
-
//
|
|
113
|
+
// 写头
|
|
77
114
|
res.writeHead(200, {
|
|
78
115
|
"Content-Type": "text/event-stream",
|
|
79
116
|
"Cache-Control": "no-cache",
|
|
@@ -90,27 +127,36 @@ const packSSE = (sseHandlers, options) => {
|
|
|
90
127
|
if (rawError?.sql) {
|
|
91
128
|
error = new create_errors_1.CommonErrors.InternalError.DatabaseError();
|
|
92
129
|
}
|
|
93
|
-
const
|
|
130
|
+
const errResponse = {
|
|
94
131
|
Response: {
|
|
95
132
|
Error: { Code: error.code, Message: error.message },
|
|
96
133
|
RequestId: requestId,
|
|
97
134
|
},
|
|
98
|
-
}
|
|
135
|
+
};
|
|
136
|
+
const response = (0, exports.createSSEMsg)("error", JSON.stringify(errResponse));
|
|
99
137
|
// 完成响应
|
|
100
138
|
took = Date.now() - start;
|
|
101
|
-
logger_1.logger.
|
|
102
|
-
logger_1.logger.
|
|
103
|
-
res.write(response);
|
|
104
|
-
return res.end();
|
|
139
|
+
logger_1.logger.info(`发送消息:\n${response}`);
|
|
140
|
+
logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
|
|
141
|
+
res.write(response, () => res.end());
|
|
105
142
|
}
|
|
106
143
|
};
|
|
107
144
|
};
|
|
108
145
|
exports.packSSE = packSSE;
|
|
109
|
-
const
|
|
146
|
+
const createSSEMsg = (event, message) => {
|
|
110
147
|
const id = (0, short_id_1.shortId)();
|
|
111
148
|
const idStr = `id: ${id}\n`;
|
|
112
149
|
const eventStr = `event: ${event}\n`;
|
|
113
150
|
const dataStr = `data: ${message}\n\n`;
|
|
114
|
-
return idStr
|
|
151
|
+
return `${idStr}${eventStr}${dataStr}`;
|
|
152
|
+
};
|
|
153
|
+
exports.createSSEMsg = createSSEMsg;
|
|
154
|
+
const createResponseMsg = (data, requestId) => {
|
|
155
|
+
const msg = {
|
|
156
|
+
Response: {
|
|
157
|
+
RequestId: requestId || (0, uuid_1.v4)(),
|
|
158
|
+
Data: data,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
return JSON.stringify(msg);
|
|
115
162
|
};
|
|
116
|
-
exports.createSSEMessage = createSSEMessage;
|
package/dist/helper/logger.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare const logger: {
|
|
2
|
-
|
|
2
|
+
info: (message: unknown) => void;
|
|
3
|
+
error: (message: unknown) => void;
|
|
4
|
+
warning: (message: unknown) => void;
|
|
3
5
|
};
|
|
4
|
-
type LogLevel = "info" | "error" | "warning";
|
|
5
|
-
declare function log(level: LogLevel, message: unknown): void;
|
|
6
|
-
export {};
|
package/dist/helper/logger.js
CHANGED
|
@@ -4,14 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.logger = void 0;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const logTime = (0, moment_1.default)().format(`YYYY-MM-DD HH:mm:ss`);
|
|
7
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
8
|
+
const log = (level, message) => {
|
|
9
|
+
const logTime = (0, dayjs_1.default)().format(`YYYY-MM-DD HH:mm:ss`);
|
|
11
10
|
if (typeof message === "string") {
|
|
12
11
|
console.log(`[${level}] - [${logTime}] - ${message}`);
|
|
13
12
|
}
|
|
14
13
|
else {
|
|
15
14
|
console.log(`[${level}] - [${logTime}] - `, message);
|
|
16
15
|
}
|
|
17
|
-
}
|
|
16
|
+
};
|
|
17
|
+
const info = (message) => log("info", message);
|
|
18
|
+
const error = (message) => log("error", message);
|
|
19
|
+
const warning = (message) => log("warning", message);
|
|
20
|
+
exports.logger = { info, error, warning };
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kb-server",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.39",
|
|
4
4
|
"description": "A fast server for Node.JS,made by express.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rm -rf ./dist && tsc",
|
|
8
|
-
"
|
|
8
|
+
"release": "npm run build && npm publish"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"2kb",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"better-sse": "^0.13.0",
|
|
26
26
|
"class-transformer": "^0.5.1",
|
|
27
27
|
"class-validator": "^0.14.1",
|
|
28
|
+
"dayjs": "^1.11.13",
|
|
28
29
|
"express": "^4.21.1",
|
|
29
30
|
"is-plain-object": "^5.0.0",
|
|
30
|
-
"moment": "^2.30.1",
|
|
31
31
|
"nanoid": "^3.3.9",
|
|
32
32
|
"utility-types": "^3.11.0",
|
|
33
33
|
"uuid": "^11.0.3"
|