starlight-server 1.2.0 → 1.5.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.md +11 -1
- package/dist/demo/index.js +41 -27
- package/dist/http/cors.d.ts +33 -0
- package/dist/http/cors.js +33 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/mime-types.js +1 -20
- package/dist/http/request.d.ts +18 -2
- package/dist/http/request.js +28 -7
- package/dist/http/response.d.ts +9 -5
- package/dist/http/response.js +16 -8
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -2
- package/dist/logging.d.ts +4 -3
- package/dist/logging.js +7 -19
- package/dist/router/helpers.d.ts +6 -0
- package/dist/router/helpers.js +24 -0
- package/dist/router/index.d.ts +67 -38
- package/dist/router/index.js +107 -36
- package/dist/router/match-path.d.ts +13 -0
- package/dist/router/match-path.js +160 -0
- package/dist/swagger/factories.d.ts +143 -0
- package/dist/swagger/factories.js +231 -0
- package/dist/swagger/index.d.ts +54 -6
- package/dist/swagger/index.js +94 -58
- package/dist/swagger/specification.d.ts +802 -0
- package/dist/swagger/specification.js +130 -0
- package/package.json +10 -10
- package/src/demo/index.ts +45 -42
- package/src/http/cors.ts +59 -0
- package/src/http/index.ts +1 -0
- package/src/http/mime-types.ts +1 -20
- package/src/http/request.ts +38 -10
- package/src/http/response.ts +18 -17
- package/src/http/server.ts +2 -2
- package/src/index.ts +3 -2
- package/src/logging.ts +7 -21
- package/src/router/helpers.ts +27 -0
- package/src/router/index.ts +141 -38
- package/src/router/match-path.ts +178 -0
- package/src/swagger/factories.ts +345 -0
- package/src/swagger/index.ts +113 -80
- package/src/swagger/specification.ts +823 -0
- package/dist/router/cors.d.ts +0 -24
- package/dist/router/cors.js +0 -35
- package/dist/router/match.d.ts +0 -23
- package/dist/router/match.js +0 -172
- package/dist/router/parameters.d.ts +0 -50
- package/dist/router/parameters.js +0 -118
- package/dist/router/router.d.ts +0 -128
- package/dist/router/router.js +0 -97
- package/dist/swagger/factory.d.ts +0 -97
- package/dist/swagger/factory.js +0 -144
- package/dist/swagger/openapi-spec.d.ts +0 -261
- package/dist/swagger/openapi-spec.js +0 -5
- package/dist/validators/array.d.ts +0 -9
- package/dist/validators/array.js +0 -28
- package/dist/validators/boolean.d.ts +0 -4
- package/dist/validators/boolean.js +0 -28
- package/dist/validators/common.d.ts +0 -23
- package/dist/validators/common.js +0 -25
- package/dist/validators/index.d.ts +0 -20
- package/dist/validators/index.js +0 -38
- package/dist/validators/number.d.ts +0 -10
- package/dist/validators/number.js +0 -30
- package/dist/validators/object.d.ts +0 -13
- package/dist/validators/object.js +0 -36
- package/dist/validators/string.d.ts +0 -11
- package/dist/validators/string.js +0 -29
- package/src/.DS_Store +0 -0
- package/src/router/cors.ts +0 -54
- package/src/router/match.ts +0 -194
- package/src/router/parameters.ts +0 -175
- package/src/router/router.ts +0 -234
- package/src/swagger/factory.ts +0 -169
- package/src/swagger/openapi-spec.ts +0 -312
- package/src/validators/array.ts +0 -33
- package/src/validators/boolean.ts +0 -23
- package/src/validators/common.ts +0 -46
- package/src/validators/index.ts +0 -50
- package/src/validators/number.ts +0 -36
- package/src/validators/object.ts +0 -41
- package/src/validators/string.ts +0 -38
package/README.md
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
# Starlight Server:
|
|
1
|
+
# Starlight Server: 功能简约、结构清晰的 HTTP 服务框架
|
|
2
|
+
|
|
3
|
+
TODO:
|
|
4
|
+
https://github.com/pillarjs/path-to-regexp/blob/master/src/index.ts
|
|
5
|
+
https://github.com/expressjs/multer
|
|
6
|
+
|
|
7
|
+
此框架专用于实现 HTTP 服务。它由几个独立模块组成:
|
|
8
|
+
|
|
9
|
+
- http
|
|
10
|
+
- router
|
|
11
|
+
- swagger
|
package/dist/demo/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { getFileDir } from '@anjianshi/utils/env-node/index.js';
|
|
3
|
-
import { getLogger, startHTTPServer, Router
|
|
4
|
-
import { registerSwaggerRoute } from '../swagger/index.js';
|
|
3
|
+
import { getLogger, startHTTPServer, Router } from '../index.js';
|
|
5
4
|
const logger = getLogger({
|
|
6
5
|
level: 'debug',
|
|
7
6
|
debugLib: '*',
|
|
@@ -9,43 +8,58 @@ const logger = getLogger({
|
|
|
9
8
|
dir: path.resolve(getFileDir(import.meta), '../../logs'),
|
|
10
9
|
},
|
|
11
10
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
// declare module '../index.js' {
|
|
12
|
+
// interface Context {
|
|
13
|
+
// now: () => number
|
|
14
|
+
// }
|
|
15
|
+
// }
|
|
16
|
+
const router = new Router();
|
|
17
|
+
router.setCors(true);
|
|
18
|
+
router.bindSwagger();
|
|
19
|
+
router.setExecutor(async (basicContext, route) => {
|
|
20
|
+
await route.handler({
|
|
21
|
+
...basicContext,
|
|
22
|
+
// now: () => Date.now(),
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
const swagger = router.swagger;
|
|
26
|
+
swagger.registerResponse('hello', swagger.response({
|
|
27
|
+
hello: swagger.string(),
|
|
28
|
+
}));
|
|
23
29
|
router.register({
|
|
24
|
-
category: 'demo',
|
|
25
|
-
description: 'hello world',
|
|
26
30
|
method: 'GET',
|
|
27
31
|
path: '/hello',
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
document: {
|
|
33
|
+
category: 'demo',
|
|
34
|
+
description: 'hello world',
|
|
35
|
+
query: {
|
|
36
|
+
q1: { schema: swagger.number(), required: true },
|
|
37
|
+
q2: { schema: swagger.number(), description: '这是q2' },
|
|
38
|
+
q3: swagger.boolean(),
|
|
39
|
+
},
|
|
40
|
+
body: {
|
|
41
|
+
abc: swagger.number(),
|
|
42
|
+
def: swagger.array(swagger.string()),
|
|
43
|
+
},
|
|
44
|
+
response: {
|
|
45
|
+
name: swagger.string(),
|
|
46
|
+
},
|
|
34
47
|
},
|
|
35
|
-
handler(
|
|
36
|
-
response.json({
|
|
48
|
+
handler(ctx) {
|
|
49
|
+
ctx.response.json({ name: 'world' });
|
|
37
50
|
},
|
|
38
51
|
});
|
|
39
52
|
router.register({
|
|
40
|
-
category: 'demo',
|
|
41
53
|
method: 'POST',
|
|
42
54
|
path: '/hello',
|
|
43
|
-
|
|
55
|
+
document: {
|
|
56
|
+
category: 'demo',
|
|
57
|
+
response: swagger.ref('response', 'hello'),
|
|
58
|
+
},
|
|
44
59
|
handler({ response }) {
|
|
45
|
-
response.json({ hello: 'world
|
|
60
|
+
response.json({ hello: 'world' });
|
|
46
61
|
},
|
|
47
62
|
});
|
|
48
|
-
registerSwaggerRoute(router);
|
|
49
63
|
startHTTPServer({
|
|
50
64
|
handler: router.handle,
|
|
51
65
|
logger: logger.getChild('http'),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS 处理逻辑
|
|
3
|
+
*/
|
|
4
|
+
import { type Request } from './request.js';
|
|
5
|
+
import { ResponseUtils } from './response.js';
|
|
6
|
+
import { type NodeRequest, type NodeResponse } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* 判断是否是 CORS Preflight 请求
|
|
9
|
+
*/
|
|
10
|
+
export declare function isPreflight(request: Request | NodeRequest): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 若请求是 CORS Preflight 请求,返回客户端原本想请求的 Method
|
|
13
|
+
* 否则返回 null
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPreflightRequestMethod(request: Request | NodeRequest): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* CORS 放行规则
|
|
18
|
+
* - false:不放行
|
|
19
|
+
* - true:无条件放行(等同于 { allowOrigin: '*', allowHeaders: '*', exposeHeaders: '*' })
|
|
20
|
+
* - object:手动指定规则
|
|
21
|
+
* - 未指定的 key 不会输出对应 header
|
|
22
|
+
* - allowHeaders 仅对 Preflight 请求有效
|
|
23
|
+
* - exposeHeaders 仅对非 Preflight 请求有效
|
|
24
|
+
*/
|
|
25
|
+
export type CORSRule = boolean | {
|
|
26
|
+
allowOrigin?: string;
|
|
27
|
+
allowHeaders?: string;
|
|
28
|
+
exposeHeaders?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 输出 CORS 相关 headers
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleCORS(request: Request | NodeRequest, response: ResponseUtils | NodeResponse, rule: CORSRule): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ResponseUtils } from './response.js';
|
|
2
|
+
/**
|
|
3
|
+
* 判断是否是 CORS Preflight 请求
|
|
4
|
+
*/
|
|
5
|
+
export function isPreflight(request) {
|
|
6
|
+
return (request.method === 'OPTIONS' &&
|
|
7
|
+
typeof request.headers['access-control-request-method'] === 'string');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 若请求是 CORS Preflight 请求,返回客户端原本想请求的 Method
|
|
11
|
+
* 否则返回 null
|
|
12
|
+
*/
|
|
13
|
+
export function getPreflightRequestMethod(request) {
|
|
14
|
+
return isPreflight(request) ? request.headers['access-control-request-method'] ?? '' : null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 输出 CORS 相关 headers
|
|
18
|
+
*/
|
|
19
|
+
export function handleCORS(request, response, rule) {
|
|
20
|
+
// 不输出 CORS 相关 headers,浏览器会默认服务端不允许跨域请求
|
|
21
|
+
if (rule === false)
|
|
22
|
+
return;
|
|
23
|
+
if (rule === true)
|
|
24
|
+
rule = { allowOrigin: '*', allowHeaders: '*', exposeHeaders: '*' };
|
|
25
|
+
const nodeResponse = response instanceof ResponseUtils ? response.nodeResponse : response;
|
|
26
|
+
const requestIsPreflight = isPreflight(request);
|
|
27
|
+
if (rule.allowOrigin !== undefined)
|
|
28
|
+
nodeResponse.setHeader('Access-Control-Allow-Origin', rule.allowOrigin);
|
|
29
|
+
if (requestIsPreflight && rule.allowHeaders !== undefined)
|
|
30
|
+
nodeResponse.setHeader('Access-Control-Allow-Headers', rule.allowHeaders);
|
|
31
|
+
if (!requestIsPreflight && rule.exposeHeaders !== undefined)
|
|
32
|
+
nodeResponse.setHeader('Access-Control-Expose-Headers', rule.exposeHeaders);
|
|
33
|
+
}
|
package/dist/http/index.d.ts
CHANGED
package/dist/http/index.js
CHANGED
package/dist/http/mime-types.js
CHANGED
|
@@ -50,26 +50,7 @@ export const MIMETypes = [
|
|
|
50
50
|
{ type: 'application/mxf', extensions: ['.mxf'] },
|
|
51
51
|
{
|
|
52
52
|
type: 'application/octet-stream',
|
|
53
|
-
extensions: [
|
|
54
|
-
'.a',
|
|
55
|
-
'.bin',
|
|
56
|
-
'.bpk',
|
|
57
|
-
'.deploy',
|
|
58
|
-
'.dist',
|
|
59
|
-
'.distz',
|
|
60
|
-
'.dmg',
|
|
61
|
-
'.dms',
|
|
62
|
-
'.dump',
|
|
63
|
-
'.elc',
|
|
64
|
-
'.iso',
|
|
65
|
-
'.lha',
|
|
66
|
-
'.lrf',
|
|
67
|
-
'.lzh',
|
|
68
|
-
'.o',
|
|
69
|
-
'.obj',
|
|
70
|
-
'.pkg',
|
|
71
|
-
'.so',
|
|
72
|
-
],
|
|
53
|
+
extensions: ['.a', '.bin', '.bpk', '.deploy', '.dist', '.distz', '.dmg', '.dms', '.dump', '.elc', '.iso', '.lha', '.lrf', '.lzh', '.o', '.obj', '.pkg', '.so'], // prettier-ignore
|
|
73
54
|
},
|
|
74
55
|
{ type: 'application/oda', extensions: ['.oda'] },
|
|
75
56
|
{ type: 'application/oebps-package+xml', extensions: ['.opf'] },
|
package/dist/http/request.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import type http from 'node:http';
|
|
3
|
-
import { type Logger } from '
|
|
3
|
+
import { type Logger } from '../logging.js';
|
|
4
4
|
import { RequestBody, type BodyOptions } from './body/index.js';
|
|
5
5
|
import { type NodeRequest } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* 对 Node.js 请求内容的二次封装
|
|
8
|
+
*
|
|
8
9
|
* - 提供经过整理的请求信息
|
|
9
10
|
* - 自带 body 解析(实现了 “body 大小限制” 和 “JSON、form-data 等格式的内容解析”)
|
|
10
11
|
*
|
|
@@ -15,11 +16,26 @@ import { type NodeRequest } from './types.js';
|
|
|
15
16
|
export declare class Request {
|
|
16
17
|
readonly nodeRequest: NodeRequest;
|
|
17
18
|
protected readonly logger: Logger;
|
|
19
|
+
/** HTTP Method,始终为大写 */
|
|
18
20
|
readonly method: string;
|
|
21
|
+
/** 完整的请求 URL */
|
|
22
|
+
readonly url: string;
|
|
23
|
+
/** 域名及端口 */
|
|
19
24
|
readonly host: string;
|
|
25
|
+
/** 请求路径 */
|
|
20
26
|
readonly path: string;
|
|
21
|
-
|
|
27
|
+
/** 解析后的 query 对象 */
|
|
28
|
+
readonly query: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* headers 对象,以 Record<string, xxx> 形式访问,仅支持小写 key。
|
|
31
|
+
* (可使用 request.getHeader() 方法不区分大小写获取 header)
|
|
32
|
+
*/
|
|
22
33
|
readonly headers: http.IncomingHttpHeaders;
|
|
34
|
+
/** 请求体。出于性能考虑,调用具体解析方法时才执行解析。 */
|
|
23
35
|
readonly body: RequestBody;
|
|
24
36
|
constructor(nodeRequest: NodeRequest, logger: Logger, bodyOptions: BodyOptions);
|
|
37
|
+
/**
|
|
38
|
+
* 获取 HTTP Header,支持任意大小写形式:content-type / Content-Type
|
|
39
|
+
*/
|
|
40
|
+
getHeader<const K extends string>(key: K): http.IncomingHttpHeaders[Lowercase<K>];
|
|
25
41
|
}
|
package/dist/http/request.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { parseQuery } from '@anjianshi/utils';
|
|
1
2
|
import { RequestBody } from './body/index.js';
|
|
2
3
|
import { HTTPError } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* 对 Node.js 请求内容的二次封装
|
|
6
|
+
*
|
|
5
7
|
* - 提供经过整理的请求信息
|
|
6
8
|
* - 自带 body 解析(实现了 “body 大小限制” 和 “JSON、form-data 等格式的内容解析”)
|
|
7
9
|
*
|
|
@@ -12,29 +14,48 @@ import { HTTPError } from './types.js';
|
|
|
12
14
|
export class Request {
|
|
13
15
|
nodeRequest;
|
|
14
16
|
logger;
|
|
17
|
+
/** HTTP Method,始终为大写 */
|
|
15
18
|
method;
|
|
19
|
+
/** 完整的请求 URL */
|
|
20
|
+
url;
|
|
21
|
+
/** 域名及端口 */
|
|
16
22
|
host;
|
|
23
|
+
/** 请求路径 */
|
|
17
24
|
path;
|
|
25
|
+
/** 解析后的 query 对象 */
|
|
18
26
|
query;
|
|
27
|
+
/**
|
|
28
|
+
* headers 对象,以 Record<string, xxx> 形式访问,仅支持小写 key。
|
|
29
|
+
* (可使用 request.getHeader() 方法不区分大小写获取 header)
|
|
30
|
+
*/
|
|
19
31
|
headers;
|
|
32
|
+
/** 请求体。出于性能考虑,调用具体解析方法时才执行解析。 */
|
|
20
33
|
body;
|
|
21
34
|
constructor(nodeRequest, logger, bodyOptions) {
|
|
22
35
|
this.nodeRequest = nodeRequest;
|
|
23
36
|
this.logger = logger;
|
|
24
37
|
if (nodeRequest.method === undefined)
|
|
25
38
|
throw new HTTPError(405);
|
|
26
|
-
this.method = nodeRequest.method;
|
|
39
|
+
this.method = nodeRequest.method.toUpperCase();
|
|
27
40
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
41
|
+
// 参考:<https://nodejs.org/api/http.html#messageurl>
|
|
42
|
+
const urlObject = new URL(nodeRequest.url ?? '', `http://${nodeRequest.headers.host ?? ''}`);
|
|
43
|
+
this.url = urlObject.href;
|
|
44
|
+
this.host = urlObject.host;
|
|
45
|
+
this.path = urlObject.pathname;
|
|
32
46
|
}
|
|
33
47
|
catch (e) {
|
|
34
|
-
this.logger.warn(
|
|
35
|
-
throw new HTTPError(400, '
|
|
48
|
+
this.logger.warn(`parse URL failed:${nodeRequest.url}`, e);
|
|
49
|
+
throw new HTTPError(400, 'URL invalid');
|
|
36
50
|
}
|
|
51
|
+
this.query = parseQuery(this.url);
|
|
37
52
|
this.headers = nodeRequest.headers;
|
|
38
53
|
this.body = new RequestBody(nodeRequest, bodyOptions);
|
|
39
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* 获取 HTTP Header,支持任意大小写形式:content-type / Content-Type
|
|
57
|
+
*/
|
|
58
|
+
getHeader(key) {
|
|
59
|
+
return this.headers[key.toLowerCase()];
|
|
60
|
+
}
|
|
40
61
|
}
|
package/dist/http/response.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { type Logger } from '
|
|
2
|
+
import { type Logger } from '../logging.js';
|
|
3
3
|
import { type NodeResponse } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* 封装响应内容输出函数
|
|
@@ -11,10 +11,6 @@ export declare class ResponseUtils {
|
|
|
11
11
|
constructor(nodeResponse: NodeResponse, logger: Logger);
|
|
12
12
|
header(name: string, value: string): void;
|
|
13
13
|
headers(...items: [string, string][]): void;
|
|
14
|
-
/**
|
|
15
|
-
* Output CORS Headers
|
|
16
|
-
*/
|
|
17
|
-
cors(allowOrigin?: string, allowHeaders?: string): void;
|
|
18
14
|
text(content: string | Buffer): void;
|
|
19
15
|
/**
|
|
20
16
|
* Output JSON
|
|
@@ -27,6 +23,14 @@ export declare class ResponseUtils {
|
|
|
27
23
|
file(content: Buffer | string, path: string): void;
|
|
28
24
|
/**
|
|
29
25
|
* Output HTTP Error
|
|
26
|
+
*
|
|
27
|
+
* response.error(new HTTPError(xxx)) // 传入 HTTP Error
|
|
28
|
+
* response.error(404) // 传入 HTTP Status
|
|
29
|
+
* response.error(xxx) // 传入其他内容,会记录下日志并以 500 状态结束请求
|
|
30
30
|
*/
|
|
31
31
|
error(error: unknown): void;
|
|
32
|
+
/**
|
|
33
|
+
* 执行重定向
|
|
34
|
+
*/
|
|
35
|
+
redirect(url: string, permanent?: boolean): void;
|
|
32
36
|
}
|
package/dist/http/response.js
CHANGED
|
@@ -17,12 +17,6 @@ export class ResponseUtils {
|
|
|
17
17
|
headers(...items) {
|
|
18
18
|
items.forEach(([name, value]) => this.header(name, value));
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Output CORS Headers
|
|
22
|
-
*/
|
|
23
|
-
cors(allowOrigin = '*', allowHeaders = '*') {
|
|
24
|
-
this.headers(['Access-Control-Allow-Origin', allowOrigin], ['Access-Control-Allow-Headers', allowHeaders]);
|
|
25
|
-
}
|
|
26
20
|
text(content) {
|
|
27
21
|
this.nodeResponse.end(content);
|
|
28
22
|
}
|
|
@@ -51,16 +45,30 @@ export class ResponseUtils {
|
|
|
51
45
|
}
|
|
52
46
|
/**
|
|
53
47
|
* Output HTTP Error
|
|
48
|
+
*
|
|
49
|
+
* response.error(new HTTPError(xxx)) // 传入 HTTP Error
|
|
50
|
+
* response.error(404) // 传入 HTTP Status
|
|
51
|
+
* response.error(xxx) // 传入其他内容,会记录下日志并以 500 状态结束请求
|
|
54
52
|
*/
|
|
55
53
|
error(error) {
|
|
56
54
|
if (error instanceof HTTPError) {
|
|
57
55
|
this.nodeResponse.statusCode = error.status; // eslint-disable-line require-atomic-updates
|
|
58
56
|
this.nodeResponse.end(error.message);
|
|
59
57
|
}
|
|
58
|
+
else if (typeof error === 'number') {
|
|
59
|
+
this.error(new HTTPError(error));
|
|
60
|
+
}
|
|
60
61
|
else {
|
|
61
62
|
this.logger.error(error);
|
|
62
|
-
this.
|
|
63
|
-
this.nodeResponse.end(new HTTPError(500).message);
|
|
63
|
+
this.error(new HTTPError(500));
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* 执行重定向
|
|
68
|
+
*/
|
|
69
|
+
redirect(url, permanent = false) {
|
|
70
|
+
this.nodeResponse.statusCode = permanent ? 301 : 302;
|
|
71
|
+
this.header('Location', url);
|
|
72
|
+
this.nodeResponse.end();
|
|
73
|
+
}
|
|
66
74
|
}
|
package/dist/http/server.d.ts
CHANGED
package/dist/http/server.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
export * from './logging.js';
|
|
1
2
|
export * from './http/index.js';
|
|
2
3
|
export * from './router/index.js';
|
|
3
|
-
export * from './
|
|
4
|
-
|
|
4
|
+
export * from './swagger/index.js';
|
|
5
|
+
export { validators } from '@anjianshi/utils/validators/index.js';
|
package/dist/logging.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 实现日志记录
|
|
3
3
|
*/
|
|
4
|
-
import { Logger } from '@anjianshi/utils';
|
|
5
|
-
import { type FileHandlerOptions } from '@anjianshi/utils/env-node/logging.js';
|
|
4
|
+
import { Logger, LogLevel } from '@anjianshi/utils';
|
|
5
|
+
import { type FileHandlerOptions } from '@anjianshi/utils/env-node/logging/index.js';
|
|
6
6
|
export type { FileHandlerOptions };
|
|
7
|
+
export { Logger, LogLevel };
|
|
7
8
|
export interface LoggingOptions {
|
|
8
9
|
/**
|
|
9
10
|
* 指定日志等级(debug info warn err)
|
|
10
11
|
* Specify log level.
|
|
11
12
|
*/
|
|
12
|
-
level?: string;
|
|
13
|
+
level?: string | LogLevel;
|
|
13
14
|
/**
|
|
14
15
|
* 文件日志参数
|
|
15
16
|
* File log options
|
package/dist/logging.js
CHANGED
|
@@ -2,29 +2,17 @@
|
|
|
2
2
|
* 实现日志记录
|
|
3
3
|
*/
|
|
4
4
|
import { truthy, Logger, LogLevel, adaptDebugLib } from '@anjianshi/utils';
|
|
5
|
-
import { ConsoleHandler, FileHandler, } from '@anjianshi/utils/env-node/logging.js';
|
|
5
|
+
import { ConsoleHandler, FileHandler, } from '@anjianshi/utils/env-node/logging/index.js';
|
|
6
6
|
import debug from 'debug';
|
|
7
|
-
|
|
8
|
-
debug: LogLevel.Debug,
|
|
9
|
-
info: LogLevel.Info,
|
|
10
|
-
warn: LogLevel.Warning,
|
|
11
|
-
warning: LogLevel.Warning,
|
|
12
|
-
err: LogLevel.Error,
|
|
13
|
-
error: LogLevel.Error,
|
|
14
|
-
};
|
|
7
|
+
export { Logger, LogLevel };
|
|
15
8
|
export function getLogger(options = {}) {
|
|
16
9
|
const logger = new Logger();
|
|
17
10
|
logger.addHandler(new ConsoleHandler());
|
|
18
|
-
if (options.level !== undefined)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
logger.setLevel(level);
|
|
22
|
-
}
|
|
23
|
-
if (options.file) {
|
|
11
|
+
if (options.level !== undefined)
|
|
12
|
+
logger.setLevel(options.level);
|
|
13
|
+
if (options.file)
|
|
24
14
|
logger.addHandler(new FileHandler(options.file));
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
void adaptDebugLib(debug, options.debugLib === true ? '*' : options.debugLib, logger);
|
|
28
|
-
}
|
|
15
|
+
if (truthy(options.debugLib))
|
|
16
|
+
adaptDebugLib(debug, options.debugLib === true ? '*' : options.debugLib, logger);
|
|
29
17
|
return logger;
|
|
30
18
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Validator, validators } from '@anjianshi/utils/validators/index.js';
|
|
2
|
+
import { type BasicContext } from './index.js';
|
|
3
|
+
export { validators };
|
|
4
|
+
export declare function validatePathParameters<T>(this: BasicContext, struct: Record<string, Validator>): T;
|
|
5
|
+
export declare function validateQuery<T>(this: BasicContext, struct: Record<string, Validator>): T;
|
|
6
|
+
export declare function validateBody<T>(this: BasicContext, struct: Record<string, Validator>): Promise<T>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { validators } from '@anjianshi/utils/validators/index.js';
|
|
2
|
+
import { HTTPError } from '../index.js';
|
|
3
|
+
export { validators };
|
|
4
|
+
export function validatePathParameters(struct) {
|
|
5
|
+
const result = validators.struct(struct).validate('path', this.pathParameters);
|
|
6
|
+
if (result.success)
|
|
7
|
+
return result.data;
|
|
8
|
+
throw new HTTPError(400, result.message);
|
|
9
|
+
}
|
|
10
|
+
export function validateQuery(struct) {
|
|
11
|
+
const result = validators.struct(struct).validate('query', this.request.query);
|
|
12
|
+
if (result.success)
|
|
13
|
+
return result.data;
|
|
14
|
+
throw new HTTPError(400, result.message);
|
|
15
|
+
}
|
|
16
|
+
export async function validateBody(struct) {
|
|
17
|
+
const body = await this.request.body.json();
|
|
18
|
+
if (typeof body !== 'object' || body === null || Array.isArray(body))
|
|
19
|
+
throw new HTTPError(400, 'Invalid JSON body, should be an object.');
|
|
20
|
+
const result = validators.struct(struct).validate('body', this.request.body);
|
|
21
|
+
if (result.success)
|
|
22
|
+
return result.data;
|
|
23
|
+
throw new HTTPError(400, result.message);
|
|
24
|
+
}
|
package/dist/router/index.d.ts
CHANGED
|
@@ -1,38 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
import { type CORSRule } from '../http/cors.js';
|
|
2
|
+
import { type Request, type ResponseUtils } from '../http/index.js';
|
|
3
|
+
import { Swagger, type Method } from '../swagger/index.js';
|
|
4
|
+
import * as helpers from './helpers.js';
|
|
5
|
+
import { type PathParameters } from './match-path.js';
|
|
6
|
+
export interface BasicContext {
|
|
7
|
+
request: Request;
|
|
8
|
+
response: ResponseUtils;
|
|
9
|
+
pathParameters: PathParameters;
|
|
10
|
+
validatePathParameters: typeof helpers.validatePathParameters;
|
|
11
|
+
validateQuery: typeof helpers.validateQuery;
|
|
12
|
+
validateBody: typeof helpers.validateBody;
|
|
13
|
+
}
|
|
14
|
+
/** 使用者可自行补充 Context 定义 */
|
|
15
|
+
export interface Context extends BasicContext {
|
|
16
|
+
}
|
|
17
|
+
export interface Route {
|
|
18
|
+
method: Method;
|
|
19
|
+
path: string;
|
|
20
|
+
handler: (context: Context) => void | Promise<void>;
|
|
21
|
+
/** 接口文档(需 router 绑定 Swagger 实例才生效) */
|
|
22
|
+
document?: Parameters<Swagger['registerOperation']>[2];
|
|
23
|
+
/** 此接口的 CORS 规则 */
|
|
24
|
+
cors?: CORSRule;
|
|
25
|
+
}
|
|
26
|
+
export declare class Router {
|
|
27
|
+
/**
|
|
28
|
+
* ----------------------
|
|
29
|
+
* 路由定义
|
|
30
|
+
* ----------------------
|
|
31
|
+
*/
|
|
32
|
+
readonly routes: Route[];
|
|
33
|
+
register(route: Route): void;
|
|
34
|
+
/**
|
|
35
|
+
* ----------------------
|
|
36
|
+
* 全局 CORS 配置
|
|
37
|
+
* ----------------------
|
|
38
|
+
*/
|
|
39
|
+
private cors;
|
|
40
|
+
setCors(rule: typeof this.cors): void;
|
|
41
|
+
/**
|
|
42
|
+
* ----------------------
|
|
43
|
+
* Swagger 配置
|
|
44
|
+
* ----------------------
|
|
45
|
+
*/
|
|
46
|
+
private _swagger;
|
|
47
|
+
/**
|
|
48
|
+
* 访问绑定到这个 router 的 Swagger 实例
|
|
49
|
+
* (必须事先调用过 `router.bindSwagger()`)
|
|
50
|
+
*/
|
|
51
|
+
get swagger(): Swagger;
|
|
52
|
+
bindSwagger(swagger?: Swagger, endpoint?: string): void;
|
|
53
|
+
private registerRouteToSwagger;
|
|
54
|
+
/**
|
|
55
|
+
* -------------------------------
|
|
56
|
+
* route 调用 / context 配置
|
|
57
|
+
* -------------------------------
|
|
58
|
+
*/
|
|
59
|
+
private executor;
|
|
60
|
+
setExecutor(executor: typeof this.executor): void;
|
|
61
|
+
/**
|
|
62
|
+
* ----------------------
|
|
63
|
+
* 请求处理
|
|
64
|
+
* ----------------------
|
|
65
|
+
*/
|
|
66
|
+
readonly handle: (request: Request, response: ResponseUtils) => Promise<void>;
|
|
67
|
+
}
|