qyani-web 1.0.4 → 1.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qyani-web",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Backend for the app",
5
5
  "main": "./dist/index.js",
6
6
  "author": "Qianrenni",
@@ -21,5 +21,6 @@
21
21
  "@types/node-forge": "^1.3.11",
22
22
  "ts-node": "^10.9.2",
23
23
  "typescript": "^5.8.3"
24
- }
24
+ },
25
+ "files": ["dist", "package.json","demo","README.md","package-lock.json"]
25
26
  }
package/keys/cert.pem DELETED
@@ -1,19 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIDDzCCAfegAwIBAgIBATANBgkqhkiG9w0BAQsFADBLMRIwEAYDVQQDEwlsb2Nh
3
- bGhvc3QxCzAJBgNVBAYTAkNOMRIwEAYDVQQKEwlRaWFucmVubmkxFDASBgNVBAsT
4
- C0RldmVsb3BtZW50MB4XDTI1MDUyMTAyMjAyN1oXDTI2MDUyMTAyMjAyN1owSzES
5
- MBAGA1UEAxMJbG9jYWxob3N0MQswCQYDVQQGEwJDTjESMBAGA1UEChMJUWlhbnJl
6
- bm5pMRQwEgYDVQQLEwtEZXZlbG9wbWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEP
7
- ADCCAQoCggEBAOPPt4WzRj+yDuya3nlQjkG1ZhnVIpvzUp3YUzPd/Z4eJ9e+awVY
8
- +YcObS/1hm7A44IDGTjn9m08UzNm/PfFPqE2FM1ODLV0s6myBS/NBFc/G7UiKjbr
9
- HItznkehX+JTqm5gLu/45B4LaUtdPEmG5CYVTQa3dwypnQi4PaGMlfxmc1xzKe/K
10
- QDEnZ39TdTlsptSb02szYQZxHvugLKde+y82pCKg9Q/SPhb3PSJjeKg8LQTFLxmM
11
- lwmzUNSDDn/UzLaswcxLgs6wcEv422A7cR+tPL7PJ0T0cNJ2VySU/zH3WCSGr1Xn
12
- CFJnQwCtIZS0WQ/G5zUvM6PUpy21BoJaqhcCAwEAATANBgkqhkiG9w0BAQsFAAOC
13
- AQEARF0DvPQGHRt3waaUhFJGPNEeKrb+uCkl1c5i25SUPdcvfpa8nt6m06souMGK
14
- oziH6uJlt/WBwgQmVp402/nxyQhNX3SY+MBkvBZ+l3FaPKsjiotM3u3vW9K8/NXD
15
- /A4bKPie/TwurHSp5opRp9ITr15Jo1NRjRin0h0h9of0POgwEtbizuboU0sKM96g
16
- IIv4xAWJ31ww5jtuStB1saA05YiYnnqGhfJ02ShOcFTIstLYPXSi+wd858t+96pu
17
- A5sy9JotEJYOCGBzmQpf9tOlDHIFOvJz6inD0HOkiVtG/AjU7mlxGgAU8qbhBRZ+
18
- Met0g+B2/TlKNaZATdRn3jGkgQ==
19
- -----END CERTIFICATE-----
package/keys/key.pem DELETED
@@ -1,27 +0,0 @@
1
- -----BEGIN RSA PRIVATE KEY-----
2
- MIIEowIBAAKCAQEA48+3hbNGP7IO7JreeVCOQbVmGdUim/NSndhTM939nh4n175r
3
- BVj5hw5tL/WGbsDjggMZOOf2bTxTM2b898U+oTYUzU4MtXSzqbIFL80EVz8btSIq
4
- Nusci3OeR6Ff4lOqbmAu7/jkHgtpS108SYbkJhVNBrd3DKmdCLg9oYyV/GZzXHMp
5
- 78pAMSdnf1N1OWym1JvTazNhBnEe+6Asp177LzakIqD1D9I+Fvc9ImN4qDwtBMUv
6
- GYyXCbNQ1IMOf9TMtqzBzEuCzrBwS/jbYDtxH608vs8nRPRw0nZXJJT/MfdYJIav
7
- VecIUmdDAK0hlLRZD8bnNS8zo9SnLbUGglqqFwIDAQABAoIBAARkD15RLMRIhczI
8
- qrYS4pMLUIWkm9n+pXPWZLrQsgqMppRGyXWdF+jNqIwZQuHlRN1Pevp4+hZz6F9K
9
- wmByLvlY0Kt+WsoFOaXxK57BaFktewwRiwJro1gKvp0Ufq50PLTg6gwFCOc4AsEJ
10
- nIJcSmRzhPxhUoxDbqaUNS06ckpJk+re1fYVgpdwmcLdSupTDDhzwa5hp8Kte83/
11
- Hy5Jb+5N88CA1FTltnhiH/b8ELjvUD7aC527MLjvumN0tbYG1WmRNmLsrt04Sh4a
12
- 9Akk/Qir+WKQJ9HVnNSZzjyuMphA/cGgDc9mrsikvQxO/BJVdnBn5UhU+XyyEQIM
13
- eRVXEDUCgYEA9uW72s160l0YDgSSUbH1vZzrXeXFx9uc+O1aEWbTjVV5J7ln0+/T
14
- m4O49hxiiSx09caFRGN6dp6itXFSvuB1eydxEVteNQyXmrZ2dBrMATkicQ0bEGiR
15
- kN5eBXpj/E8dlUZYu2+OfSguhWawXd2S+Aust2xT9bonz5l7oTRSWasCgYEA7DXY
16
- ep85tU/Q0gE+50ezMd+AMp0bBGk74hDJzYxrOo+fRwSMsdl974jjWGWN4YiJ8MiT
17
- ywFvF4JQWM7z81QBbetAbW63o+kc2l4zgRdbm3mS/fs6nYANWbZJzVxqPgG0tIXJ
18
- 0L8ZCcO8bykYnxp/Oah2qkiNDLtjmtt/qPcgfUUCgYA0yohxT3BMnKUxEh8rIq8V
19
- vQDCkgfJJRcaTr3sbJ0dYkJGkSk3Mtd/MBoTXZxgEkZr6ufB6Lddt6u4uiNkNuBH
20
- 1fYiLPQmyat85DjNuJEYTdwkahX4WzOM008tQq8wWsZLjcAZS9AeyMGxunRJjcAI
21
- E475pUsKhqROilXZ8TMOgwKBgDSjx0uEYmrqj6HNT0RhcSjDIyGka1nvTb4SebaJ
22
- /Xd+fENqWyRjULJp8ihH8Xd6Yys6t9z8w9IcY2V3MVxfszXYwHHiw6AOmjR728R/
23
- /RVH4Z8iNqzU1aTRf975ImARJ0E3s4MGo0DO0xZ+tRL357Rku5U1N+HHfOoM0oVI
24
- FYB1AoGBALBmVXYclZuJsK3ZKjh+0UNGo41SnXkNLGaykAgXU35ljAZDonTyluJx
25
- jq98FwG7LqCiYdd8bGYvT5aIP8Rs7EsTqrIYjE179H/qHWqwrLxrcwdxUC42MeTr
26
- bX/8jsAyoez7rzC6nXkpoQ1G3kW3YoxLE72kNErnzuA6S+NciNnb
27
- -----END RSA PRIVATE KEY-----
@@ -1,44 +0,0 @@
1
- import http from "http";
2
- /**
3
- * 文件信息
4
- * @param fieldname 字段名
5
- * @param filename 文件名
6
- * @param encoding 编码
7
- * @param mimetype MIME类型
8
- * @param data 文件数据
9
- */
10
- export type FileInfo = {
11
- fieldname: string;
12
- filename: string;
13
- encoding: string;
14
- mimetype: string;
15
- data: Buffer;
16
- };
17
-
18
- /**
19
- * 处理请求对象
20
- * @param query 查询参数
21
- * @param params 路由参数
22
- * @param body 请求体
23
- */
24
- export type ProcessRequest = {
25
- query: Record<string, string|number|boolean>,
26
- params: Record<string, string|number|boolean>,
27
- body:{fields:Record<string, string|number|boolean>, files:FileInfo[]},
28
- } & http.IncomingMessage;
29
-
30
- /**
31
- * 中间件函数类型定义
32
- * @param req 请求对象
33
- * @param res 响应对象
34
- * @return 返回一个布尔值,表示是否结束请求
35
- */
36
- export type Middleware = (req: http.IncomingMessage, res: http.ServerResponse) => boolean|Promise<boolean>| undefined;
37
-
38
- /**
39
- * 请求处理函数类型定义
40
- * @param req 请求对象
41
- * @param res 响应对象
42
- * @return 返回一个布尔值,表示是否结束请求
43
- */
44
- export type Handler = (req: http.IncomingMessage, res: http.ServerResponse) =>boolean|Promise<boolean>|undefined;
package/src/index.ts DELETED
@@ -1,252 +0,0 @@
1
- // app.js
2
- import https from 'https';
3
- import url from 'url';
4
- import GroupRoute from "./lib/group-route";
5
- import * as http from "http";
6
- import {Handler, Middleware} from "./config/type";
7
-
8
-
9
- export default class WebApp {
10
- private routes: { method: string; url: string; chain: Middleware[] }[] = [];
11
- private beforeRequestMiddleWares: Middleware[] = [];
12
- private afterRequestMiddleWares: Middleware[] = [];
13
-
14
- constructor() {}
15
-
16
- /**
17
- * 添加一个前置中间件
18
- * @param middleware 中间件函数
19
- * @returns 返回当前 WebApp 实例,支持链式调用
20
- */
21
- beforeRequest(middleware: Middleware): this {
22
- if (typeof middleware !== 'function') {
23
- throw new Error('Middleware must be a function');
24
- }
25
- if (this.beforeRequestMiddleWares.includes(middleware)) {
26
- throw new Error('Middleware already exists');
27
- }
28
- this.beforeRequestMiddleWares.push(middleware);
29
- return this;
30
- }
31
-
32
- /**
33
- * 添加一个后置中间件
34
- * @param middleware 中间件函数
35
- * @returns 返回当前 WebApp 实例,支持链式调用
36
- */
37
- afterRequest(middleware: Middleware): this {
38
- if (typeof middleware !== 'function') {
39
- throw new Error('Middleware must be a function');
40
- }
41
- if (this.afterRequestMiddleWares.includes(middleware)) {
42
- throw new Error('Middleware already exists');
43
- }
44
- this.afterRequestMiddleWares.push(middleware);
45
- return this;
46
- }
47
-
48
- /**
49
- * 注册一个路由
50
- * @param method HTTP 方法,如 'GET', 'POST' 等
51
- * @param url 路由路径
52
- * @param handler 请求处理函数
53
- * @param beforeRequest 在处理函数前执行的中间件数组
54
- * @param afterRequest 在处理函数后执行的中间件数组
55
- * @returns 返回当前 WebApp 实例,支持链式调用
56
- */
57
- request(method: string, url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
58
- this._register(method, url, handler, beforeRequest, afterRequest);
59
- return this;
60
- }
61
-
62
- /**
63
- * 注册一个 GET 请求路由
64
- * @param url 路由路径
65
- * @param handler 请求处理函数
66
- * @param beforeRequest 在处理函数前执行的中间件数组
67
- * @param afterRequest 在处理函数后执行的中间件数组
68
- * @returns 返回当前 WebApp 实例,支持链式调用
69
- */
70
- get(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
71
- this._register('GET', url, handler, beforeRequest, afterRequest);
72
- return this;
73
- }
74
-
75
- /**
76
- * 注册一个 POST 请求路由
77
- * @param url 路由路径
78
- * @param handler 请求处理函数
79
- * @param beforeRequest 在处理函数前执行的中间件数组
80
- * @param afterRequest 在处理函数后执行的中间件数组
81
- * @returns 返回当前 WebApp 实例,支持链式调用
82
- */
83
- post(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
84
- this._register('POST', url, handler, beforeRequest, afterRequest);
85
- return this;
86
- }
87
-
88
- /**
89
- * 注册一个 PUT 请求路由
90
- * @param url 路由路径
91
- * @param handler 请求处理函数
92
- * @param beforeRequest 在处理函数前执行的中间件数组
93
- * @param afterRequest 在处理函数后执行的中间件数组
94
- * @returns 返回当前 WebApp 实例,支持链式调用
95
- */
96
- put(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
97
- this._register('PUT', url, handler, beforeRequest, afterRequest);
98
- return this;
99
- }
100
-
101
- /**
102
- * 注册一个 DELETE 请求路由
103
- * @param url 路由路径
104
- * @param handler 请求处理函数
105
- * @param beforeRequest 在处理函数前执行的中间件数组
106
- * @param afterRequest 在处理函数后执行的中间件数组
107
- * @returns 返回当前 WebApp 实例,支持链式调用
108
- */
109
- delete(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
110
- this._register('DELETE', url, handler, beforeRequest, afterRequest);
111
- return this;
112
- }
113
-
114
- /**
115
- * 注册一个路由组
116
- * @param groupRoute 路由组实例
117
- * @returns 返回当前 WebApp 实例,支持链式调用
118
- */
119
- registerGroupRoute(groupRoute: GroupRoute): this {
120
- this.routes.push(...groupRoute.getRoutes());
121
- return this;
122
- }
123
-
124
- /**
125
- * 匹配路由参数
126
- * @param routePath 路由路径
127
- * @param requestPath 请求路径
128
- * @returns 匹配的参数对象
129
- */
130
- matchRoute(routePath: string, requestPath: string): Record<string, string> | null {
131
- const routeParts = routePath.split('/');
132
- const requestParts = requestPath.split('/');
133
-
134
- if (routeParts.length !== requestParts.length) return null;
135
-
136
- const params: Record<string, string> = {};
137
- for (let i = 0; i < routeParts.length; i++) {
138
- if (routeParts[i] !== requestParts[i] && !routeParts[i].startsWith(':')) {
139
- return null;
140
- }
141
- if (routeParts[i].startsWith(':')) {
142
- const paramName = routeParts[i].slice(1);
143
- params[paramName] = requestParts[i];
144
- }
145
- }
146
-
147
- return params;
148
- }
149
-
150
- /**
151
- * 处理请求
152
- * @param req 请求对象
153
- * @param res 请求响应对象
154
- * @private
155
- */
156
- private async _handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
157
- const parsedUrl = new url.URL(req.url ?? '', `http://${req.headers.host}`);
158
- const path = parsedUrl.pathname;
159
- const method = req.method;
160
-
161
- // 标记是否结束处理
162
- let handled: boolean | Promise<boolean> | undefined = false;
163
- try {
164
- //执行前置中间件
165
- for (const mw of this.beforeRequestMiddleWares) {
166
- if (handled !== true) handled = await mw(req, res);
167
- }
168
- if (handled) return;
169
- // 查找匹配的路由
170
- let matchedRoute: { method: string; url: string; chain: Middleware[] } | null = null
171
- let params: Record<string, string> | null = null;
172
-
173
- for (let route of this.routes) {
174
- if (route.method === method) {
175
- params = this.matchRoute(route.url, path);
176
- if (params) {
177
- matchedRoute = route;
178
- break;
179
- }
180
- }
181
- }
182
- if (params && matchedRoute) {
183
- // 设置请求参数
184
- (req as any).query = parsedUrl.searchParams;
185
- (req as any).params = params;
186
- for (const mw of matchedRoute.chain) {
187
- if (!handled) handled = await mw(req, res);
188
- }
189
- } else {
190
- // 404
191
- res.writeHead(404, {'Content-Type': 'application/json'});
192
- res.end(JSON.stringify({error: 'Not Found'}));
193
- }
194
- } catch (error: unknown) {
195
- // 500
196
- res.writeHead(500, {'Content-Type': 'application/json'});
197
- if (error instanceof Error) {
198
- res.end(JSON.stringify({
199
- error: 'Internal Server Error',
200
- message: error.message,
201
- }));
202
- console.log(error.stack);
203
- } else {
204
- res.end(JSON.stringify({
205
- error: 'Internal Server Error',
206
- message: 'An unknown error occurred.',
207
- }));
208
- }
209
- } finally {
210
- //执行后置中间件
211
- for (const mw of this.afterRequestMiddleWares) {
212
- if (!handled) handled = await mw(req, res);
213
- }
214
- }
215
- }
216
-
217
- /**
218
- * 启动服务器
219
- * @param port 端口号
220
- * @param callback 回调函数
221
- * @param protocol 协议类型
222
- * @param certs https证书
223
- */
224
- listen(port: number, callback: () => void,protocol:string = 'http',certs?:{cert:string,key:string}): void {
225
- let server;
226
- if (protocol !== 'https') {
227
- server = http.createServer(this._handleRequest.bind(this));
228
- }
229
- else if (protocol === 'https' && certs) {
230
- server = https.createServer(certs, this._handleRequest.bind(this));
231
- }
232
- else {
233
- console.log('未找到证书文件,使用http协议启动');
234
- server = http.createServer(this._handleRequest.bind(this));
235
- }
236
- server.listen(port, callback);
237
- }
238
-
239
- /**
240
- * 内部方法,用于注册路由
241
- * @param method HTTP 方法
242
- * @param url 路由路径
243
- * @param handler 请求处理函数
244
- * @param beforeRequest 在处理函数前执行的中间件数组
245
- * @param afterRequest 在处理函数后执行的中间件数组
246
- */
247
- private _register(method: string, url: string, handler: Handler, beforeRequest: Middleware[], afterRequest: Middleware[]): void {
248
- // 构建完整的中间件链:beforeRequest + handler + afterRequest
249
- const chain = [...beforeRequest, handler, ...afterRequest];
250
- this.routes.push({method, url, chain});
251
- }
252
- }
@@ -1,153 +0,0 @@
1
- import http from "http";
2
-
3
- /**
4
- * 中间件函数类型定义
5
- * @param req 请求对象
6
- * @param res 响应对象
7
- * @return 返回一个布尔值,表示是否结束请求
8
- */
9
- type Middleware = (req: http.IncomingMessage, res: http.ServerResponse) => boolean|Promise<boolean>| undefined;
10
-
11
- /**
12
- * 请求处理函数类型定义
13
- * @param req 请求对象
14
- * @param res 响应对象
15
- * @return 返回一个布尔值,表示是否结束请求
16
- */
17
- type Handler = (req: http.IncomingMessage, res: http.ServerResponse) => boolean|Promise<boolean>|undefined;
18
-
19
-
20
- /**
21
- * GroupRoute 类用于组织和管理一组具有共同前缀的 HTTP 路由。
22
- * 支持在请求处理前后添加中间件,并支持链式调用。
23
- */
24
- export default class GroupRoute {
25
- // 在请求处理前执行的中间件列表
26
- private beforeRequestMiddleWares: Middleware[] = [];
27
-
28
- // 在请求处理后执行的中间件列表
29
- private afterRequestMiddleWares: Middleware[] = [];
30
-
31
- // 注册的路由列表,包含 HTTP 方法、URL 和处理链
32
- private routes: { method: string; url: string; chain: Middleware[] }[] = [];
33
-
34
- /**
35
- * 构造函数
36
- * @param prefix 路由组的前缀,例如 "/api"
37
- */
38
- constructor(private readonly prefix: string) {}
39
-
40
- /**
41
- * 添加一个在请求处理前执行的中间件
42
- * @param middleware 中间件函数
43
- * @returns 返回当前 GroupRoute 实例,支持链式调用
44
- */
45
- beforeRequest(middleware: Middleware): this {
46
- this.beforeRequestMiddleWares.push(middleware);
47
- return this;
48
- }
49
-
50
- /**
51
- * 添加一个在请求处理后执行的中间件
52
- * @param middleware 中间件函数
53
- * @returns 返回当前 GroupRoute 实例,支持链式调用
54
- */
55
- afterRequest(middleware: Middleware): this {
56
- this.afterRequestMiddleWares.push(middleware);
57
- return this;
58
- }
59
-
60
- /**
61
- * 注册一个 GET 请求路由
62
- * @param url 路由路径
63
- * @param handler 请求处理函数
64
- * @param beforeRequest 在处理函数前执行的中间件数组
65
- * @param afterRequest 在处理函数后执行的中间件数组
66
- * @returns 返回当前 GroupRoute 实例,支持链式调用
67
- */
68
- get(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
69
- this._register('GET', this.prefix + url, handler, beforeRequest, afterRequest);
70
- return this;
71
- }
72
-
73
- /**
74
- * 注册一个 POST 请求路由
75
- * @param url 路由路径
76
- * @param handler 请求处理函数
77
- * @param beforeRequest 在处理函数前执行的中间件数组
78
- * @param afterRequest 在处理函数后执行的中间件数组
79
- * @returns 返回当前 GroupRoute 实例,支持链式调用
80
- */
81
- post(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
82
- this._register('POST', this.prefix + url, handler, beforeRequest, afterRequest);
83
- return this;
84
- }
85
-
86
- /**
87
- * 注册一个 PUT 请求路由
88
- * @param url 路由路径
89
- * @param handler 请求处理函数
90
- * @param beforeRequest 在处理函数前执行的中间件数组
91
- * @param afterRequest 在处理函数后执行的中间件数组
92
- * @returns 返回当前 GroupRoute 实例,支持链式调用
93
- */
94
- put(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
95
- this._register('PUT', this.prefix + url, handler, beforeRequest, afterRequest);
96
- return this;
97
- }
98
-
99
- /**
100
- * 注册一个 DELETE 请求路由
101
- * @param url 路由路径
102
- * @param handler 请求处理函数
103
- * @param beforeRequest 在处理函数前执行的中间件数组
104
- * @param afterRequest 在处理函数后执行的中间件数组
105
- * @returns 返回当前 GroupRoute 实例,支持链式调用
106
- */
107
- delete(url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
108
- this._register('DELETE', this.prefix + url, handler, beforeRequest, afterRequest);
109
- return this;
110
- }
111
-
112
- /**
113
- * 通用方法,用于注册任意 HTTP 方法的路由
114
- * @param method HTTP 方法,如 'GET', 'POST' 等
115
- * @param url 路由路径
116
- * @param handler 请求处理函数
117
- * @param beforeRequest 在处理函数前执行的中间件数组
118
- * @param afterRequest 在处理函数后执行的中间件数组
119
- */
120
- request(method: string, url: string, handler: Handler, beforeRequest: Middleware[] = [], afterRequest: Middleware[] = []): this {
121
- this._register(method, url, handler, beforeRequest, afterRequest);
122
- return this;
123
- }
124
-
125
- /**
126
- * 内部方法,用于将路由信息注册到路由列表中
127
- * @param method HTTP 方法
128
- * @param url 路由路径
129
- * @param handler 请求处理函数
130
- * @param beforeRequest 在处理函数前执行的中间件数组
131
- * @param afterRequest 在处理函数后执行的中间件数组
132
- */
133
- private _register(method: string, url: string, handler: Handler, beforeRequest: Middleware[], afterRequest: Middleware[]): void {
134
- // 构建完整的中间件链:beforeRequestMiddleWares + beforeRequest + handler + afterRequest + afterRequestMiddleWares
135
- const chain: Middleware[] = [
136
- ...this.beforeRequestMiddleWares,
137
- ...beforeRequest,
138
- handler,
139
- ...afterRequest,
140
- ...this.afterRequestMiddleWares,
141
- ];
142
-
143
- this.routes.push({ method, url, chain });
144
- }
145
-
146
- /**
147
- * 获取当前路由组的所有路由信息(可用于调试或注册到框架中)
148
- * @returns 路由列表
149
- */
150
- getRoutes(): { method: string; url: string; chain: Middleware[] }[] {
151
- return this.routes;
152
- }
153
- }
@@ -1,175 +0,0 @@
1
- // 解析 application/json
2
- /*
3
- * 解析 application/json
4
- * @param {string} rawBody - 待解析的 JSON 字符串
5
- * @returns {object} 解析后的 JSON 对象
6
- */
7
- import http from "http";
8
- import Bb from 'busboy'
9
- import {FileInfo, ProcessRequest} from "../../config/type";
10
-
11
- function parseJson(rawBody: string): {fields: Record<string, string>, files: FileInfo[]} {
12
- try {
13
- let result=JSON.parse(rawBody);
14
- console.log("parseJson",result);
15
- return {fields:result, files:[]};
16
- } catch (e) {
17
- throw new Error('Invalid JSON');
18
- }
19
- }
20
-
21
- // 解析 application/x-www-form-urlencoded
22
- /*
23
- * 解析 application/x-www-form-urlencoded
24
- * @param {string} rawBody - 待解析的表单数据
25
- * @returns Record<string, string> 解析后的表单数据
26
- */
27
- function parseForm(rawBody: string): { fields: Record<string, string>, files: FileInfo[] } {
28
- const params = new URLSearchParams(rawBody);
29
- const form: Record<string, string> = {};
30
- for (const [key, value] of params.entries()) {
31
- form[key] = value;
32
- }
33
- console.log("parseForm",form);
34
- return { fields: form, files: []};
35
- }
36
-
37
- /*
38
- * 解析 multipart/form-data
39
- * @param {http.IncomingMessage} req - 请求对象
40
- * @returns {Promise<{fields: Record<string, string>, files: any[]}>} 解析结果
41
- */
42
- function parseMultipart(req:http.IncomingMessage) {
43
- return new Promise<{fields: Record<string, string>, files: FileInfo[]}>((resolve, reject) => {
44
- // const bb = import('busboy')({
45
- // headers: req.headers,
46
- // limits: {
47
- // fileSize: 10*1024*1024 ,// 最大 10MB
48
- // files:10
49
- // }
50
- // }
51
- // );
52
-
53
- const bb=Bb({
54
- headers: req.headers,
55
- limits: {
56
- fileSize: 10*1024*1024 ,// 最大 10MB
57
- files:10
58
- }
59
- })
60
- const fields: Record<string, string> = {};
61
- const files: FileInfo[] = [];
62
-
63
- // 普通字段
64
- bb.on('field', (name:string, value:string) => {
65
- fields[name] = value;
66
- });
67
-
68
- // 文件字段
69
- bb.on('file', (fieldname:string, file, info:{filename:string, mimeType:string, encoding:string}) => {
70
- const { filename, mimeType, encoding } = info;
71
- const chunks: Buffer[] = [];
72
-
73
- console.log(`File [${fieldname}]: ${filename}, ${mimeType}`);
74
- // ✅ 关键:监听 'limit' 事件
75
- file.on('limit', () => {
76
- req.unpipe(bb);
77
- reject(new Error( `${filename}文件大小超出限制`));
78
- });
79
- file.on('data', (chunk) => {
80
- chunks.push(chunk);
81
- });
82
-
83
- file.on('end', () => {
84
- files.push({
85
- fieldname,
86
- filename,
87
- mimetype: mimeType,
88
- encoding,
89
- data: Buffer.concat(chunks)
90
- });
91
- console.log(`文件 [${fieldname}] 接收完成`);
92
- });
93
-
94
- file.on('error', (err) => {
95
- reject(err);
96
- });
97
- });
98
-
99
- // 完成解析
100
- bb.on('finish', () => {
101
- resolve({ fields, files });
102
- });
103
-
104
- // 错误处理
105
- bb.on('error', (err) => {
106
- reject(err);
107
- });
108
- // 文件数量超出限制
109
- bb.on('filesLimit', () => {
110
- req.unpipe(bb);
111
- reject(new Error('文件数量超过限制'));
112
- });
113
-
114
- // 字段数量超出限制
115
- bb.on('fieldsLimit', () => {
116
- req.unpipe(bb);
117
- reject(new Error('字段数量超过限制'));
118
- });
119
-
120
- // 管道输入
121
- req.pipe(bb);
122
- });
123
- }
124
-
125
- // 映射 Content-Type 到对应的解析函数
126
- const parserType=[ 'application/json','application/x-www-form-urlencoded','multipart/form-data']
127
-
128
- /**
129
- * 解析请求体
130
- * @param req http.IncomingMessage 请求对象
131
- * @param res http.ServerResponse 响应对象
132
- * @returns {Promise<boolean>} 是否结束处理
133
- */
134
- export async function bodyParser(req:http.IncomingMessage, res:http.ServerResponse) {
135
-
136
- const contentType = req.headers['content-type'] || '';
137
-
138
- (req as ProcessRequest).body=await new Promise<{ fields: Record<string, string>, files: FileInfo[] }>((resolve, reject) => {
139
- const matchedType = parserType.find(type => type===contentType)
140
-
141
- if (!matchedType) {
142
- return resolve({fields:{}, files:[]});
143
- }
144
-
145
- if (matchedType === 'multipart/form-data') {
146
- parseMultipart(req)
147
- .then(data => resolve(data))
148
- .catch(err => reject(err));
149
- } else {
150
-
151
- // 处理其他类型
152
- let body: Buffer[] = [];
153
- req.on('data', chunk => {
154
- body.push(chunk);
155
- });
156
-
157
- req.on('end', () => {
158
- const rawBody = Buffer.concat(body).toString();
159
- try {
160
- const result = contentType==='application/json'?parseJson(rawBody):parseForm(rawBody);
161
- resolve(result);
162
- } catch (e) {
163
- reject(e);
164
- }
165
- });
166
-
167
- req.on('error', (err) => {
168
- reject(err);
169
- });
170
- }
171
-
172
- });
173
-
174
- return false; // 继续执行下一个中间件
175
- }
@@ -1,64 +0,0 @@
1
- // middlewares/cors.js
2
- /*
3
- * 跨域中间件配置项
4
- */
5
- import http from "http";
6
-
7
- type CorsOptions = {
8
- origin?: string | string[];//允许的源
9
- methods?: string[];//允许的方法
10
- allowedHeaders?: string[];//允许的请求头
11
- credentials?: boolean;//允许携带cookie
12
- };
13
- /*
14
- * 跨域中间件
15
- * @param options 跨域配置项
16
- * @returns 中间件函数
17
- */
18
- export function cors(options: CorsOptions = {}) {
19
- const {
20
- origin = '*',
21
- methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
22
- allowedHeaders = ['Content-Type', 'Authorization'],
23
- credentials = false,
24
- } = options;
25
-
26
- return function corsMiddleware(req: http.IncomingMessage, res: http.ServerResponse) {
27
- const requestOrigin = req.headers.origin;
28
-
29
- if (requestOrigin) {
30
- let allowThisOrigin = false;
31
-
32
- if (origin === '*') {
33
- allowThisOrigin = true;
34
- } else if (Array.isArray(origin)) {
35
- allowThisOrigin = origin.some(host => host === requestOrigin);
36
- }
37
-
38
- if (allowThisOrigin) {
39
- // 设置 CORS 响应头
40
- res.setHeader('Access-Control-Allow-Origin', requestOrigin);
41
-
42
- if (credentials) {
43
- res.setHeader('Access-Control-Allow-Credentials', 'true');
44
- }
45
- res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
46
- res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
47
-
48
- // 处理 OPTIONS 预检请求
49
- if (req.method === 'OPTIONS') {
50
- res.writeHead(204); // No Content
51
- res.end();
52
- return true; // 已处理
53
- }
54
- } else {
55
- console.warn(`Blocked request from disallowed origin: ${requestOrigin}`);
56
- res.writeHead(403, { 'Content-Type': 'text/plain' });
57
- res.end('Forbidden');
58
- return true;
59
- }
60
- }
61
-
62
- return false; // 继续后续处理
63
- };
64
- }
@@ -1,95 +0,0 @@
1
- // middleware/logger.js
2
-
3
- /*
4
- * 获取当前时间
5
- * @return {string} 格式化后的时间字符串
6
- */
7
- import http from "http";
8
- import {Middleware} from "../../config/type";
9
-
10
- export function formatLocalTime(){
11
- const date=new Date();
12
- const year = date.getFullYear();
13
- const month = String(date.getMonth() + 1).padStart(2, '0');
14
- const day = String(date.getDate()).padStart(2, '0');
15
- const hours = String(date.getHours()).padStart(2, '0');
16
- const minutes = String(date.getMinutes()).padStart(2, '0');
17
- const seconds = String(date.getSeconds()).padStart(2, '0');
18
-
19
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
20
- }
21
-
22
- /*
23
- * 请求日志记录
24
- * @param {http.IncomingMessage} req 请求对象
25
- * @param {http.ServerResponse} res 响应对象
26
- * @return {boolean} 默认返回 false,表示继续执行后续逻辑
27
- */
28
- export const requestLogger = (req:http.IncomingMessage, res:http.ServerResponse) => {
29
- const method = req.method;
30
- const url = req.url;
31
- const timestamp = formatLocalTime();
32
-
33
- console.log(`[${timestamp}] ${req.headers.origin||''} request ${method} ${url} `);
34
- return false; // 继续执行后续逻辑
35
- };
36
-
37
- /*
38
- * 响应日志记录
39
- * @param {http.IncomingMessage} req 请求对象
40
- * @param {http.ServerResponse} res 响应对象
41
- * @return {boolean} 默认返回 false,表示继续执行后续逻辑
42
- */
43
- export const responseLogger = (req:http.IncomingMessage, res:http.ServerResponse) => {
44
- const startTime = process.hrtime();
45
- // 保存原始 end 方法
46
- const originalEnd = res.end;
47
-
48
- // 覆盖 res.end 方法以捕获响应结束
49
- res.end = function (this: typeof res, chunk?: any, encoding?: BufferEncoding | (() => void), callback?: () => void): typeof res {
50
- const [seconds, nanoseconds] = process.hrtime(startTime);
51
- const durationMs = Math.round(seconds * 1000 + nanoseconds / 1e6);
52
-
53
- const statusCode = res.statusCode || 500;
54
- const timestamp = formatLocalTime();
55
-
56
- console.log(
57
- `[${timestamp}] ${req.headers.origin||''} response ${req.method} ${req.url} ${statusCode} ${durationMs}ms`
58
- );
59
-
60
- return originalEnd.call(this, chunk, encoding as BufferEncoding, callback);
61
- };
62
- return false; // 继续执行后续逻辑
63
- };
64
-
65
- // function errorLoggerInjection(app){
66
- // if (!app)
67
- // throw new Error('app is required');
68
- // // 所有路由注册前自动包装 handler
69
- // app.routes = app.routes.map(route => ({
70
- // ...route,
71
- // handler: wrapHandler(route.handler)
72
- // }));
73
- // }
74
- //
75
- // function wrapHandler(handler:Middleware) {
76
- // return (req, res) => {
77
- // Promise.resolve()
78
- // .then(() => handler(req, res))
79
- // .catch(error => {
80
- // const timestamp = formatLocalTime();
81
- // const method = req.method;
82
- // const url = req.url;
83
- // console.error(`[${timestamp}] ERROR ${method} ${url}`);
84
- // console.error('Error Stack:',error.stack);
85
- // if(!res.headersSent){
86
- // res.writeHead(500, { 'Content-Type': 'application/json' });
87
- // }
88
- // res.end(JSON.stringify({
89
- // error: 'Internal Server Error',
90
- // message: error.message
91
- // }));
92
- // });
93
- // };
94
- // }
95
- console.log('logger.js loaded')
@@ -1,94 +0,0 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import http from "http";
4
- /*
5
- * 解析文件类型
6
- * @param extension 文件扩展名
7
- * @returns string 文件类型
8
- */
9
- function getContentType(extname: string): string {
10
- const map: Record<string, string>= {
11
- '.html': 'text/html',
12
- '.js': 'text/javascript',
13
- '.css': 'text/css',
14
- '.json': 'application/json',
15
- '.png': 'image/png',
16
- '.jpg': 'image/jpeg',
17
- '.gif': 'image/gif',
18
- '.svg': 'image/svg+xml',
19
- '.txt': 'text/plain',
20
- '.woff': 'font/woff',
21
- '.woff2': 'font/woff2',
22
- '.ttf': 'font/ttf',
23
- '.eot': 'application/vnd.ms-fontobject',
24
- '.mp3': 'audio/mpeg',
25
- '.mp4': 'video/mp4',
26
- '.wav': 'audio/wav',
27
- '.pdf': 'application/pdf',
28
- '.zip': 'application/zip',
29
- '.doc': 'application/msword',
30
- '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
31
- '.xls': 'application/vnd.ms-excel',
32
- '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
33
- '.ppt': 'application/vnd.ms-powerpoint',
34
- '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
35
- '.webp': 'image/webp'
36
- };
37
- return map[extname] || 'application/octet-stream';
38
- }
39
-
40
-
41
- /*
42
- * 静态资源配置项
43
- * @param prefix URL 前缀
44
- * @param rootDir 静态资源根目录
45
- */
46
- type StaticResourcesOptions = {
47
- prefix?: string;
48
- rootDir?: string;
49
- };
50
- /*
51
- * 静态资源中间件
52
- * @param options 配置项
53
- * @returns 返回一个中间件函数
54
- */
55
- export const staticResources = function (options: StaticResourcesOptions = {}){
56
- const { prefix = '/public', // URL 前缀
57
- rootDir = path.join(process.cwd(), prefix) // 默认 public 目录
58
- } = options;
59
- console.log('静态资源中间件已启动...', rootDir);
60
- return async function (req: http.IncomingMessage, res: http.ServerResponse) {
61
- const parsedUrl = new URL(req.url?? '', `http://${req.headers.host}`);
62
- const pathname = parsedUrl.pathname;
63
- // 检查是否匹配前缀
64
- if (!pathname.startsWith(prefix)) {
65
- return false; // 继续后续中间件
66
- }
67
- // 构建实际文件路径
68
- const filePath = pathname.replace(prefix, '');
69
- const fullPath = path.join(rootDir, filePath);
70
- try {
71
- const stats = await fs.promises.stat(fullPath);
72
- if (!stats.isFile()) {
73
- res.writeHead(404, { 'Content-Type': 'text/plain' });
74
- res.end('Not Found');
75
- return true; // 中断中间件链
76
- }
77
- const extname = path.extname(fullPath).toLowerCase();
78
- const contentType = getContentType(extname);
79
- res.writeHead(200, { 'Content-Type': contentType });
80
- fs.createReadStream(fullPath).pipe(res);
81
- return true; // 已响应,中断后续流程
82
- }
83
- catch (err:unknown) {
84
- res.writeHead(404, { 'Content-Type': 'text/plain' });
85
- res.end('Not Found');
86
- if(err instanceof Error){
87
- console.error('静态资源访问错误:', err.message);
88
- }
89
-
90
-
91
- return true; // 中断流程
92
- }
93
- };
94
- };
@@ -1,169 +0,0 @@
1
- import { formatLocalTime } from './logger.js';
2
- import http from "http";
3
- import {ProcessRequest} from "../../config/type";
4
-
5
- /**
6
- * 字段验证规则类型定义
7
- */
8
- type ValidationRule = {
9
- required?: boolean; // 是否必填
10
- optional?: boolean; // 是否可选(与required互斥)
11
- type?: 'string' | 'number' | 'boolean'; // 期望的类型
12
- min?: number; // 最小长度(仅字符串)
13
- max?: number; // 最大长度(仅字符串)
14
- format?: 'email'; // 格式验证(目前只支持email)
15
- };
16
-
17
- /**
18
- * 验证配置类型定义
19
- * 可以对body、query、params分别设置验证规则
20
- */
21
- type ValidationOptions = {
22
- [section: string]: Record<string, ValidationRule>;
23
- };
24
-
25
- /**
26
- * 获取字段值
27
- * @param section body部分
28
- * @param field 字段名
29
- * @param req 请求体
30
- * @returns 返回字段值
31
- */
32
- function getValue(section: string, field: string, req: ProcessRequest):string|null|number|boolean{
33
- let value= null;
34
- switch (section) {
35
- case 'query':
36
- value = req.query[field];
37
- break;
38
- case 'params':
39
- value = req.params[field];
40
- break;
41
- case 'body':
42
- value = req.body.fields[field];
43
- break;
44
- default:
45
- break;
46
- }
47
- return value;
48
- }
49
-
50
- /**
51
- *
52
- */
53
-
54
- function setValue(section: string, field: string, value: number|boolean|string, req: ProcessRequest): void{
55
- switch (section) {
56
- case 'body':
57
- req.body.fields[field] = value;
58
- break;
59
- case 'query':
60
- req.query[field] = value;
61
- break;
62
- case 'params':
63
- req.params[field] = value;
64
- break;
65
- default:
66
- break;
67
- }
68
- }
69
- /**
70
- * 参数验证中间件工厂函数
71
- * @param options 验证配置
72
- * @returns 返回一个中间件函数
73
- */
74
- export const validation = (options: ValidationOptions) => {
75
- return (req:http.IncomingMessage, res:http.ServerResponse) => {
76
- const request=req as ProcessRequest;
77
- const errors: string[] = [];
78
- // 遍历每个需要校验的部分(body、query、params)
79
- for (const [section, fields] of Object.entries(options)) {
80
- let data= null;
81
- switch (section){
82
- case 'query':
83
- data = request.query;
84
- break;
85
- case 'body':
86
- data = request.body.fields;
87
- break;
88
- case 'params':
89
- data = request.params;
90
- break;
91
- default:
92
- errors.push(`Invalid section: ${section}`);
93
- return;
94
- }
95
-
96
-
97
- if (!data) {
98
- errors.push(`Invalid request section: ${section}`)
99
- break;
100
- }
101
-
102
- // 遍历每个字段规则
103
- for (const [field, rules] of Object.entries(fields)) {
104
-
105
- let value= getValue(section, field, request);
106
-
107
-
108
- // 判断是否为必填字段
109
- if (rules.required === true && value === null) {
110
- errors.push(`"${field}" is required in ${section}`);
111
- continue;
112
- }
113
-
114
- // 如果是可选字段且未提供,则跳过后续检查
115
- if (rules.optional === true && value === null) {
116
- continue;
117
- }
118
- // ======【关键改进】自动类型转换(仅 query / params)======
119
- if ((section === 'query' || section === 'params') && value !== null) {
120
- if (rules.type === 'number') {
121
- const num = parseFloat(typeof value === "string" ? value :'error');
122
- if (!isNaN(num)) {
123
- setValue(section, field, num, request) // 写回原始对象
124
- }else{
125
- errors.push(`"${field}" must be a number in ${section} your's is ${value}`);
126
- }
127
- } else if (rules.type === 'boolean') {
128
- if (value === 'true') {
129
- setValue(section, field, true, request)
130
- } else if (value === 'false') {
131
- setValue(section, field, false, request);
132
- }else{
133
- errors.push(`"${field}" must be a boolean in ${section}`);
134
- }
135
- }
136
- }
137
-
138
- // 字符串长度校验
139
- if (typeof value === 'string') {
140
- if (rules.min !== undefined && value.length < rules.min) {
141
- errors.push(`"${field}" must be at least ${rules.min} characters long`);
142
- }
143
- if (rules.max !== undefined && value.length > rules.max) {
144
- errors.push(`"${field}" must be at most ${rules.max} characters long`);
145
- }
146
- }
147
-
148
- // 格式校验(如 email)
149
- if (rules.format === 'email'&& value !== null) {
150
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
151
- if (!emailRegex.test(typeof value === "string" ? value :'error')) {
152
- errors.push(`"${field}" must be a valid email address`);
153
- }
154
- }
155
- }
156
- }
157
-
158
- // 如果有错误,返回错误
159
- if (errors.length > 0) {
160
- res.writeHead(400, { 'Content-Type': 'application/json' })
161
- res.end(JSON.stringify({ message: '参数校验失败', errors }))
162
- console.log(`[${formatLocalTime()}] 参数校验失败: ${errors.join(', ')}`)
163
- return true
164
- }
165
-
166
- // 否则继续下一个中间件
167
- return false
168
- };
169
- };
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "esModuleInterop": true,
6
- "strict": true,
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "moduleResolution": "node",
10
- "resolveJsonModule": true,
11
- "skipLibCheck": false,
12
- "allowJs": true,
13
- },
14
- "include": ["src/**/*"]
15
- }