tspace-spear 1.2.4 → 1.2.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/dist/lib/core/server/index.js +1 -1
- package/dist/lib/core/server/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/core/server/express.d.ts +0 -295
- package/dist/lib/core/server/express.js +0 -1356
- package/dist/lib/core/server/express.js.map +0 -1
- package/dist/lib/core/server/request.d.ts +0 -2
- package/dist/lib/core/server/request.js +0 -7
- package/dist/lib/core/server/request.js.map +0 -1
|
@@ -1,1356 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.Express = void 0;
|
|
40
|
-
const http_1 = __importStar(require("http"));
|
|
41
|
-
const cluster_1 = __importDefault(require("cluster"));
|
|
42
|
-
const os_1 = __importDefault(require("os"));
|
|
43
|
-
const fs_1 = __importDefault(require("fs"));
|
|
44
|
-
const path_1 = __importDefault(require("path"));
|
|
45
|
-
const on_finished_1 = __importDefault(require("on-finished"));
|
|
46
|
-
const ws_1 = __importDefault(require("ws"));
|
|
47
|
-
const parser_factory_1 = require("./parser-factory");
|
|
48
|
-
const fast_router_1 = require("./fast-router");
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
* The 'Spear' class is used to create a server and handle HTTP requests.
|
|
52
|
-
*
|
|
53
|
-
* @returns {Spear} application
|
|
54
|
-
* @example
|
|
55
|
-
* new Spear()
|
|
56
|
-
* .get('/' , () => 'Hello world!')
|
|
57
|
-
* .get('/json' , () => {
|
|
58
|
-
* return {
|
|
59
|
-
* message : 'Hello world!'
|
|
60
|
-
* }
|
|
61
|
-
* })
|
|
62
|
-
* .listen(3000 , () => console.log('server listening on port : 3000'))
|
|
63
|
-
*
|
|
64
|
-
*/
|
|
65
|
-
class Express {
|
|
66
|
-
_controllers;
|
|
67
|
-
_middlewares;
|
|
68
|
-
_globalPrefix;
|
|
69
|
-
_router = new fast_router_1.FastRouter();
|
|
70
|
-
_parser = new parser_factory_1.ParserFactory();
|
|
71
|
-
_adapter = http_1.default;
|
|
72
|
-
_cluster;
|
|
73
|
-
_cors;
|
|
74
|
-
_swagger = {
|
|
75
|
-
use: false,
|
|
76
|
-
path: '/api/docs',
|
|
77
|
-
servers: [
|
|
78
|
-
{
|
|
79
|
-
url: 'http://localhost:3000'
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
tags: [],
|
|
83
|
-
info: {
|
|
84
|
-
title: "API Documentation",
|
|
85
|
-
description: "This is a sample documentation",
|
|
86
|
-
version: "1.0.0"
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
_swaggerSpecs = [];
|
|
90
|
-
_ws;
|
|
91
|
-
_errorHandler = null;
|
|
92
|
-
_globalMiddlewares = [];
|
|
93
|
-
_formatResponse = null;
|
|
94
|
-
_onListeners = [];
|
|
95
|
-
_fileUploadOptions = {
|
|
96
|
-
limit: Infinity,
|
|
97
|
-
tempFileDir: 'tmp',
|
|
98
|
-
removeTempFile: {
|
|
99
|
-
remove: false,
|
|
100
|
-
ms: 1000 * 60 * 10
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
_express = true;
|
|
104
|
-
constructor({ controllers, middlewares, globalPrefix, logger, cluster, adapter, express }) {
|
|
105
|
-
if (logger)
|
|
106
|
-
this.useLogger();
|
|
107
|
-
if (cluster)
|
|
108
|
-
this.useCluster(cluster);
|
|
109
|
-
if (adapter)
|
|
110
|
-
this.useAdater(adapter);
|
|
111
|
-
// if(express)
|
|
112
|
-
this._controllers = controllers;
|
|
113
|
-
// this._middlewares = middlewares;
|
|
114
|
-
this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* The get 'instance' method is used to get the instance of Spear.
|
|
118
|
-
*
|
|
119
|
-
* @returns {this}
|
|
120
|
-
*/
|
|
121
|
-
get instance() {
|
|
122
|
-
return this;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* The get 'routers' method is used get the all routers.
|
|
126
|
-
*
|
|
127
|
-
* @returns {FastRouter}
|
|
128
|
-
*/
|
|
129
|
-
get routers() {
|
|
130
|
-
return this._router;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* The 'ws' method is used to creates the WebSocket server.
|
|
134
|
-
*
|
|
135
|
-
* @callback {Function} WebSocketServer
|
|
136
|
-
* @param {WebSocketServer} wss - WebSocketServer
|
|
137
|
-
* @returns {this}
|
|
138
|
-
*/
|
|
139
|
-
ws(handlers, options) {
|
|
140
|
-
this._ws.handler = handlers();
|
|
141
|
-
this._ws.options = options ?? {};
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* The 'use' method is used to add the middleware into the request pipeline.
|
|
146
|
-
*
|
|
147
|
-
* @callback {Function} middleware
|
|
148
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
149
|
-
* @property {Function} next - go to next function
|
|
150
|
-
* @returns {this}
|
|
151
|
-
*/
|
|
152
|
-
use(middleware) {
|
|
153
|
-
this._globalMiddlewares.push(middleware);
|
|
154
|
-
return this;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* The 'useAdater' method is used to switch between different server implementations,
|
|
158
|
-
* such as the native Node.js HTTP server or uWebSockets.js (uWS).
|
|
159
|
-
*
|
|
160
|
-
* @param {T.Adapter} adapter - The adapter instance (e.g., HTTP or uWS).
|
|
161
|
-
* @returns {this} Returns the current instance for chaining
|
|
162
|
-
*/
|
|
163
|
-
useAdater(adapter) {
|
|
164
|
-
this._adapter = adapter;
|
|
165
|
-
this._parser.useAdater(adapter);
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* The 'useCluster' method is used cluster run the server
|
|
170
|
-
*
|
|
171
|
-
* @param {boolean | number} cluster
|
|
172
|
-
* @returns {this}
|
|
173
|
-
*/
|
|
174
|
-
useCluster(cluster) {
|
|
175
|
-
if (cluster === false)
|
|
176
|
-
return this;
|
|
177
|
-
this._cluster = cluster ?? true;
|
|
178
|
-
return this;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* The 'useLogger' method is used to add the middleware view logger response.
|
|
182
|
-
*
|
|
183
|
-
* @callback {Function} middleware
|
|
184
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
185
|
-
* @property {Function} next - go to next function
|
|
186
|
-
* @returns {this}
|
|
187
|
-
*/
|
|
188
|
-
useLogger({ methods, exceptPath } = {}) {
|
|
189
|
-
this._globalMiddlewares.push(({ req, res }, next) => {
|
|
190
|
-
const diffTime = (hrtime) => {
|
|
191
|
-
const MS = 1000;
|
|
192
|
-
if (hrtime == null)
|
|
193
|
-
return 0;
|
|
194
|
-
const [start, end] = process.hrtime(hrtime);
|
|
195
|
-
const time = ((start * MS) + (end / 1e6));
|
|
196
|
-
return `${time > MS ? `${(time / MS).toFixed(2)} s` : `${time.toFixed(2)} ms`}`;
|
|
197
|
-
};
|
|
198
|
-
const statusCode = (res) => {
|
|
199
|
-
const statusCode = res.statusCode == null ? 500 : Number(res.statusCode);
|
|
200
|
-
return statusCode < 400
|
|
201
|
-
? `\x1b[32m${statusCode}\x1b[0m`
|
|
202
|
-
: `\x1b[31m${statusCode}\x1b[0m`;
|
|
203
|
-
};
|
|
204
|
-
if (exceptPath instanceof RegExp && exceptPath.test(req.url))
|
|
205
|
-
return next();
|
|
206
|
-
if (Array.isArray(exceptPath) && exceptPath.some(v => req.url === v))
|
|
207
|
-
return next();
|
|
208
|
-
if (methods != null &&
|
|
209
|
-
methods.length &&
|
|
210
|
-
!methods.some(v => v.toLowerCase() === req.method.toLowerCase())) {
|
|
211
|
-
return next();
|
|
212
|
-
}
|
|
213
|
-
const startTime = process.hrtime();
|
|
214
|
-
(0, on_finished_1.default)(res, () => {
|
|
215
|
-
console.log([
|
|
216
|
-
`[\x1b[1m\x1b[34mINFO\x1b[0m]`,
|
|
217
|
-
`\x1b[34m${new Date().toJSON()}\x1b[0m`,
|
|
218
|
-
`\x1b[33m${req.method}\x1b[0m`,
|
|
219
|
-
`${decodeURIComponent(req.url)}`,
|
|
220
|
-
`${statusCode(res)}`,
|
|
221
|
-
`${diffTime(startTime)}`,
|
|
222
|
-
].join(" "));
|
|
223
|
-
});
|
|
224
|
-
return next();
|
|
225
|
-
});
|
|
226
|
-
return this;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* The 'useBodyParser' method is a middleware used to parse the request body of incoming HTTP requests.
|
|
230
|
-
* @param {object?}
|
|
231
|
-
* @property {array?} except the body parser with some methods
|
|
232
|
-
* @returns {this}
|
|
233
|
-
*/
|
|
234
|
-
useBodyParser({ except } = {}) {
|
|
235
|
-
this._globalMiddlewares.push((ctx, next) => {
|
|
236
|
-
const { req, res } = ctx;
|
|
237
|
-
if (Array.isArray(except) &&
|
|
238
|
-
except.some(v => v.toLowerCase() === (req.method).toLowerCase())) {
|
|
239
|
-
return next();
|
|
240
|
-
}
|
|
241
|
-
const contentType = req?.headers['content-type'] ?? null;
|
|
242
|
-
if (contentType == null)
|
|
243
|
-
return next();
|
|
244
|
-
const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
|
|
245
|
-
if (isFileUpload)
|
|
246
|
-
return next();
|
|
247
|
-
if (req?.body != null)
|
|
248
|
-
return next();
|
|
249
|
-
Promise.resolve(this._parser.body(req, res))
|
|
250
|
-
.then(body => {
|
|
251
|
-
req.body = body;
|
|
252
|
-
return next();
|
|
253
|
-
})
|
|
254
|
-
.catch(err => {
|
|
255
|
-
return this._nextFunction(ctx)(err);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
return this;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
|
|
262
|
-
*
|
|
263
|
-
* @param {?Object}
|
|
264
|
-
* @property {?number} limits // bytes. default Infinity
|
|
265
|
-
* @property {?string} tempFileDir
|
|
266
|
-
* @property {?Object} removeTempFile
|
|
267
|
-
* @property {boolean} removeTempFile.remove
|
|
268
|
-
* @property {number} removeTempFile.ms
|
|
269
|
-
* @returns
|
|
270
|
-
*/
|
|
271
|
-
useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
|
|
272
|
-
if (limit != null) {
|
|
273
|
-
this._fileUploadOptions.limit = limit;
|
|
274
|
-
}
|
|
275
|
-
if (tempFileDir != null) {
|
|
276
|
-
this._fileUploadOptions.tempFileDir = tempFileDir;
|
|
277
|
-
}
|
|
278
|
-
if (removeTempFile != null) {
|
|
279
|
-
this._fileUploadOptions.removeTempFile = removeTempFile;
|
|
280
|
-
}
|
|
281
|
-
this._globalMiddlewares.push((ctx, next) => {
|
|
282
|
-
const { req, res } = ctx;
|
|
283
|
-
if (req.method === 'GET') {
|
|
284
|
-
return next();
|
|
285
|
-
}
|
|
286
|
-
const contentType = req?.headers['content-type'];
|
|
287
|
-
const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
|
|
288
|
-
if (!isFileUpload)
|
|
289
|
-
return next();
|
|
290
|
-
if (req?.files != null)
|
|
291
|
-
return next();
|
|
292
|
-
Promise
|
|
293
|
-
.resolve(this._parser.files({ req, res, options: this._fileUploadOptions }))
|
|
294
|
-
.then(r => {
|
|
295
|
-
req.files = r.files;
|
|
296
|
-
req.body = r.body;
|
|
297
|
-
return next();
|
|
298
|
-
})
|
|
299
|
-
.catch(err => {
|
|
300
|
-
return this._nextFunction(ctx)(err);
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
return this;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* The 'useCookiesParser' method is a middleware used to parses cookies attached to the client request object.
|
|
307
|
-
*
|
|
308
|
-
* @returns {this}
|
|
309
|
-
*/
|
|
310
|
-
useCookiesParser() {
|
|
311
|
-
this._globalMiddlewares.push(({ req }, next) => {
|
|
312
|
-
if (req?.cookies != null)
|
|
313
|
-
return next();
|
|
314
|
-
req.cookies = this._parser.cookies(req);
|
|
315
|
-
return next();
|
|
316
|
-
});
|
|
317
|
-
return this;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* The 'useRouter' method is used to add the router in the request context.
|
|
321
|
-
*
|
|
322
|
-
* @parms {Function} router
|
|
323
|
-
* @property {Function} router - get() , post() , put() , patch() , delete()
|
|
324
|
-
* @returns {this}
|
|
325
|
-
*/
|
|
326
|
-
useRouter(router) {
|
|
327
|
-
const routes = router.routes;
|
|
328
|
-
for (const { path, method, handlers } of routes) {
|
|
329
|
-
this[method](this._normalizePath(this._globalPrefix, path), ...handlers);
|
|
330
|
-
}
|
|
331
|
-
return this;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* The 'useSwagger' method is a middleware used to create swagger api.
|
|
335
|
-
*
|
|
336
|
-
* @param {?Object} doc
|
|
337
|
-
* @returns
|
|
338
|
-
*/
|
|
339
|
-
useSwagger(doc = {}) {
|
|
340
|
-
const { path, servers, tags, info, options } = doc;
|
|
341
|
-
this._swagger = {
|
|
342
|
-
use: true,
|
|
343
|
-
options: options,
|
|
344
|
-
path: path ?? this._swagger.path,
|
|
345
|
-
servers: servers ?? this._swagger.servers,
|
|
346
|
-
tags: tags ?? this._swagger.tags,
|
|
347
|
-
info: info ?? this._swagger.info
|
|
348
|
-
};
|
|
349
|
-
return this;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* The 'listen' method is used to bind and start a server to a particular port and optionally a hostname.
|
|
353
|
-
*
|
|
354
|
-
* @param {number} port
|
|
355
|
-
* @param {function} callback
|
|
356
|
-
* @returns
|
|
357
|
-
*/
|
|
358
|
-
async listen(port, hostname, callback) {
|
|
359
|
-
if (arguments.length === 2 && typeof hostname === 'function') {
|
|
360
|
-
callback = hostname;
|
|
361
|
-
}
|
|
362
|
-
const server = await this._createServer();
|
|
363
|
-
if (this._cluster != null &&
|
|
364
|
-
this._cluster || typeof this._cluster === 'number') {
|
|
365
|
-
this._clusterMode({
|
|
366
|
-
server,
|
|
367
|
-
port,
|
|
368
|
-
hostname,
|
|
369
|
-
callback
|
|
370
|
-
});
|
|
371
|
-
return server;
|
|
372
|
-
}
|
|
373
|
-
if ('App' in this._adapter) {
|
|
374
|
-
const handler = () => {
|
|
375
|
-
this._onListeners.forEach(listener => listener());
|
|
376
|
-
if (this._swagger.use) {
|
|
377
|
-
this._swaggerHandler();
|
|
378
|
-
}
|
|
379
|
-
callback?.({ server, port });
|
|
380
|
-
};
|
|
381
|
-
if (hostname) {
|
|
382
|
-
server.listen(port, String(hostname), handler);
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
server.listen(port, handler);
|
|
386
|
-
}
|
|
387
|
-
return server;
|
|
388
|
-
}
|
|
389
|
-
const args = hostname
|
|
390
|
-
? [port, hostname, () => callback?.({ server, port: port })]
|
|
391
|
-
: [port, () => callback?.({ server, port: port })];
|
|
392
|
-
server.listen(...args);
|
|
393
|
-
server.on('listening', () => {
|
|
394
|
-
this._onListeners.forEach(listener => listener());
|
|
395
|
-
if (this._swagger.use) {
|
|
396
|
-
this._swaggerHandler();
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
return server;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* The 'cors' is used to enable the cors origins on the server.
|
|
403
|
-
*
|
|
404
|
-
* @params {Object}
|
|
405
|
-
* @property {(string | RegExp)[]} origins
|
|
406
|
-
* @property {boolean} credentials
|
|
407
|
-
* @returns
|
|
408
|
-
*/
|
|
409
|
-
cors({ origins, credentials } = {}) {
|
|
410
|
-
this._cors = ((req, res) => {
|
|
411
|
-
const origin = req.headers?.origin ?? null;
|
|
412
|
-
if (origin == null)
|
|
413
|
-
return;
|
|
414
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS');
|
|
415
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
416
|
-
if (Array.isArray(origins) && origins.length) {
|
|
417
|
-
for (const o of origins) {
|
|
418
|
-
if (typeof o === 'string' && (o === origin || o === '*')) {
|
|
419
|
-
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
|
-
if (o instanceof RegExp && o.test(origin)) {
|
|
423
|
-
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (credentials) {
|
|
428
|
-
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
429
|
-
}
|
|
430
|
-
if (req.method === 'OPTIONS') {
|
|
431
|
-
res.writeHead(204, { 'Content-Length': '0' });
|
|
432
|
-
res.end();
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
return;
|
|
436
|
-
});
|
|
437
|
-
return this;
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* The 'response' method is used to format the response
|
|
441
|
-
*
|
|
442
|
-
* @param {function} format
|
|
443
|
-
* @returns
|
|
444
|
-
*/
|
|
445
|
-
response(format) {
|
|
446
|
-
this._formatResponse = format;
|
|
447
|
-
return this;
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* The 'catch' method is middleware that is specifically designed to handle errors.
|
|
451
|
-
*
|
|
452
|
-
* that occur during the processing of requests
|
|
453
|
-
*
|
|
454
|
-
* @param {function} error
|
|
455
|
-
* @returns
|
|
456
|
-
*/
|
|
457
|
-
catch(error) {
|
|
458
|
-
this._errorHandler = error;
|
|
459
|
-
return this;
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* The 'notfound' method is middleware that is specifically designed to handle errors notfound that occur during the processing of requests
|
|
463
|
-
*
|
|
464
|
-
* @param {function} fn
|
|
465
|
-
* @returns
|
|
466
|
-
*/
|
|
467
|
-
notfound(fn) {
|
|
468
|
-
const handler = ({ req, res }) => {
|
|
469
|
-
const ctx = this._createContext({ req, res, ps: {} });
|
|
470
|
-
return fn(ctx);
|
|
471
|
-
};
|
|
472
|
-
this.all('*', handler);
|
|
473
|
-
return this;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* The 'get' method is used to add the request handler to the router for the 'GET' method.
|
|
477
|
-
*
|
|
478
|
-
* @param {string} path
|
|
479
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
480
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
481
|
-
* @property {Function} next - go to next function
|
|
482
|
-
* @returns {this}
|
|
483
|
-
*/
|
|
484
|
-
get(path, ...handlers) {
|
|
485
|
-
this._onListeners.push(() => {
|
|
486
|
-
return this._router.get(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
487
|
-
});
|
|
488
|
-
return this;
|
|
489
|
-
}
|
|
490
|
-
getx(path, ...handlers) {
|
|
491
|
-
this._onListeners.push(() => {
|
|
492
|
-
return this._router.get(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
493
|
-
});
|
|
494
|
-
return this;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* The 'post' method is used to add the request handler to the router for the 'POST' method.
|
|
498
|
-
*
|
|
499
|
-
* @param {string} path
|
|
500
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
501
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
502
|
-
* @property {Function} next - go to next function
|
|
503
|
-
* @returns {this}
|
|
504
|
-
*/
|
|
505
|
-
post(path, ...handlers) {
|
|
506
|
-
this._onListeners.push(() => {
|
|
507
|
-
return this._router.post(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
508
|
-
});
|
|
509
|
-
return this;
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* The 'put' method is used to add the request handler to the router for the 'PUT' method.
|
|
513
|
-
*
|
|
514
|
-
* @param {string} path
|
|
515
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
516
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
517
|
-
* @property {Function} next - go to next function
|
|
518
|
-
* @returns {this}
|
|
519
|
-
*/
|
|
520
|
-
put(path, ...handlers) {
|
|
521
|
-
this._onListeners.push(() => {
|
|
522
|
-
return this._router.put(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
523
|
-
});
|
|
524
|
-
return this;
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* The 'patch' method is used to add the request handler to the router for the 'PATCH' method.
|
|
528
|
-
*
|
|
529
|
-
* @param {string} path
|
|
530
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
531
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
532
|
-
* @property {Function} next - go to next function
|
|
533
|
-
* @returns {this}
|
|
534
|
-
*/
|
|
535
|
-
patch(path, ...handlers) {
|
|
536
|
-
this._onListeners.push(() => {
|
|
537
|
-
return this._router.patch(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
538
|
-
});
|
|
539
|
-
return this;
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* The 'delete' method is used to add the request handler to the router for the 'DELETE' method.
|
|
543
|
-
*
|
|
544
|
-
* @param {string} path
|
|
545
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
546
|
-
* @property {Object} ctx - context { req , res , query , params , cookies , files , body}
|
|
547
|
-
* @property {Function} next - go to next function
|
|
548
|
-
* @returns {this}
|
|
549
|
-
*/
|
|
550
|
-
delete(path, ...handlers) {
|
|
551
|
-
this._onListeners.push(() => {
|
|
552
|
-
return this._router.delete(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
553
|
-
});
|
|
554
|
-
return this;
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* The 'head' method is used to add the request handler to the router for 'HEAD' methods.
|
|
558
|
-
*
|
|
559
|
-
* @param {string} path
|
|
560
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
561
|
-
* @property {object} ctx - context { req , res , query , params , cookies , files , body}
|
|
562
|
-
* @property {function} next - go to next function
|
|
563
|
-
* @returns {this}
|
|
564
|
-
*/
|
|
565
|
-
head(path, ...handlers) {
|
|
566
|
-
this._onListeners.push(() => {
|
|
567
|
-
return this._router.head(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
568
|
-
});
|
|
569
|
-
return this;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* The 'head' method is used to add the request handler to the router for 'HEAD' methods.
|
|
573
|
-
*
|
|
574
|
-
* @param {string} path
|
|
575
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
576
|
-
* @property {object} ctx - context { req , res , query , params , cookies , files , body}
|
|
577
|
-
* @property {function} next - go to next function
|
|
578
|
-
* @returns {this}
|
|
579
|
-
*/
|
|
580
|
-
options(path, ...handlers) {
|
|
581
|
-
this._onListeners.push(() => {
|
|
582
|
-
return this._router.options(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
583
|
-
});
|
|
584
|
-
return this;
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
|
|
588
|
-
*
|
|
589
|
-
* @param {string} path
|
|
590
|
-
* @callback {...Function[]} handlers of the middlewares
|
|
591
|
-
* @property {object} ctx - context { req , res , query , params , cookies , files , body}
|
|
592
|
-
* @property {function} next - go to next function
|
|
593
|
-
* @returns {this}
|
|
594
|
-
*/
|
|
595
|
-
all(path, ...handlers) {
|
|
596
|
-
this._onListeners.push(() => {
|
|
597
|
-
return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
|
|
598
|
-
});
|
|
599
|
-
return this;
|
|
600
|
-
}
|
|
601
|
-
async _import(dir, pattern) {
|
|
602
|
-
const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
603
|
-
const files = (await Promise.all(directories.map((directory) => {
|
|
604
|
-
const newDir = path_1.default.resolve(String(dir), directory.name);
|
|
605
|
-
if (pattern == null) {
|
|
606
|
-
return directory.isDirectory() ? this._import(newDir) : newDir;
|
|
607
|
-
}
|
|
608
|
-
return directory.isDirectory()
|
|
609
|
-
? this._import(newDir)
|
|
610
|
-
: pattern.test(directory.name)
|
|
611
|
-
? newDir
|
|
612
|
-
: null;
|
|
613
|
-
}))).filter(d => d != null);
|
|
614
|
-
return [].concat(...files);
|
|
615
|
-
}
|
|
616
|
-
async _registerControllers() {
|
|
617
|
-
if (this._controllers == null)
|
|
618
|
-
return;
|
|
619
|
-
if (!Array.isArray(this._controllers)) {
|
|
620
|
-
const controllers = await this._import(this._controllers.folder, this._controllers.name);
|
|
621
|
-
for (const file of controllers) {
|
|
622
|
-
const imported = await Promise.resolve(`${file}`).then(s => __importStar(require(s)));
|
|
623
|
-
let maybeController = imported?.default;
|
|
624
|
-
if (maybeController == null) {
|
|
625
|
-
const entry = Object
|
|
626
|
-
.entries(imported)
|
|
627
|
-
.find(([name]) => {
|
|
628
|
-
return /controller$/i.test(name);
|
|
629
|
-
});
|
|
630
|
-
maybeController = entry?.[1];
|
|
631
|
-
}
|
|
632
|
-
const controller = maybeController;
|
|
633
|
-
if (typeof controller !== "function") {
|
|
634
|
-
console.log(`\x1b[31m[ControllerLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a controller class`);
|
|
635
|
-
continue;
|
|
636
|
-
}
|
|
637
|
-
const controllerInstance = new controller();
|
|
638
|
-
const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
|
|
639
|
-
const routers = Reflect.getMetadata("routers", controller) ?? [];
|
|
640
|
-
const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
|
|
641
|
-
if (prefixPath == null)
|
|
642
|
-
continue;
|
|
643
|
-
for (const { method, path, handler } of Array.from(routers)) {
|
|
644
|
-
const find = Array.from(swaggers).find(s => s.handler === handler);
|
|
645
|
-
if (find != null) {
|
|
646
|
-
this._swaggerSpecs = [
|
|
647
|
-
...this._swaggerSpecs,
|
|
648
|
-
{
|
|
649
|
-
...find,
|
|
650
|
-
path: this._normalizePath(this._globalPrefix, prefixPath, path),
|
|
651
|
-
method
|
|
652
|
-
}
|
|
653
|
-
];
|
|
654
|
-
}
|
|
655
|
-
this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
for (const controller of this._controllers) {
|
|
661
|
-
const controllerInstance = new controller();
|
|
662
|
-
const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
|
|
663
|
-
const routers = Reflect.getMetadata("routers", controller) ?? [];
|
|
664
|
-
const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
|
|
665
|
-
if (prefixPath == null)
|
|
666
|
-
continue;
|
|
667
|
-
for (const { method, path, handler } of Array.from(routers)) {
|
|
668
|
-
const find = Array.from(swaggers).find(s => s.handler === handler);
|
|
669
|
-
if (find != null) {
|
|
670
|
-
this._swaggerSpecs = [
|
|
671
|
-
...this._swaggerSpecs,
|
|
672
|
-
{
|
|
673
|
-
...find,
|
|
674
|
-
path: this._normalizePath(this._globalPrefix, prefixPath, path),
|
|
675
|
-
method
|
|
676
|
-
}
|
|
677
|
-
];
|
|
678
|
-
}
|
|
679
|
-
this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
async _registerMiddlewares() {
|
|
684
|
-
if (this._middlewares == null)
|
|
685
|
-
return;
|
|
686
|
-
if (!Array.isArray(this._middlewares)) {
|
|
687
|
-
const middlewares = await this._import(this._middlewares.folder, this._middlewares.name);
|
|
688
|
-
for (const file of middlewares) {
|
|
689
|
-
const imported = await Promise.resolve(`${file}`).then(s => __importStar(require(s)));
|
|
690
|
-
let maybeMiddleware = imported?.default;
|
|
691
|
-
if (maybeMiddleware == null) {
|
|
692
|
-
const entry = Object
|
|
693
|
-
.entries(imported)
|
|
694
|
-
.find(([name]) => {
|
|
695
|
-
return /middleware$/i.test(name);
|
|
696
|
-
});
|
|
697
|
-
maybeMiddleware = entry?.[1];
|
|
698
|
-
}
|
|
699
|
-
const middleware = maybeMiddleware;
|
|
700
|
-
if (typeof middleware !== "function") {
|
|
701
|
-
console.log(`\x1b[31m[MiddlewareLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a middleware`);
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
this.use(middleware);
|
|
705
|
-
}
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
const middlewares = this._middlewares;
|
|
709
|
-
for (const middleware of middlewares) {
|
|
710
|
-
this.use(middleware);
|
|
711
|
-
}
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
_customizeResponse(req, res) {
|
|
715
|
-
const response = res;
|
|
716
|
-
response.json = (results) => {
|
|
717
|
-
if (res.writableEnded)
|
|
718
|
-
return;
|
|
719
|
-
if (typeof results === 'string') {
|
|
720
|
-
if (!res.headersSent) {
|
|
721
|
-
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
722
|
-
}
|
|
723
|
-
return res.end(results);
|
|
724
|
-
}
|
|
725
|
-
if (!res.headersSent) {
|
|
726
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
727
|
-
}
|
|
728
|
-
if (results == null) {
|
|
729
|
-
if (this._formatResponse != null) {
|
|
730
|
-
return res.end(JSON.stringify(this._formatResponse(null, res.statusCode)));
|
|
731
|
-
}
|
|
732
|
-
return res.end();
|
|
733
|
-
}
|
|
734
|
-
if (this._formatResponse != null) {
|
|
735
|
-
return res.end(JSON.stringify(this._formatResponse({
|
|
736
|
-
...results
|
|
737
|
-
}, res.statusCode)));
|
|
738
|
-
}
|
|
739
|
-
return res.end(JSON.stringify({
|
|
740
|
-
...results,
|
|
741
|
-
}));
|
|
742
|
-
};
|
|
743
|
-
response.send = (results) => {
|
|
744
|
-
if (res.writableEnded)
|
|
745
|
-
return;
|
|
746
|
-
res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
|
|
747
|
-
return res.end(results);
|
|
748
|
-
};
|
|
749
|
-
response.html = (results) => {
|
|
750
|
-
if (res.writableEnded)
|
|
751
|
-
return;
|
|
752
|
-
res.writeHead(res.statusCode, { 'Content-Type': 'text/html' });
|
|
753
|
-
return res.end(results);
|
|
754
|
-
};
|
|
755
|
-
response.error = (err) => {
|
|
756
|
-
const statusCandidates = [
|
|
757
|
-
err?.response?.data?.code,
|
|
758
|
-
err?.code,
|
|
759
|
-
err?.status,
|
|
760
|
-
err?.statusCode,
|
|
761
|
-
err?.response?.data?.statusCode
|
|
762
|
-
];
|
|
763
|
-
let code = statusCandidates
|
|
764
|
-
.map(v => Number(v))
|
|
765
|
-
.find(v => Number.isFinite(v) && v >= 400) ?? 500;
|
|
766
|
-
const message = err?.response?.data?.errorMessage ??
|
|
767
|
-
err?.response?.data?.message ??
|
|
768
|
-
err?.message ??
|
|
769
|
-
`The request '${req.url}' resulted in a server error.`;
|
|
770
|
-
response.status(code);
|
|
771
|
-
const payload = { message };
|
|
772
|
-
if (this._formatResponse) {
|
|
773
|
-
return res.end(JSON.stringify(this._formatResponse(payload, code)));
|
|
774
|
-
}
|
|
775
|
-
return res.end(JSON.stringify(payload));
|
|
776
|
-
};
|
|
777
|
-
response.ok = (results) => {
|
|
778
|
-
return response.json(results == null ? {} : results);
|
|
779
|
-
};
|
|
780
|
-
response.created = (results) => {
|
|
781
|
-
response.status(201);
|
|
782
|
-
return response.json(results == null ? {} : results);
|
|
783
|
-
};
|
|
784
|
-
response.accepted = (results) => {
|
|
785
|
-
response.status(202);
|
|
786
|
-
return response.json(results == null ? {} : results);
|
|
787
|
-
};
|
|
788
|
-
response.noContent = () => {
|
|
789
|
-
response.status(204);
|
|
790
|
-
return res.end();
|
|
791
|
-
};
|
|
792
|
-
response.badRequest = (message) => {
|
|
793
|
-
if (res.writableEnded)
|
|
794
|
-
return;
|
|
795
|
-
response.status(400);
|
|
796
|
-
message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
|
|
797
|
-
if (this._formatResponse != null) {
|
|
798
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 400)));
|
|
799
|
-
}
|
|
800
|
-
return res.end(JSON.stringify({
|
|
801
|
-
message: message
|
|
802
|
-
}));
|
|
803
|
-
};
|
|
804
|
-
response.unauthorized = (message) => {
|
|
805
|
-
response.status(401);
|
|
806
|
-
message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
|
|
807
|
-
if (this._formatResponse != null) {
|
|
808
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 401)));
|
|
809
|
-
}
|
|
810
|
-
return res.end(JSON.stringify({
|
|
811
|
-
message
|
|
812
|
-
}));
|
|
813
|
-
};
|
|
814
|
-
response.paymentRequired = (message) => {
|
|
815
|
-
response.status(402);
|
|
816
|
-
message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
|
|
817
|
-
if (this._formatResponse != null) {
|
|
818
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 402)));
|
|
819
|
-
}
|
|
820
|
-
return res.end(JSON.stringify({
|
|
821
|
-
message
|
|
822
|
-
}));
|
|
823
|
-
};
|
|
824
|
-
response.forbidden = (message) => {
|
|
825
|
-
response.status(403);
|
|
826
|
-
message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
|
|
827
|
-
if (this._formatResponse != null) {
|
|
828
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 403)));
|
|
829
|
-
}
|
|
830
|
-
return res.end(JSON.stringify({
|
|
831
|
-
message
|
|
832
|
-
}));
|
|
833
|
-
};
|
|
834
|
-
response.notFound = (message) => {
|
|
835
|
-
response.status(404);
|
|
836
|
-
message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
|
|
837
|
-
if (this._formatResponse != null) {
|
|
838
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 404)));
|
|
839
|
-
}
|
|
840
|
-
return res.end(JSON.stringify({
|
|
841
|
-
message
|
|
842
|
-
}));
|
|
843
|
-
};
|
|
844
|
-
response.unprocessable = (message) => {
|
|
845
|
-
response.status(422);
|
|
846
|
-
message = message ?? `The request to '${req.url}' failed validation.`;
|
|
847
|
-
if (this._formatResponse != null) {
|
|
848
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 422)));
|
|
849
|
-
}
|
|
850
|
-
return res.end(JSON.stringify({
|
|
851
|
-
message
|
|
852
|
-
}));
|
|
853
|
-
};
|
|
854
|
-
response.tooManyRequests = (message) => {
|
|
855
|
-
response.status(429);
|
|
856
|
-
message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
|
|
857
|
-
if (this._formatResponse != null) {
|
|
858
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 429)));
|
|
859
|
-
}
|
|
860
|
-
return res.end(JSON.stringify({
|
|
861
|
-
message
|
|
862
|
-
}));
|
|
863
|
-
};
|
|
864
|
-
response.serverError = (message) => {
|
|
865
|
-
response.status(500);
|
|
866
|
-
message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
|
|
867
|
-
if (this._formatResponse != null) {
|
|
868
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 500)));
|
|
869
|
-
}
|
|
870
|
-
return res.end(JSON.stringify({
|
|
871
|
-
message
|
|
872
|
-
}));
|
|
873
|
-
};
|
|
874
|
-
response.status = (code) => {
|
|
875
|
-
res.writeHead(code, { 'Content-Type': 'application/json' });
|
|
876
|
-
return res;
|
|
877
|
-
};
|
|
878
|
-
response.setCookies = (cookies) => {
|
|
879
|
-
const cookieLists = [];
|
|
880
|
-
for (const [key, v] of Object.entries(cookies)) {
|
|
881
|
-
let str = `${key}=${typeof v === 'string' ? v : v.value}`;
|
|
882
|
-
if (typeof v !== 'string') {
|
|
883
|
-
if (v.sameSite)
|
|
884
|
-
str += `; SameSite=${v.sameSite}`;
|
|
885
|
-
str += `; Path=${v.path ?? '/'}`;
|
|
886
|
-
if (v.domain)
|
|
887
|
-
str += `; Domain=${v.domain}`;
|
|
888
|
-
if (v.httpOnly)
|
|
889
|
-
str += `; HttpOnly`;
|
|
890
|
-
if (v.secure)
|
|
891
|
-
str += `; Secure`;
|
|
892
|
-
if (v.expires) {
|
|
893
|
-
const maxAge = Math.floor((v.expires.getTime() - Date.now()) / 1000);
|
|
894
|
-
str += `; Max-Age=${maxAge}`;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
cookieLists.push(str);
|
|
898
|
-
}
|
|
899
|
-
if ('App' in this._adapter) {
|
|
900
|
-
for (const cookie of cookieLists) {
|
|
901
|
-
res.setHeader('Set-Cookie', cookie);
|
|
902
|
-
}
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
res.setHeader('Set-Cookie', cookieLists);
|
|
906
|
-
};
|
|
907
|
-
return response;
|
|
908
|
-
}
|
|
909
|
-
_wrapHandlers(...handlers) {
|
|
910
|
-
if (this._express) {
|
|
911
|
-
return (req, res, ps) => {
|
|
912
|
-
if (res.writableEnded)
|
|
913
|
-
return;
|
|
914
|
-
const ctx = this._createContext({ req, res, ps });
|
|
915
|
-
const dispatch = (index = 0) => {
|
|
916
|
-
try {
|
|
917
|
-
const handler = handlers[index];
|
|
918
|
-
if (!handler)
|
|
919
|
-
return;
|
|
920
|
-
const result = index === handlers.length - 1
|
|
921
|
-
? this._wrapResponse(handler)(req, res, this._nextFunction(req, res))
|
|
922
|
-
: handler(req, res, () => dispatch(index + 1));
|
|
923
|
-
return Promise.resolve(result);
|
|
924
|
-
}
|
|
925
|
-
catch (err) {
|
|
926
|
-
return this._nextFunction(ctx)(err);
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
return dispatch();
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
return (req, res, ps) => {
|
|
933
|
-
if (res.writableEnded)
|
|
934
|
-
return;
|
|
935
|
-
const ctx = this._createContext({ req, res, ps });
|
|
936
|
-
const dispatch = (index = 0) => {
|
|
937
|
-
try {
|
|
938
|
-
const handler = handlers[index];
|
|
939
|
-
if (!handler)
|
|
940
|
-
return;
|
|
941
|
-
const result = index === handlers.length - 1
|
|
942
|
-
? this._wrapResponse(handler)(ctx, this._nextFunction(ctx))
|
|
943
|
-
: handler(ctx, () => dispatch(index + 1));
|
|
944
|
-
return Promise.resolve(result);
|
|
945
|
-
}
|
|
946
|
-
catch (err) {
|
|
947
|
-
return this._nextFunction(ctx)(err);
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
return dispatch();
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
_wrapResponse(handler) {
|
|
954
|
-
return (ctx, next) => {
|
|
955
|
-
Promise.resolve(handler(ctx, next))
|
|
956
|
-
.then(result => {
|
|
957
|
-
if (ctx.res.writableEnded)
|
|
958
|
-
return;
|
|
959
|
-
if (result instanceof http_1.ServerResponse) {
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
if (result == null) {
|
|
963
|
-
if (!ctx.res.headersSent) {
|
|
964
|
-
ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
|
|
965
|
-
}
|
|
966
|
-
ctx.res.end();
|
|
967
|
-
return;
|
|
968
|
-
}
|
|
969
|
-
if (typeof result === 'string') {
|
|
970
|
-
ctx.res.end(result ?? '');
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
if (this._formatResponse != null) {
|
|
974
|
-
const formattedResult = this._formatResponse(result, ctx.res.statusCode);
|
|
975
|
-
if (typeof formattedResult === 'string') {
|
|
976
|
-
if (!ctx.res.headersSent) {
|
|
977
|
-
ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
978
|
-
}
|
|
979
|
-
ctx.res.end(formattedResult);
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
if (!ctx.res.headersSent) {
|
|
983
|
-
ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
984
|
-
}
|
|
985
|
-
ctx.res.end(JSON.stringify(formattedResult));
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
if (!ctx.res.headersSent) {
|
|
989
|
-
ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
990
|
-
}
|
|
991
|
-
ctx.res.end(JSON.stringify(result));
|
|
992
|
-
return;
|
|
993
|
-
})
|
|
994
|
-
.catch(err => {
|
|
995
|
-
return this._nextFunction(ctx)(err);
|
|
996
|
-
});
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
_nextFunction(ctx) {
|
|
1000
|
-
const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
|
|
1001
|
-
return (err) => {
|
|
1002
|
-
if (err != null) {
|
|
1003
|
-
if (this._errorHandler != null) {
|
|
1004
|
-
const callback = this._errorHandler(err, ctx);
|
|
1005
|
-
if (callback == null || !(callback instanceof http_1.ServerResponse)) {
|
|
1006
|
-
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1007
|
-
return ctx.res.end(JSON.stringify({
|
|
1008
|
-
message: err?.message
|
|
1009
|
-
}));
|
|
1010
|
-
}
|
|
1011
|
-
return callback;
|
|
1012
|
-
}
|
|
1013
|
-
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1014
|
-
if (this._formatResponse != null) {
|
|
1015
|
-
return ctx.res.end(JSON.stringify(this._formatResponse({
|
|
1016
|
-
message: err?.message,
|
|
1017
|
-
}, ctx.res.statusCode)));
|
|
1018
|
-
}
|
|
1019
|
-
ctx.res.end(JSON.stringify({
|
|
1020
|
-
message: err?.message
|
|
1021
|
-
}));
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
if (this._errorHandler != null) {
|
|
1025
|
-
return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
|
|
1026
|
-
}
|
|
1027
|
-
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1028
|
-
if (this._formatResponse != null) {
|
|
1029
|
-
ctx.res.end(JSON.stringify(this._formatResponse({
|
|
1030
|
-
message: NEXT_MESSAGE
|
|
1031
|
-
}, ctx.res.statusCode)));
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
ctx.res.end(JSON.stringify({
|
|
1035
|
-
message: NEXT_MESSAGE
|
|
1036
|
-
}));
|
|
1037
|
-
return;
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
_clusterMode({ server, port, hostname, callback }) {
|
|
1041
|
-
if (cluster_1.default.isPrimary) {
|
|
1042
|
-
const numCPUs = os_1.default.cpus().length;
|
|
1043
|
-
const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
|
|
1044
|
-
? numCPUs
|
|
1045
|
-
: this._cluster;
|
|
1046
|
-
for (let i = 0; i < maxWorkers; i++) {
|
|
1047
|
-
cluster_1.default.fork();
|
|
1048
|
-
}
|
|
1049
|
-
cluster_1.default.on('exit', () => {
|
|
1050
|
-
cluster_1.default.fork();
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
if (cluster_1.default.isWorker) {
|
|
1054
|
-
if ('App' in this._adapter) {
|
|
1055
|
-
const handler = () => {
|
|
1056
|
-
this._onListeners.forEach(listener => listener());
|
|
1057
|
-
if (this._swagger.use) {
|
|
1058
|
-
this._swaggerHandler();
|
|
1059
|
-
}
|
|
1060
|
-
callback?.({ server, port });
|
|
1061
|
-
};
|
|
1062
|
-
if (hostname) {
|
|
1063
|
-
server.listen(port, hostname, handler);
|
|
1064
|
-
return server;
|
|
1065
|
-
}
|
|
1066
|
-
server.listen(port, handler);
|
|
1067
|
-
return server;
|
|
1068
|
-
}
|
|
1069
|
-
const args = hostname
|
|
1070
|
-
? [port, hostname, () => callback?.({ server, port: port })]
|
|
1071
|
-
: [port, () => callback?.({ server, port: port })];
|
|
1072
|
-
server.listen(...args);
|
|
1073
|
-
server.on('listening', () => {
|
|
1074
|
-
this._onListeners.forEach(listener => listener());
|
|
1075
|
-
if (this._swagger.use) {
|
|
1076
|
-
this._swaggerHandler();
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1079
|
-
server.on('error', (_) => {
|
|
1080
|
-
port = Math.floor(Math.random() * 8999) + 1000;
|
|
1081
|
-
server.listen(port);
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
async _createServer() {
|
|
1087
|
-
await this._registerMiddlewares();
|
|
1088
|
-
await this._registerControllers();
|
|
1089
|
-
const lookup = this._router.lookup.bind(this._router);
|
|
1090
|
-
const cors = this._cors;
|
|
1091
|
-
const adapter = this._adapter;
|
|
1092
|
-
if ('App' in adapter) {
|
|
1093
|
-
const server = adapter.App();
|
|
1094
|
-
server.any('/*', (uwsRes, uwsReq) => {
|
|
1095
|
-
const { req, res } = this._uWSRequestResponse(uwsReq, uwsRes);
|
|
1096
|
-
if (cors)
|
|
1097
|
-
cors(req, res);
|
|
1098
|
-
return lookup(req, res);
|
|
1099
|
-
});
|
|
1100
|
-
if (this._ws?.handler) {
|
|
1101
|
-
server.ws('/*', {
|
|
1102
|
-
open: (ws) => {
|
|
1103
|
-
this._ws.handler?.connection?.(ws);
|
|
1104
|
-
},
|
|
1105
|
-
message: (ws, message) => {
|
|
1106
|
-
this._ws.handler?.message?.(ws, Buffer.from(message));
|
|
1107
|
-
},
|
|
1108
|
-
close: (ws, code, message) => {
|
|
1109
|
-
this._ws.handler?.close?.(ws, code, Buffer.from(message));
|
|
1110
|
-
}
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
return server;
|
|
1114
|
-
}
|
|
1115
|
-
const server = http_1.default.createServer((req, res) => {
|
|
1116
|
-
if (cors)
|
|
1117
|
-
cors(req, res);
|
|
1118
|
-
this._router.lookup(req, res);
|
|
1119
|
-
});
|
|
1120
|
-
if (this._ws?.handler) {
|
|
1121
|
-
this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
|
|
1122
|
-
this._ws.server.on('connection', (ws) => {
|
|
1123
|
-
if (this._ws.handler?.connection) {
|
|
1124
|
-
this._ws.handler.connection(ws);
|
|
1125
|
-
}
|
|
1126
|
-
ws.on('message', (data) => {
|
|
1127
|
-
this._ws.handler?.message?.(ws, data);
|
|
1128
|
-
});
|
|
1129
|
-
ws.on('close', (code, reason) => {
|
|
1130
|
-
if (this._ws.handler?.close) {
|
|
1131
|
-
this._ws.handler?.close(ws, code, reason);
|
|
1132
|
-
}
|
|
1133
|
-
});
|
|
1134
|
-
ws.on('error', (err) => {
|
|
1135
|
-
if (this._ws.handler?.error) {
|
|
1136
|
-
this._ws.handler.error(ws, err);
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
return server;
|
|
1142
|
-
}
|
|
1143
|
-
_createContext({ req, res, ps }) {
|
|
1144
|
-
const request = req;
|
|
1145
|
-
const response = this._customizeResponse(req, res);
|
|
1146
|
-
const headers = req.headers;
|
|
1147
|
-
const params = ps;
|
|
1148
|
-
const body = request.body;
|
|
1149
|
-
const files = request.files;
|
|
1150
|
-
const cookies = request.cookies;
|
|
1151
|
-
const query = this._parser.queryString(req.url) || {};
|
|
1152
|
-
const xff = headers['x-forwarded-for'];
|
|
1153
|
-
const xrip = headers['x-real-ip'];
|
|
1154
|
-
const cfip = headers['cf-connecting-ip'];
|
|
1155
|
-
let ips = [];
|
|
1156
|
-
if (cfip) {
|
|
1157
|
-
ips = Array.isArray(cfip) ? cfip : [cfip];
|
|
1158
|
-
}
|
|
1159
|
-
else if (xff) {
|
|
1160
|
-
ips = Array.isArray(xff) ? xff : [xff];
|
|
1161
|
-
}
|
|
1162
|
-
else if (xrip) {
|
|
1163
|
-
ips = Array.isArray(xrip) ? xrip : [xrip];
|
|
1164
|
-
}
|
|
1165
|
-
else {
|
|
1166
|
-
const addr = req.socket?.remoteAddress;
|
|
1167
|
-
ips = addr ? [addr] : [];
|
|
1168
|
-
}
|
|
1169
|
-
const ip = (ips.length ? ips[0] : null);
|
|
1170
|
-
request.params = params;
|
|
1171
|
-
request.query = query;
|
|
1172
|
-
request.ip = ip;
|
|
1173
|
-
request.ips = ips;
|
|
1174
|
-
return {
|
|
1175
|
-
req: request,
|
|
1176
|
-
res: response,
|
|
1177
|
-
headers: headers || {},
|
|
1178
|
-
params: params || {},
|
|
1179
|
-
query,
|
|
1180
|
-
body: body || {},
|
|
1181
|
-
files: files || {},
|
|
1182
|
-
cookies: cookies || {},
|
|
1183
|
-
ip,
|
|
1184
|
-
ips
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
_uWSRequestResponse(uwsReq, uwsRes) {
|
|
1188
|
-
const req = {
|
|
1189
|
-
method: String(uwsReq.getMethod()).toUpperCase(),
|
|
1190
|
-
url: uwsReq.getUrl() + (uwsReq.getQuery() ? `?${uwsReq.getQuery()}` : ''),
|
|
1191
|
-
headers: {}
|
|
1192
|
-
};
|
|
1193
|
-
uwsReq.forEach((key, value) => req.headers[key] = value);
|
|
1194
|
-
const res = {
|
|
1195
|
-
writeHeader: (key, value) => {
|
|
1196
|
-
if (!res.aborted) {
|
|
1197
|
-
uwsRes.writeHeader(key, value);
|
|
1198
|
-
}
|
|
1199
|
-
return res;
|
|
1200
|
-
},
|
|
1201
|
-
setHeader: (key, value) => {
|
|
1202
|
-
if (!res.aborted) {
|
|
1203
|
-
uwsRes.writeHeader(key, value);
|
|
1204
|
-
}
|
|
1205
|
-
return res;
|
|
1206
|
-
},
|
|
1207
|
-
writeHead(status, context) {
|
|
1208
|
-
res.writeHeaders = {
|
|
1209
|
-
...res.writeHeaders,
|
|
1210
|
-
[status]: context
|
|
1211
|
-
};
|
|
1212
|
-
res.headersSent = true;
|
|
1213
|
-
res.statusCode = status;
|
|
1214
|
-
return res;
|
|
1215
|
-
},
|
|
1216
|
-
_writeHead(status, context) {
|
|
1217
|
-
const statusMessages = {
|
|
1218
|
-
100: 'Continue',
|
|
1219
|
-
101: 'Switching Protocols',
|
|
1220
|
-
102: 'Processing',
|
|
1221
|
-
200: 'OK',
|
|
1222
|
-
201: 'Created',
|
|
1223
|
-
202: 'Accepted',
|
|
1224
|
-
203: 'Non-Authoritative Information',
|
|
1225
|
-
204: 'No Content',
|
|
1226
|
-
205: 'Reset Content',
|
|
1227
|
-
206: 'Partial Content',
|
|
1228
|
-
207: 'Multi-Status',
|
|
1229
|
-
208: 'Already Reported',
|
|
1230
|
-
226: 'IM Used',
|
|
1231
|
-
300: 'Multiple Choices',
|
|
1232
|
-
301: 'Moved Permanently',
|
|
1233
|
-
302: 'Found',
|
|
1234
|
-
303: 'See Other',
|
|
1235
|
-
304: 'Not Modified',
|
|
1236
|
-
305: 'Use Proxy',
|
|
1237
|
-
306: '(Unused)',
|
|
1238
|
-
307: 'Temporary Redirect',
|
|
1239
|
-
308: 'Permanent Redirect',
|
|
1240
|
-
400: 'Bad Request',
|
|
1241
|
-
401: 'Unauthorized',
|
|
1242
|
-
402: 'Payment Required',
|
|
1243
|
-
403: 'Forbidden',
|
|
1244
|
-
404: 'Not Found',
|
|
1245
|
-
405: 'Method Not Allowed',
|
|
1246
|
-
406: 'Not Acceptable',
|
|
1247
|
-
407: 'Proxy Authentication Required',
|
|
1248
|
-
408: 'Request Timeout',
|
|
1249
|
-
409: 'Conflict',
|
|
1250
|
-
410: 'Gone',
|
|
1251
|
-
411: 'Length Required',
|
|
1252
|
-
412: 'Precondition Failed',
|
|
1253
|
-
413: 'Payload Too Large',
|
|
1254
|
-
414: 'URI Too Long',
|
|
1255
|
-
415: 'Unsupported Media Type',
|
|
1256
|
-
416: 'Range Not Satisfiable',
|
|
1257
|
-
417: 'Expectation Failed',
|
|
1258
|
-
418: 'I\'m a teapot',
|
|
1259
|
-
421: 'Misdirected Request',
|
|
1260
|
-
422: 'Unprocessable Entity',
|
|
1261
|
-
423: 'Locked',
|
|
1262
|
-
424: 'Failed Dependency',
|
|
1263
|
-
425: 'Too Early',
|
|
1264
|
-
426: 'Upgrade Required',
|
|
1265
|
-
428: 'Precondition Required',
|
|
1266
|
-
429: 'Too Many Requests',
|
|
1267
|
-
431: 'Request Header Fields Too Large',
|
|
1268
|
-
451: 'Unavailable For Legal Reasons',
|
|
1269
|
-
500: 'Internal Server Error',
|
|
1270
|
-
501: 'Not Implemented',
|
|
1271
|
-
502: 'Bad Gateway',
|
|
1272
|
-
503: 'Service Unavailable',
|
|
1273
|
-
504: 'Gateway Timeout',
|
|
1274
|
-
505: 'HTTP Version Not Supported',
|
|
1275
|
-
506: 'Variant Also Negotiates',
|
|
1276
|
-
507: 'Insufficient Storage',
|
|
1277
|
-
508: 'Loop Detected',
|
|
1278
|
-
510: 'Not Extended',
|
|
1279
|
-
511: 'Network Authentication Required'
|
|
1280
|
-
};
|
|
1281
|
-
const statusMessage = statusMessages[status] || statusMessages[500];
|
|
1282
|
-
res.uwsRes.writeStatus(`${status} ${statusMessage}`);
|
|
1283
|
-
res.uwsRes.writeHeader(Object.keys(context)[0], Object.values(context)[0]);
|
|
1284
|
-
return res;
|
|
1285
|
-
},
|
|
1286
|
-
writeStatus: (status) => {
|
|
1287
|
-
if (!res.aborted) {
|
|
1288
|
-
res.uwsRes.writeStatus(status);
|
|
1289
|
-
}
|
|
1290
|
-
return res;
|
|
1291
|
-
},
|
|
1292
|
-
end: (str) => {
|
|
1293
|
-
if (res.aborted) {
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
uwsRes.cork(() => {
|
|
1297
|
-
if (!res.aborted) {
|
|
1298
|
-
res.aborted = true;
|
|
1299
|
-
for (const h in res.writeHeaders) {
|
|
1300
|
-
//@ts-ignore
|
|
1301
|
-
res._writeHead(h, res.writeHeaders[h]);
|
|
1302
|
-
}
|
|
1303
|
-
uwsRes.end(str);
|
|
1304
|
-
return;
|
|
1305
|
-
}
|
|
1306
|
-
});
|
|
1307
|
-
},
|
|
1308
|
-
aborted: false,
|
|
1309
|
-
writeHeaders: {},
|
|
1310
|
-
headersSent: false,
|
|
1311
|
-
statusCode: 200,
|
|
1312
|
-
uwsRes,
|
|
1313
|
-
};
|
|
1314
|
-
uwsRes.onAborted(() => {
|
|
1315
|
-
res.aborted = true;
|
|
1316
|
-
});
|
|
1317
|
-
return { req, res };
|
|
1318
|
-
}
|
|
1319
|
-
_normalizePath(...paths) {
|
|
1320
|
-
const path = paths
|
|
1321
|
-
.join('/')
|
|
1322
|
-
.replace(/\/+/g, '/')
|
|
1323
|
-
.replace(/\/+$/, '');
|
|
1324
|
-
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
1325
|
-
return /\/api\/api/.test(normalizedPath)
|
|
1326
|
-
? normalizedPath.replace(/\/api\/api\//, "/api/")
|
|
1327
|
-
: normalizedPath;
|
|
1328
|
-
}
|
|
1329
|
-
_swaggerHandler() {
|
|
1330
|
-
const routes = this.routers
|
|
1331
|
-
.routes
|
|
1332
|
-
.filter(r => {
|
|
1333
|
-
return [
|
|
1334
|
-
"GET", "POST",
|
|
1335
|
-
"PUT", "PATCH",
|
|
1336
|
-
"DELETE",
|
|
1337
|
-
"HEAD", "OPTIONS"
|
|
1338
|
-
].includes(r.method);
|
|
1339
|
-
});
|
|
1340
|
-
const { path, html, staticSwaggerHandler, staticUrl } = this._parser.swagger({
|
|
1341
|
-
...this._swagger,
|
|
1342
|
-
specs: this._swaggerSpecs,
|
|
1343
|
-
routes
|
|
1344
|
-
});
|
|
1345
|
-
this._router.get(staticUrl, staticSwaggerHandler);
|
|
1346
|
-
this._router.get(path, (req, res) => {
|
|
1347
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1348
|
-
res.end(html);
|
|
1349
|
-
return;
|
|
1350
|
-
});
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
exports.Express = Express;
|
|
1355
|
-
exports.default = Express;
|
|
1356
|
-
//# sourceMappingURL=express.js.map
|