kb-server 0.0.1-beta.8 → 0.0.2-beta.1
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 +33 -3
- package/dist/common/api-middleware.js +6 -6
- package/dist/common/call-service.d.ts +1 -0
- package/dist/common/call-service.js +23 -0
- package/dist/common/create-api.d.ts +12 -7
- package/dist/common/create-api.js +26 -5
- package/dist/common/create-errors.d.ts +3 -0
- package/dist/common/create-errors.js +1 -0
- package/dist/common/create-server.d.ts +50 -0
- package/dist/common/create-server.js +7 -1
- package/dist/common/create-sse.d.ts +25 -0
- package/dist/common/create-sse.js +31 -0
- package/dist/common/sse-middleware.d.ts +12 -0
- package/dist/common/sse-middleware.js +173 -0
- package/dist/helper/logger.d.ts +3 -4
- package/dist/helper/logger.js +11 -8
- package/dist/helper/short-id.d.ts +1 -0
- package/dist/helper/short-id.js +7 -0
- package/dist/helper/sleep.d.ts +1 -0
- package/dist/helper/sleep.js +5 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,14 +1,44 @@
|
|
|
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
|
|
|
11
|
+
// 写法一
|
|
12
|
+
const server = createServer({ apis });
|
|
13
|
+
server.listen(3000);
|
|
14
|
+
|
|
15
|
+
// 写法二
|
|
11
16
|
(async () => {
|
|
17
|
+
// 其他的异步操作,例如:初始化数据库
|
|
12
18
|
return createServer({ apis });
|
|
13
19
|
})().then((app) => app.listen(3000));
|
|
14
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
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const callService: <P = Record<string, any>, R = any>(endpoint: string, body: P) => Promise<R>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.callService = void 0;
|
|
4
|
+
const create_errors_1 = require("./create-errors");
|
|
5
|
+
const callService = async (endpoint, body) => {
|
|
6
|
+
const response = await fetch(endpoint, {
|
|
7
|
+
headers: {
|
|
8
|
+
"Content-Type": "application/json",
|
|
9
|
+
},
|
|
10
|
+
method: "POST",
|
|
11
|
+
body: JSON.stringify(body),
|
|
12
|
+
});
|
|
13
|
+
const result = await response.json();
|
|
14
|
+
if (result?.Response?.Error) {
|
|
15
|
+
const errMsg = result.Response.Error?.Message;
|
|
16
|
+
if (errMsg) {
|
|
17
|
+
throw new create_errors_1.CommonErrors.InternalError.ServiceError(errMsg);
|
|
18
|
+
}
|
|
19
|
+
throw new create_errors_1.CommonErrors.InternalError.ServiceError();
|
|
20
|
+
}
|
|
21
|
+
return result?.Response?.Data;
|
|
22
|
+
};
|
|
23
|
+
exports.callService = callService;
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
import { ValidationError } from "class-validator";
|
|
1
2
|
import { Class } from "utility-types";
|
|
2
|
-
export interface ServerContext {
|
|
3
|
+
export interface ServerContext<A = any> {
|
|
3
4
|
/** 被调用方的 RequestId */
|
|
4
5
|
RequestId: string;
|
|
5
6
|
/** 权限信息 */
|
|
6
|
-
AuthInfo?:
|
|
7
|
+
AuthInfo?: A;
|
|
7
8
|
}
|
|
8
9
|
export type API<P, R> = (param: P) => Promise<R>;
|
|
9
|
-
export type APIExecution<P, R> = (
|
|
10
|
+
export type APIExecution<P, R, A> = (
|
|
10
11
|
/** 请求入参 */
|
|
11
12
|
params: P,
|
|
12
13
|
/** 请求上下文 */
|
|
13
|
-
ctx: ServerContext) => Promise<R>;
|
|
14
|
-
export type AnyAPIExecution = APIExecution<any, any>;
|
|
14
|
+
ctx: ServerContext<A>) => Promise<R>;
|
|
15
|
+
export type AnyAPIExecution = APIExecution<any, any, any>;
|
|
15
16
|
export type APIs = Record<string, AnyAPIExecution>;
|
|
16
|
-
export declare function createAPI<P, R>(ParamsClass: Class<P>, execution: APIExecution<P, R>): API<P, R>;
|
|
17
|
+
export declare function createAPI<P, R, A>(ParamsClass: Class<P>, execution: APIExecution<P, R, A>): API<P, R>;
|
|
17
18
|
type StubParam<T> = T extends (params: infer P) => any ? P : never;
|
|
18
|
-
export declare function implementAPI<
|
|
19
|
+
export declare function implementAPI<F extends (params: any) => any, A = Record<string, any>>(_actionStub: F, ParamsClass: Class<StubParam<F>>, execution: APIExecution<StubParam<F>, ReturnType<F>, A>): API<StubParam<F>, ReturnType<F>>;
|
|
20
|
+
/**
|
|
21
|
+
* 解析错误信息
|
|
22
|
+
*/
|
|
23
|
+
export declare function getMessage(message: ValidationError, propertyPath?: string): string[];
|
|
19
24
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.implementAPI = exports.createAPI = void 0;
|
|
3
|
+
exports.getMessage = exports.implementAPI = exports.createAPI = void 0;
|
|
4
4
|
const class_transformer_1 = require("class-transformer");
|
|
5
5
|
const class_validator_1 = require("class-validator");
|
|
6
6
|
const create_errors_1 = require("./create-errors");
|
|
@@ -15,10 +15,8 @@ function createAPI(ParamsClass, execution) {
|
|
|
15
15
|
: (0, class_transformer_1.plainToClass)(ParamsClass, params);
|
|
16
16
|
const errors = await (0, class_validator_1.validate)(paramsToCheck || {});
|
|
17
17
|
if (errors?.length) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
});
|
|
21
|
-
throw new create_errors_1.CommonErrors.InvalidParameter.ValidationError(messages.join(" "));
|
|
18
|
+
const errorMessages = errors.flatMap((error) => getMessage(error));
|
|
19
|
+
throw new create_errors_1.CommonErrors.InvalidParameter.ValidationError(errorMessages.join("\n"));
|
|
22
20
|
}
|
|
23
21
|
// 执行函数
|
|
24
22
|
return await execution(params, ctx);
|
|
@@ -30,3 +28,26 @@ function implementAPI(_actionStub, ParamsClass, execution) {
|
|
|
30
28
|
return createAPI(ParamsClass, execution);
|
|
31
29
|
}
|
|
32
30
|
exports.implementAPI = implementAPI;
|
|
31
|
+
/**
|
|
32
|
+
* 解析错误信息
|
|
33
|
+
*/
|
|
34
|
+
function getMessage(message, propertyPath = "") {
|
|
35
|
+
const messages = [];
|
|
36
|
+
// 构建当前属性的完整路径
|
|
37
|
+
const currentPropertyPath = propertyPath
|
|
38
|
+
? `${propertyPath}.${message.property}`
|
|
39
|
+
: message.property;
|
|
40
|
+
if (message.constraints) {
|
|
41
|
+
// 将所有约束错误合并为一个字符串
|
|
42
|
+
const constraintErrors = Object.values(message.constraints).join(", ");
|
|
43
|
+
messages.push(`${currentPropertyPath}: ${constraintErrors}`);
|
|
44
|
+
}
|
|
45
|
+
if (message.children && message.children.length > 0) {
|
|
46
|
+
// 递归处理子错误,并传递更新后的属性路径
|
|
47
|
+
for (const [_index, child] of message.children.entries()) {
|
|
48
|
+
messages.push(...getMessage(child, currentPropertyPath));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return messages;
|
|
52
|
+
}
|
|
53
|
+
exports.getMessage = getMessage;
|
|
@@ -21,6 +21,7 @@ declare const CommonErrorsMap: {
|
|
|
21
21
|
readonly EmptyParameter: "请求参数不能为空";
|
|
22
22
|
readonly EmptyAPIRequest: "未指定API的请求";
|
|
23
23
|
readonly ValidationError: "参数校验失败";
|
|
24
|
+
readonly RouteError: "路由错误";
|
|
24
25
|
};
|
|
25
26
|
readonly ResourceNotFound: {
|
|
26
27
|
readonly APINotFound: "不存在的API";
|
|
@@ -77,6 +78,7 @@ declare function createErrorsClasses<T extends MessageMap = MessageMap>(messageM
|
|
|
77
78
|
readonly EmptyParameter: "请求参数不能为空";
|
|
78
79
|
readonly EmptyAPIRequest: "未指定API的请求";
|
|
79
80
|
readonly ValidationError: "参数校验失败";
|
|
81
|
+
readonly RouteError: "路由错误";
|
|
80
82
|
};
|
|
81
83
|
readonly ResourceNotFound: {
|
|
82
84
|
readonly APINotFound: "不存在的API";
|
|
@@ -102,6 +104,7 @@ export declare const CommonErrors: ErrorClasses<MergeMessageMap<{
|
|
|
102
104
|
readonly EmptyParameter: "请求参数不能为空";
|
|
103
105
|
readonly EmptyAPIRequest: "未指定API的请求";
|
|
104
106
|
readonly ValidationError: "参数校验失败";
|
|
107
|
+
readonly RouteError: "路由错误";
|
|
105
108
|
};
|
|
106
109
|
readonly ResourceNotFound: {
|
|
107
110
|
readonly APINotFound: "不存在的API";
|
|
@@ -1,10 +1,35 @@
|
|
|
1
|
+
import express from "express";
|
|
1
2
|
import { AuthFunction } from "./api-middleware";
|
|
2
3
|
import { APIs } from "./create-api";
|
|
4
|
+
import { SseHandlers } from "./create-sse";
|
|
3
5
|
export interface ICreateServerParams {
|
|
4
6
|
/**
|
|
5
7
|
* API列表
|
|
6
8
|
*/
|
|
7
9
|
apis: APIs;
|
|
10
|
+
/**
|
|
11
|
+
* SSE配置
|
|
12
|
+
*/
|
|
13
|
+
sse?: {
|
|
14
|
+
/**
|
|
15
|
+
* SSE API列表
|
|
16
|
+
*/
|
|
17
|
+
handlers: SseHandlers;
|
|
18
|
+
/**
|
|
19
|
+
* SSE 监听的路由,例如:`/sse/generate`
|
|
20
|
+
*
|
|
21
|
+
* 注意:路由要以 `/` 开头
|
|
22
|
+
*
|
|
23
|
+
* 默认为:`/sse`
|
|
24
|
+
*/
|
|
25
|
+
route?: string;
|
|
26
|
+
/**
|
|
27
|
+
* 超时时间(单位:毫秒)
|
|
28
|
+
*
|
|
29
|
+
* @description 从第一次推送消息时开始计时,每次推送消息会重置超时时间
|
|
30
|
+
*/
|
|
31
|
+
timeout?: number;
|
|
32
|
+
};
|
|
8
33
|
/**
|
|
9
34
|
* 鉴权函数
|
|
10
35
|
*
|
|
@@ -15,6 +40,31 @@ export interface ICreateServerParams {
|
|
|
15
40
|
* 默认请求日志,true开启,false关闭
|
|
16
41
|
*/
|
|
17
42
|
log?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 自定义中间件列表
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
*
|
|
48
|
+
* ```
|
|
49
|
+
* import cors from "cors"; // 引入cors中间件
|
|
50
|
+
*
|
|
51
|
+
* ...其他代码...
|
|
52
|
+
*
|
|
53
|
+
* const server = createServer(
|
|
54
|
+
* {
|
|
55
|
+
* apis: myAPIs,
|
|
56
|
+
* authFn: myAuthFunction,
|
|
57
|
+
* log: true,
|
|
58
|
+
* middlewares: [cors()], // 传入cors中间件
|
|
59
|
+
* },
|
|
60
|
+
* {
|
|
61
|
+
* limit: "5mb",
|
|
62
|
+
* }
|
|
63
|
+
*);
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
middlewares?: express.RequestHandler[];
|
|
18
68
|
}
|
|
19
69
|
export interface ICreateServerOptions {
|
|
20
70
|
limit?: string | number | undefined;
|
|
@@ -26,16 +26,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
exports.createServer = void 0;
|
|
27
27
|
const express_1 = __importStar(require("express"));
|
|
28
28
|
const api_middleware_1 = require("./api-middleware");
|
|
29
|
+
const sse_middleware_1 = require("./sse-middleware");
|
|
29
30
|
/**
|
|
30
31
|
* 创建 Server
|
|
31
32
|
*/
|
|
32
33
|
function createServer(params, options) {
|
|
33
|
-
const { apis, authFn, log } = params || {};
|
|
34
|
+
const { apis, sse, authFn, log, middlewares = [] } = params || {};
|
|
34
35
|
const { limit = "10mb" } = options || {};
|
|
36
|
+
const { handlers, ...resetSSE } = sse || {};
|
|
35
37
|
const app = (0, express_1.default)();
|
|
36
38
|
// POST 参数获取
|
|
37
39
|
app.use((0, express_1.urlencoded)({ extended: true, limit }));
|
|
38
40
|
app.use((0, express_1.json)({ limit }));
|
|
41
|
+
// 使用自定义中间件
|
|
42
|
+
middlewares.forEach((middleware) => app.use(middleware));
|
|
43
|
+
// 注入SSE
|
|
44
|
+
handlers && app.use((0, sse_middleware_1.packSSE)(handlers, { log, ...resetSSE }));
|
|
39
45
|
// 注入API
|
|
40
46
|
app.use((0, api_middleware_1.packAPI)(apis, { authFn, log }));
|
|
41
47
|
return app;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Class } from "utility-types";
|
|
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
|
+
};
|
|
12
|
+
export type API<P, R> = (param: P) => Promise<R>;
|
|
13
|
+
export type SseExecution<P, R, A> = (
|
|
14
|
+
/** 请求入参 */
|
|
15
|
+
params: P,
|
|
16
|
+
/** SSE相关操作函数 */
|
|
17
|
+
sse: SSEHandlers,
|
|
18
|
+
/** 请求上下文 */
|
|
19
|
+
ctx: ServerContext<A>) => Promise<R>;
|
|
20
|
+
export type AnySseExecution = SseExecution<any, any, any>;
|
|
21
|
+
export type SseHandlers = Record<string, AnySseExecution>;
|
|
22
|
+
export declare function createSseAPI<P, R, A>(ParamsClass: Class<P>, execution: SseExecution<P, R, A>): API<P, R>;
|
|
23
|
+
type StubParam<T> = T extends (params: infer P) => any ? P : never;
|
|
24
|
+
export declare function implementSseAPI<F extends (params: any) => any, A = Record<string, any>>(_actionStub: F, ParamsClass: Class<StubParam<F>>, execution: SseExecution<StubParam<F>, ReturnType<F>, A>): API<StubParam<F>, ReturnType<F>>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.implementSseAPI = exports.createSseAPI = void 0;
|
|
4
|
+
const class_transformer_1 = require("class-transformer");
|
|
5
|
+
const class_validator_1 = require("class-validator");
|
|
6
|
+
const create_errors_1 = require("./create-errors");
|
|
7
|
+
const create_api_1 = require("./create-api");
|
|
8
|
+
function createSseAPI(ParamsClass, execution) {
|
|
9
|
+
const runtime = async (params, sse, ctx) => {
|
|
10
|
+
if (!params) {
|
|
11
|
+
throw new create_errors_1.CommonErrors.InvalidParameter.EmptyParameter();
|
|
12
|
+
}
|
|
13
|
+
// 校验参数
|
|
14
|
+
const paramsToCheck = params instanceof ParamsClass
|
|
15
|
+
? params
|
|
16
|
+
: (0, class_transformer_1.plainToClass)(ParamsClass, params);
|
|
17
|
+
const errors = await (0, class_validator_1.validate)(paramsToCheck || {});
|
|
18
|
+
if (errors?.length) {
|
|
19
|
+
const errorMessages = errors.flatMap((error) => (0, create_api_1.getMessage)(error));
|
|
20
|
+
throw new create_errors_1.CommonErrors.InvalidParameter.ValidationError(errorMessages.join("\n"));
|
|
21
|
+
}
|
|
22
|
+
// 执行函数
|
|
23
|
+
return await execution(params, sse, ctx);
|
|
24
|
+
};
|
|
25
|
+
return runtime;
|
|
26
|
+
}
|
|
27
|
+
exports.createSseAPI = createSseAPI;
|
|
28
|
+
function implementSseAPI(_actionStub, ParamsClass, execution) {
|
|
29
|
+
return createSseAPI(ParamsClass, execution);
|
|
30
|
+
}
|
|
31
|
+
exports.implementSseAPI = implementSseAPI;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { AuthFunction } from "./api-middleware";
|
|
3
|
+
import { SseHandlers } from "./create-sse";
|
|
4
|
+
interface IPackSSEOptions {
|
|
5
|
+
authFn?: AuthFunction;
|
|
6
|
+
route?: string;
|
|
7
|
+
log?: boolean;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const packSSE: (sseHandlers: SseHandlers, options?: IPackSSEOptions) => express.RequestHandler;
|
|
11
|
+
export declare const createSSEMsg: (event: string, message: string) => string;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSSEMsg = exports.packSSE = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const logger_1 = require("../helper/logger");
|
|
6
|
+
const create_errors_1 = require("./create-errors");
|
|
7
|
+
const short_id_1 = require("../helper/short-id");
|
|
8
|
+
const packSSE = (sseHandlers, options) => {
|
|
9
|
+
const { authFn, log = true, route = "/sse", timeout = 1000 * 30, } = options || {};
|
|
10
|
+
return async (req, res, next) => {
|
|
11
|
+
// 生成SSE API映射
|
|
12
|
+
const sseMap = new Map(Object.entries(sseHandlers).map(([action, execution]) => [
|
|
13
|
+
action,
|
|
14
|
+
execution,
|
|
15
|
+
]));
|
|
16
|
+
// 检查SSE路由
|
|
17
|
+
const isValidRoute = route.startsWith("/");
|
|
18
|
+
if (!isValidRoute) {
|
|
19
|
+
throw new create_errors_1.CommonErrors.InvalidParameter.RouteError();
|
|
20
|
+
}
|
|
21
|
+
const { path } = req;
|
|
22
|
+
// 非SSE的请求直接放过
|
|
23
|
+
if (path !== route) {
|
|
24
|
+
next();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// 请求开始时间
|
|
28
|
+
const start = Date.now();
|
|
29
|
+
let took = 0;
|
|
30
|
+
// 生成请求ID
|
|
31
|
+
const requestId = (0, uuid_1.v4)();
|
|
32
|
+
// 上下文
|
|
33
|
+
const ctx = {
|
|
34
|
+
RequestId: requestId,
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
// API 解析
|
|
38
|
+
const { Action, ...params } = req.body || {};
|
|
39
|
+
if (log) {
|
|
40
|
+
logger_1.logger.info(`请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
|
|
41
|
+
}
|
|
42
|
+
// 接口未定义
|
|
43
|
+
if (!Action) {
|
|
44
|
+
throw new create_errors_1.CommonErrors.InvalidParameter.EmptyAPIRequest();
|
|
45
|
+
}
|
|
46
|
+
// 处理鉴权函数
|
|
47
|
+
if (authFn !== null && authFn !== undefined) {
|
|
48
|
+
if (typeof authFn !== "function") {
|
|
49
|
+
throw new create_errors_1.CommonErrors.ResourceNotFound.AuthFunctionNotFound();
|
|
50
|
+
}
|
|
51
|
+
const authResult = await authFn(Action, req);
|
|
52
|
+
if (!authResult) {
|
|
53
|
+
throw new create_errors_1.CommonErrors.FailOperation.NoPermission();
|
|
54
|
+
}
|
|
55
|
+
if (typeof authResult !== "boolean") {
|
|
56
|
+
// 把权限信息写入上下文
|
|
57
|
+
ctx.AuthInfo = authResult || {};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// API 处理
|
|
61
|
+
const execution = sseMap.get(Action);
|
|
62
|
+
if (typeof execution !== "function") {
|
|
63
|
+
throw new create_errors_1.CommonErrors.ResourceNotFound.APINotFound();
|
|
64
|
+
}
|
|
65
|
+
const abortController = new AbortController();
|
|
66
|
+
let timeoutId;
|
|
67
|
+
// 连接断开
|
|
68
|
+
let isConnected = true;
|
|
69
|
+
res.on("close", () => {
|
|
70
|
+
if (timeoutId)
|
|
71
|
+
clearTimeout(timeoutId);
|
|
72
|
+
isConnected = false;
|
|
73
|
+
abortController.abort();
|
|
74
|
+
// 完成响应
|
|
75
|
+
took = Date.now() - start;
|
|
76
|
+
logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
|
|
77
|
+
return res.end();
|
|
78
|
+
});
|
|
79
|
+
// 写头
|
|
80
|
+
res.writeHead(200, {
|
|
81
|
+
"Content-Type": "text/event-stream",
|
|
82
|
+
"Cache-Control": "no-cache",
|
|
83
|
+
Connection: "keep-alive",
|
|
84
|
+
});
|
|
85
|
+
// 初始化超时定时器
|
|
86
|
+
const resetTimeout = () => {
|
|
87
|
+
if (timeoutId)
|
|
88
|
+
clearTimeout(timeoutId);
|
|
89
|
+
timeoutId = setTimeout(() => {
|
|
90
|
+
logger_1.logger.info(`超时自动关闭:${requestId}`);
|
|
91
|
+
close();
|
|
92
|
+
}, timeout);
|
|
93
|
+
};
|
|
94
|
+
// 推送消息
|
|
95
|
+
const push = (data) => {
|
|
96
|
+
if (!isConnected) {
|
|
97
|
+
logger_1.logger.warning(`连接已关闭: ${requestId}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
resetTimeout();
|
|
101
|
+
const msg = createResponseMsg(data);
|
|
102
|
+
const response = (0, exports.createSSEMsg)("message", msg);
|
|
103
|
+
try {
|
|
104
|
+
res.write(response);
|
|
105
|
+
logger_1.logger.info(`推送数据: ${response}`);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error.code === "EPIPE" || error.code === "ECONNRESET") {
|
|
109
|
+
logger_1.logger.warning(`连接已关闭: ${requestId}`);
|
|
110
|
+
isConnected = false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
// 主动关闭
|
|
115
|
+
const close = () => {
|
|
116
|
+
if (!isConnected)
|
|
117
|
+
return;
|
|
118
|
+
logger_1.logger.info(`主动关闭连接: ${requestId}`);
|
|
119
|
+
const response = (0, exports.createSSEMsg)("done", "[DONE]");
|
|
120
|
+
res.write(response, () => res.end());
|
|
121
|
+
};
|
|
122
|
+
await execution(params, { push, close, abortController }, ctx);
|
|
123
|
+
}
|
|
124
|
+
catch (rawError) {
|
|
125
|
+
logger_1.logger.error(rawError);
|
|
126
|
+
if (!res.headersSent) {
|
|
127
|
+
// 写头
|
|
128
|
+
res.writeHead(200, {
|
|
129
|
+
"Content-Type": "text/event-stream",
|
|
130
|
+
"Cache-Control": "no-cache",
|
|
131
|
+
Connection: "keep-alive",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// 未知错误
|
|
135
|
+
let error = new create_errors_1.CommonErrors.InternalError.UnknownError();
|
|
136
|
+
// 可控错误
|
|
137
|
+
if (rawError instanceof create_errors_1.BaseError) {
|
|
138
|
+
error = rawError;
|
|
139
|
+
}
|
|
140
|
+
// DB错误
|
|
141
|
+
if (rawError?.sql) {
|
|
142
|
+
error = new create_errors_1.CommonErrors.InternalError.DatabaseError();
|
|
143
|
+
}
|
|
144
|
+
const errResponse = {
|
|
145
|
+
Response: {
|
|
146
|
+
Error: { Code: error.code, Message: error.message },
|
|
147
|
+
RequestId: requestId,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
const response = (0, exports.createSSEMsg)("error", JSON.stringify(errResponse));
|
|
151
|
+
logger_1.logger.info(`发送消息:\n${response}`);
|
|
152
|
+
res.write(response, () => res.end());
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
exports.packSSE = packSSE;
|
|
157
|
+
const createSSEMsg = (event, message) => {
|
|
158
|
+
const id = (0, short_id_1.shortId)();
|
|
159
|
+
const idStr = `id: ${id}\n`;
|
|
160
|
+
const eventStr = `event: ${event}\n`;
|
|
161
|
+
const dataStr = `data: ${message}\n\n`;
|
|
162
|
+
return `${idStr}${eventStr}${dataStr}`;
|
|
163
|
+
};
|
|
164
|
+
exports.createSSEMsg = createSSEMsg;
|
|
165
|
+
const createResponseMsg = (data, requestId) => {
|
|
166
|
+
const msg = {
|
|
167
|
+
Response: {
|
|
168
|
+
RequestId: requestId || (0, uuid_1.v4)(),
|
|
169
|
+
Data: data,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
return JSON.stringify(msg);
|
|
173
|
+
};
|
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
|
-
|
|
11
|
-
|
|
12
|
-
console.log(`[${level}] - ${logTime} - ${JSON.stringify(message)}`);
|
|
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`);
|
|
10
|
+
if (typeof message === "string") {
|
|
11
|
+
console.log(`[${level}] - [${logTime}] - ${message}`);
|
|
13
12
|
}
|
|
14
13
|
else {
|
|
15
|
-
console.log(`[${level}] - ${logTime} -
|
|
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 };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const shortId: () => string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shortId = void 0;
|
|
4
|
+
const nanoid_1 = require("nanoid");
|
|
5
|
+
const nanoid = (0, nanoid_1.customAlphabet)("123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", 10);
|
|
6
|
+
const shortId = () => nanoid();
|
|
7
|
+
exports.shortId = shortId;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sleep: (ms: number) => Promise<unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { createServer } from "./common/create-server";
|
|
2
2
|
export { createErrors } from "./common/create-errors";
|
|
3
|
-
export { implementAPI } from "./common/create-api";
|
|
3
|
+
export { implementAPI, ServerContext } from "./common/create-api";
|
|
4
|
+
export { implementSseAPI } from "./common/create-sse";
|
|
5
|
+
export { callService } from "./common/call-service";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.implementAPI = exports.createErrors = exports.createServer = void 0;
|
|
3
|
+
exports.callService = exports.implementSseAPI = exports.implementAPI = exports.createErrors = exports.createServer = void 0;
|
|
4
4
|
var create_server_1 = require("./common/create-server");
|
|
5
5
|
Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return create_server_1.createServer; } });
|
|
6
6
|
var create_errors_1 = require("./common/create-errors");
|
|
7
7
|
Object.defineProperty(exports, "createErrors", { enumerable: true, get: function () { return create_errors_1.createErrors; } });
|
|
8
8
|
var create_api_1 = require("./common/create-api");
|
|
9
9
|
Object.defineProperty(exports, "implementAPI", { enumerable: true, get: function () { return create_api_1.implementAPI; } });
|
|
10
|
+
var create_sse_1 = require("./common/create-sse");
|
|
11
|
+
Object.defineProperty(exports, "implementSseAPI", { enumerable: true, get: function () { return create_sse_1.implementSseAPI; } });
|
|
12
|
+
var call_service_1 = require("./common/call-service");
|
|
13
|
+
Object.defineProperty(exports, "callService", { enumerable: true, get: function () { return call_service_1.callService; } });
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kb-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-beta.1",
|
|
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,14 +16,19 @@
|
|
|
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
|
-
"
|
|
31
|
+
"nanoid": "^3.3.9",
|
|
27
32
|
"utility-types": "^3.11.0",
|
|
28
33
|
"uuid": "^11.0.3"
|
|
29
34
|
},
|