kb-server 0.0.1-beta.37 → 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/call-service.js +1 -5
- package/dist/common/create-sse.d.ts +12 -10
- package/dist/common/create-sse.js +2 -2
- package/dist/common/sse-middleware.d.ts +1 -1
- package/dist/common/sse-middleware.js +55 -44
- package/dist/helper/logger.d.ts +3 -4
- package/dist/helper/logger.js +8 -5
- package/package.json +8 -6
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,13 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.callService = void 0;
|
|
7
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
4
|
const create_errors_1 = require("./create-errors");
|
|
9
5
|
const callService = async (endpoint, body) => {
|
|
10
|
-
const response = await (
|
|
6
|
+
const response = await fetch(endpoint, {
|
|
11
7
|
headers: {
|
|
12
8
|
"Content-Type": "application/json",
|
|
13
9
|
},
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { Class } from "utility-types";
|
|
2
2
|
import { ServerContext } from "./create-api";
|
|
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
|
+
};
|
|
3
12
|
export type API<P, R> = (param: P) => Promise<R>;
|
|
4
13
|
export type SseExecution<P, R, A> = (
|
|
5
14
|
/** 请求入参 */
|
|
6
15
|
params: P,
|
|
16
|
+
/** SSE相关操作函数 */
|
|
17
|
+
sse: SSEHandlers,
|
|
7
18
|
/** 请求上下文 */
|
|
8
|
-
ctx: ServerContext<A
|
|
9
|
-
/**
|
|
10
|
-
* 操作函数
|
|
11
|
-
*/
|
|
12
|
-
operations: {
|
|
13
|
-
/**
|
|
14
|
-
* 向客户端推送消息
|
|
15
|
-
*/
|
|
16
|
-
send: (event: string, message: string) => void;
|
|
17
|
-
}) => Promise<R>;
|
|
19
|
+
ctx: ServerContext<A>) => Promise<R>;
|
|
18
20
|
export type AnySseExecution = SseExecution<any, any, any>;
|
|
19
21
|
export type SseHandlers = Record<string, AnySseExecution>;
|
|
20
22
|
export declare function createSseAPI<P, R, A>(ParamsClass: Class<P>, execution: SseExecution<P, R, A>): API<P, R>;
|
|
@@ -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,11 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createSSEMsg = exports.packSSE = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const logger_1 = require("../helper/logger");
|
|
6
6
|
const create_errors_1 = require("./create-errors");
|
|
7
7
|
const short_id_1 = require("../helper/short-id");
|
|
8
|
-
const sleep_1 = require("../helper/sleep");
|
|
9
8
|
const packSSE = (sseHandlers, options) => {
|
|
10
9
|
const { authFn, log = true, route = "/sse" } = options || {};
|
|
11
10
|
return async (req, res, next) => {
|
|
@@ -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,50 +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
|
-
let isClientConnected = true;
|
|
68
|
-
// 监听客户端断开事件
|
|
69
|
-
res.on("close", () => {
|
|
70
|
-
isClientConnected = false; // 标记连接已断开
|
|
71
|
-
logger_1.logger.log("info", `客户端已断开 - RequestId: ${requestId}`);
|
|
72
|
-
});
|
|
73
|
-
// 写请求头
|
|
65
|
+
// 写头
|
|
74
66
|
res.writeHead(200, {
|
|
75
67
|
"Content-Type": "text/event-stream",
|
|
76
68
|
"Cache-Control": "no-cache",
|
|
77
69
|
Connection: "keep-alive",
|
|
78
70
|
});
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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;
|
|
83
87
|
}
|
|
84
|
-
const
|
|
88
|
+
const msg = createResponseMsg(data);
|
|
85
89
|
try {
|
|
86
|
-
res.write(
|
|
87
|
-
|
|
88
|
-
logger_1.logger.log("info", `发送消息:\n${response}`);
|
|
89
|
-
}
|
|
90
|
+
res.write(msg);
|
|
91
|
+
logger_1.logger.info(`推送数据: ${msg}`);
|
|
90
92
|
}
|
|
91
|
-
catch (
|
|
92
|
-
if (
|
|
93
|
-
logger_1.logger.
|
|
94
|
-
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error.code === "EPIPE" || error.code === "ECONNRESET") {
|
|
95
|
+
logger_1.logger.warning(`连接已关闭: ${requestId}`);
|
|
96
|
+
isConnected = false;
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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);
|
|
105
109
|
}
|
|
106
110
|
catch (rawError) {
|
|
107
|
-
logger_1.logger.
|
|
111
|
+
logger_1.logger.error(rawError);
|
|
108
112
|
if (!res.headersSent) {
|
|
109
|
-
//
|
|
113
|
+
// 写头
|
|
110
114
|
res.writeHead(200, {
|
|
111
115
|
"Content-Type": "text/event-stream",
|
|
112
116
|
"Cache-Control": "no-cache",
|
|
@@ -123,29 +127,36 @@ const packSSE = (sseHandlers, options) => {
|
|
|
123
127
|
if (rawError?.sql) {
|
|
124
128
|
error = new create_errors_1.CommonErrors.InternalError.DatabaseError();
|
|
125
129
|
}
|
|
126
|
-
const
|
|
130
|
+
const errResponse = {
|
|
127
131
|
Response: {
|
|
128
132
|
Error: { Code: error.code, Message: error.message },
|
|
129
133
|
RequestId: requestId,
|
|
130
134
|
},
|
|
131
|
-
}
|
|
135
|
+
};
|
|
136
|
+
const response = (0, exports.createSSEMsg)("error", JSON.stringify(errResponse));
|
|
132
137
|
// 完成响应
|
|
133
138
|
took = Date.now() - start;
|
|
134
|
-
logger_1.logger.
|
|
135
|
-
logger_1.logger.
|
|
136
|
-
res.write(response);
|
|
137
|
-
// 延迟100ms关闭连接
|
|
138
|
-
await (0, sleep_1.sleep)(100);
|
|
139
|
-
return res.end();
|
|
139
|
+
logger_1.logger.info(`发送消息:\n${response}`);
|
|
140
|
+
logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
|
|
141
|
+
res.write(response, () => res.end());
|
|
140
142
|
}
|
|
141
143
|
};
|
|
142
144
|
};
|
|
143
145
|
exports.packSSE = packSSE;
|
|
144
|
-
const
|
|
146
|
+
const createSSEMsg = (event, message) => {
|
|
145
147
|
const id = (0, short_id_1.shortId)();
|
|
146
148
|
const idStr = `id: ${id}\n`;
|
|
147
149
|
const eventStr = `event: ${event}\n`;
|
|
148
150
|
const dataStr = `data: ${message}\n\n`;
|
|
149
|
-
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);
|
|
150
162
|
};
|
|
151
|
-
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",
|
|
@@ -16,21 +16,23 @@
|
|
|
16
16
|
"dist/",
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
19
22
|
"author": "broxiang",
|
|
20
23
|
"license": "ISC",
|
|
21
24
|
"dependencies": {
|
|
25
|
+
"better-sse": "^0.13.0",
|
|
22
26
|
"class-transformer": "^0.5.1",
|
|
23
27
|
"class-validator": "^0.14.1",
|
|
28
|
+
"dayjs": "^1.11.13",
|
|
24
29
|
"express": "^4.21.1",
|
|
25
30
|
"is-plain-object": "^5.0.0",
|
|
26
|
-
"moment": "^2.30.1",
|
|
27
31
|
"nanoid": "^3.3.9",
|
|
28
|
-
"node-fetch": "^2.7.0",
|
|
29
32
|
"utility-types": "^3.11.0",
|
|
30
33
|
"uuid": "^11.0.3"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
|
-
"@types/express": "^4.17.21"
|
|
34
|
-
"@types/node-fetch": "^2.6.12"
|
|
36
|
+
"@types/express": "^4.17.21"
|
|
35
37
|
}
|
|
36
38
|
}
|