te.js 1.0.0 → 1.0.2
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/.prettierrc +4 -4
- package/database/index.js +7 -7
- package/database/mongo.js +67 -14
- package/example/index.js +4 -6
- package/example/middlewares/global.midair.js +6 -0
- package/example/package.json +3 -2
- package/example/{routes → targets}/index.target.js +11 -10
- package/example/targets/user/user.target.js +17 -0
- package/example/tejas.config.json +5 -2
- package/package.json +36 -34
- package/server/ammo/body-parser.js +93 -51
- package/server/ammo/dispatch-helper.js +53 -53
- package/server/ammo/enhancer.js +53 -53
- package/server/ammo.js +96 -95
- package/server/error.js +9 -0
- package/server/files/helper.js +33 -0
- package/server/files/uploader.js +142 -0
- package/server/handler.js +71 -70
- package/server/target.js +62 -62
- package/server/targets/middleware-validator.js +22 -22
- package/server/targets/registry.js +44 -44
- package/te.js +128 -106
- package/utils/auto-register.js +26 -0
- package/utils/configuration.js +61 -61
- package/utils/request-logger.js +43 -43
- package/utils/status-codes.js +82 -82
- package/utils/tejas-entrypoint-html.js +18 -18
- package/example/routes/user/user.route.js +0 -13
package/server/ammo.js
CHANGED
|
@@ -1,95 +1,96 @@
|
|
|
1
|
-
import { statusAndData } from './ammo/dispatch-helper.js';
|
|
2
|
-
import {
|
|
3
|
-
isStatusCode,
|
|
4
|
-
toStatusCode,
|
|
5
|
-
toStatusMessage,
|
|
6
|
-
} from '../utils/status-codes.js';
|
|
7
|
-
import html from '../utils/tejas-entrypoint-html.js';
|
|
8
|
-
import ammoEnhancer from './ammo/enhancer.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
this.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.res.
|
|
45
|
-
this.res.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const errCode = arguments[
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
import { statusAndData } from './ammo/dispatch-helper.js';
|
|
2
|
+
import {
|
|
3
|
+
isStatusCode,
|
|
4
|
+
toStatusCode,
|
|
5
|
+
toStatusMessage,
|
|
6
|
+
} from '../utils/status-codes.js';
|
|
7
|
+
import html from '../utils/tejas-entrypoint-html.js';
|
|
8
|
+
import ammoEnhancer from './ammo/enhancer.js';
|
|
9
|
+
import TejError from './error.js';
|
|
10
|
+
|
|
11
|
+
class Ammo {
|
|
12
|
+
constructor(req, res) {
|
|
13
|
+
this.req = req;
|
|
14
|
+
this.res = res;
|
|
15
|
+
|
|
16
|
+
// Request related data
|
|
17
|
+
this.ip = undefined;
|
|
18
|
+
this.headers = undefined;
|
|
19
|
+
this.payload = undefined;
|
|
20
|
+
this.method = undefined;
|
|
21
|
+
|
|
22
|
+
// URL related data
|
|
23
|
+
this.protocol = undefined;
|
|
24
|
+
this.hostname = undefined;
|
|
25
|
+
this.path = undefined;
|
|
26
|
+
this.endpoint = undefined;
|
|
27
|
+
|
|
28
|
+
this.fullURL = undefined;
|
|
29
|
+
|
|
30
|
+
// Response related data
|
|
31
|
+
this.dispatchedData = undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async enhance() {
|
|
35
|
+
await ammoEnhancer(this);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fire() {
|
|
39
|
+
const { statusCode, data, contentType } = statusAndData(arguments);
|
|
40
|
+
const contentTypeHeader = { 'Content-Type': contentType };
|
|
41
|
+
|
|
42
|
+
this.dispatchedData = data;
|
|
43
|
+
|
|
44
|
+
this.res.writeHead(statusCode, contentTypeHeader);
|
|
45
|
+
this.res.write(data ?? '');
|
|
46
|
+
this.res.end();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
notFound() {
|
|
50
|
+
this.throw(new Error('Not Found'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
notAllowed() {
|
|
54
|
+
this.throw(new Error('Method Not Allowed'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
unauthorized() {
|
|
58
|
+
throw new TejError(401, 'Unauthorized');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
defaultEntry() {
|
|
62
|
+
this.fire(html);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw() {
|
|
66
|
+
const errCode = arguments[0];
|
|
67
|
+
const err = arguments[1];
|
|
68
|
+
|
|
69
|
+
let errMsg = err instanceof Error ? err.message : err.toString();
|
|
70
|
+
|
|
71
|
+
if (errCode && isStatusCode(errCode)) {
|
|
72
|
+
if (!errMsg) errMsg = toStatusMessage(errCode);
|
|
73
|
+
this.fire(errCode, errMsg);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (err instanceof Error) {
|
|
78
|
+
const errMessage = err.message;
|
|
79
|
+
|
|
80
|
+
if (!isNaN(parseInt(errMessage))) {
|
|
81
|
+
// Execute when errMessage is a number. Notice ! in front of isNan
|
|
82
|
+
const message = toStatusMessage(errMessage) ?? toStatusMessage(500);
|
|
83
|
+
this.fire(message, message);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const code = toStatusCode(errMsg) ?? 500;
|
|
88
|
+
this.fire(code, errMsg);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.fire(err);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default Ammo;
|
package/server/error.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import mime from 'mime';
|
|
2
|
+
|
|
3
|
+
const paths = (destination, filename) => {
|
|
4
|
+
const dir = `${process.cwd()}\\${destination}`;
|
|
5
|
+
const path = `${dir}\\${filename}`;
|
|
6
|
+
|
|
7
|
+
const absolute = path;
|
|
8
|
+
const relative = path.replace(process.cwd(), '');
|
|
9
|
+
|
|
10
|
+
return { dir, absolute, relative };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const extAndType = (obj) => {
|
|
14
|
+
const contentType = obj.headers['content-type'];
|
|
15
|
+
const ext = mime.getExtension(contentType);
|
|
16
|
+
const type = mime.getType(ext);
|
|
17
|
+
return {
|
|
18
|
+
ext,
|
|
19
|
+
type,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const extract = (contentDisposition, key) => {
|
|
24
|
+
if (!contentDisposition) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const parts = contentDisposition.split(';').map((part) => part.trim());
|
|
29
|
+
const part = parts.find((part) => part.startsWith(key));
|
|
30
|
+
return part ? part?.split('=')[1]?.trim()?.replace(/"/g, '') : undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { extAndType, extract, paths };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { filesize } from 'filesize';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import TejError from './../error.js';
|
|
4
|
+
import { extAndType, extract, paths } from './helper.js';
|
|
5
|
+
|
|
6
|
+
class TejFileUploader {
|
|
7
|
+
/*
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {string} options.destination - Destination to upload file to
|
|
10
|
+
* @param {string} options.name - Name of the file
|
|
11
|
+
* @param {number} options.maxFileSize - Maximum file size in bytes
|
|
12
|
+
*/
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.destination = options.destination;
|
|
15
|
+
this.name = options.name;
|
|
16
|
+
this.maxFileSize = options.maxFileSize;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
file() {
|
|
20
|
+
const keys = [...arguments];
|
|
21
|
+
return async (ammo, next) => {
|
|
22
|
+
if (!ammo.headers['content-type'].startsWith('multipart/form-data'))
|
|
23
|
+
return next();
|
|
24
|
+
|
|
25
|
+
const payload = ammo.payload;
|
|
26
|
+
const updatedPayload = {};
|
|
27
|
+
|
|
28
|
+
for (const part in payload) {
|
|
29
|
+
const obj = payload[part];
|
|
30
|
+
const contentDisposition = obj.headers['content-disposition'];
|
|
31
|
+
|
|
32
|
+
const { ext, type } = extAndType(obj);
|
|
33
|
+
if (!ext) continue;
|
|
34
|
+
|
|
35
|
+
const key = extract(contentDisposition, 'name');
|
|
36
|
+
if (ext === 'txt') {
|
|
37
|
+
updatedPayload[key] = obj.value;
|
|
38
|
+
} else {
|
|
39
|
+
if (!keys.includes(key)) continue;
|
|
40
|
+
|
|
41
|
+
const filename = extract(contentDisposition, 'filename');
|
|
42
|
+
if (!filename) continue;
|
|
43
|
+
|
|
44
|
+
const { dir, absolute, relative } = paths(this.destination, filename);
|
|
45
|
+
const size = filesize(obj.value.length,
|
|
46
|
+
{ output: 'object', round: 0 });
|
|
47
|
+
const maxSize = filesize(this.maxFileSize,
|
|
48
|
+
{ output: 'object', round: 0 });
|
|
49
|
+
if (this.maxFileSize && obj.value.length > this.maxFileSize)
|
|
50
|
+
throw new TejError(413,
|
|
51
|
+
`File size exceeds ${maxSize.value} ${maxSize.symbol}`);
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
fs.writeFileSync(absolute, obj.value, 'binary');
|
|
55
|
+
|
|
56
|
+
updatedPayload[key] = {
|
|
57
|
+
filename,
|
|
58
|
+
extension: ext,
|
|
59
|
+
path: {
|
|
60
|
+
absolute: absolute,
|
|
61
|
+
relative: relative
|
|
62
|
+
},
|
|
63
|
+
mimetype: type,
|
|
64
|
+
size
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ammo.payload = updatedPayload;
|
|
70
|
+
next();
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
files() {
|
|
75
|
+
const keys = [...arguments];
|
|
76
|
+
return async (ammo, next) => {
|
|
77
|
+
if (!ammo.headers['content-type'].startsWith('multipart/form-data'))
|
|
78
|
+
return next();
|
|
79
|
+
|
|
80
|
+
const payload = ammo.payload;
|
|
81
|
+
const updatedPayload = {};
|
|
82
|
+
const files = [];
|
|
83
|
+
|
|
84
|
+
for (const part in payload) {
|
|
85
|
+
const obj = payload[part];
|
|
86
|
+
const contentDisposition = obj.headers['content-disposition'];
|
|
87
|
+
|
|
88
|
+
const { ext, type } = extAndType(obj);
|
|
89
|
+
if (!ext) continue;
|
|
90
|
+
|
|
91
|
+
const key = extract(contentDisposition, 'name');
|
|
92
|
+
if (ext === 'txt') {
|
|
93
|
+
updatedPayload[key] = obj.value;
|
|
94
|
+
} else {
|
|
95
|
+
if (!keys.includes(key)) continue;
|
|
96
|
+
|
|
97
|
+
const filename = extract(contentDisposition, 'filename');
|
|
98
|
+
if (!filename) continue;
|
|
99
|
+
|
|
100
|
+
const { dir, absolute, relative } = paths(this.destination, filename);
|
|
101
|
+
const size = filesize(obj.value.length,
|
|
102
|
+
{ output: 'object', round: 0 });
|
|
103
|
+
const maxSize = filesize(this.maxFileSize,
|
|
104
|
+
{ output: 'object', round: 0 });
|
|
105
|
+
if (this.maxFileSize && obj.value.length > this.maxFileSize) {
|
|
106
|
+
throw new TejError(413,
|
|
107
|
+
`File size exceeds ${maxSize.value} ${maxSize.symbol}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
111
|
+
fs.writeFileSync(absolute, obj.value, 'binary');
|
|
112
|
+
|
|
113
|
+
files.push({
|
|
114
|
+
key,
|
|
115
|
+
filename,
|
|
116
|
+
path: {
|
|
117
|
+
absolute: absolute,
|
|
118
|
+
relative: relative
|
|
119
|
+
},
|
|
120
|
+
mimetype: type,
|
|
121
|
+
size
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const groupedFilesByKey = files.reduce((acc, file) => {
|
|
127
|
+
if (!acc[file.key]) acc[file.key] = [];
|
|
128
|
+
acc[file.key].push(file);
|
|
129
|
+
return acc;
|
|
130
|
+
}, {});
|
|
131
|
+
|
|
132
|
+
for (const key in groupedFilesByKey) {
|
|
133
|
+
updatedPayload[key] = groupedFilesByKey[key];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
ammo.payload = updatedPayload;
|
|
137
|
+
next();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default TejFileUploader;
|
package/server/handler.js
CHANGED
|
@@ -1,70 +1,71 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
chain.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
i
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
import { env } from 'tej-env';
|
|
2
|
+
import TejLogger from 'tej-logger';
|
|
3
|
+
import logHttpRequest from '../utils/request-logger.js';
|
|
4
|
+
|
|
5
|
+
import Ammo from './ammo.js';
|
|
6
|
+
import TejError from './error.js';
|
|
7
|
+
import TargetRegistry from './targets/registry.js';
|
|
8
|
+
|
|
9
|
+
const targetRegistry = new TargetRegistry();
|
|
10
|
+
const errorLogger = new TejLogger('Tejas.Exception');
|
|
11
|
+
|
|
12
|
+
const executeChain = async (target, ammo) => {
|
|
13
|
+
let i = 0;
|
|
14
|
+
|
|
15
|
+
const chain = targetRegistry.globalMiddlewares.concat(target.middlewares);
|
|
16
|
+
chain.push(target.shoot);
|
|
17
|
+
|
|
18
|
+
const next = async () => {
|
|
19
|
+
const middleware = chain[i];
|
|
20
|
+
i++;
|
|
21
|
+
|
|
22
|
+
const args =
|
|
23
|
+
middleware.length === 3 ? [ammo.req, ammo.res, next] : [ammo, next];
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
await middleware(...args);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
errorHandler(ammo, err);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
await next();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const errorHandler = (ammo, err) => {
|
|
36
|
+
if (env('LOG_EXCEPTIONS')) errorLogger.error(err);
|
|
37
|
+
|
|
38
|
+
if (err instanceof TejError)
|
|
39
|
+
return ammo.throw(err.code, err);
|
|
40
|
+
|
|
41
|
+
ammo.throw(500, err);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handler = async (req, res) => {
|
|
45
|
+
const target = targetRegistry.aim(req.method, req.url.split('?')[0]);
|
|
46
|
+
const ammo = new Ammo(req, res);
|
|
47
|
+
await ammo.enhance();
|
|
48
|
+
|
|
49
|
+
if (env('LOG_HTTP_REQUESTS')) logHttpRequest(ammo);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
if (target) {
|
|
53
|
+
await executeChain(target, ammo);
|
|
54
|
+
} else {
|
|
55
|
+
if (req.url === '/') {
|
|
56
|
+
ammo.defaultEntry();
|
|
57
|
+
} else {
|
|
58
|
+
errorHandler(
|
|
59
|
+
ammo,
|
|
60
|
+
new TejError(404,
|
|
61
|
+
`No target found for URL ${ammo.fullURL} with method ${ammo.method}`
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
errorHandler(ammo, err);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default handler;
|
package/server/target.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import isMiddlewareValid from './targets/middleware-validator.js';
|
|
2
|
-
import TargetRegistry from './targets/registry.js';
|
|
3
|
-
|
|
4
|
-
const targetRegistry = new TargetRegistry();
|
|
5
|
-
|
|
6
|
-
const isEndpointValid = (endpoint) => {
|
|
7
|
-
if (typeof endpoint !== 'string') return false;
|
|
8
|
-
if (endpoint.length === 0) return false;
|
|
9
|
-
return endpoint[0] === '/';
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const isShootValid = (shoot) => typeof shoot === 'function';
|
|
13
|
-
|
|
14
|
-
const validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
15
|
-
|
|
16
|
-
class Target {
|
|
17
|
-
constructor(base = '') {
|
|
18
|
-
this.base = base;
|
|
19
|
-
this.targetMiddlewares = [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
base(base) {
|
|
23
|
-
if (!base || !base.startsWith('/')) return;
|
|
24
|
-
this.base = base;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
midair() {
|
|
28
|
-
if (!arguments) return;
|
|
29
|
-
const middlewares = [...arguments];
|
|
30
|
-
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
31
|
-
this.targetMiddlewares = this.targetMiddlewares.concat(validMiddlewares);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
register() {
|
|
35
|
-
let allowedMethods = validMethods;
|
|
36
|
-
let args = arguments;
|
|
37
|
-
if (!args) return;
|
|
38
|
-
|
|
39
|
-
if (validMethods.includes(args[0])) {
|
|
40
|
-
allowedMethods = [args[0]];
|
|
41
|
-
args = arguments[1];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const endpoint = args[0];
|
|
45
|
-
const shoot = args[args.length - 1];
|
|
46
|
-
const middlewares = Array.from(args).slice(1, args.length - 1);
|
|
47
|
-
|
|
48
|
-
if (!isEndpointValid(endpoint)) return;
|
|
49
|
-
if (!isShootValid(shoot)) return;
|
|
50
|
-
|
|
51
|
-
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
52
|
-
|
|
53
|
-
targetRegistry.targets.push({
|
|
54
|
-
allowedMethods: allowedMethods.length > 0 ? allowedMethods : validMethods,
|
|
55
|
-
endpoint: this.base + endpoint,
|
|
56
|
-
middlewares: this.targetMiddlewares.concat(validMiddlewares),
|
|
57
|
-
shoot,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export default Target;
|
|
1
|
+
import isMiddlewareValid from './targets/middleware-validator.js';
|
|
2
|
+
import TargetRegistry from './targets/registry.js';
|
|
3
|
+
|
|
4
|
+
const targetRegistry = new TargetRegistry();
|
|
5
|
+
|
|
6
|
+
const isEndpointValid = (endpoint) => {
|
|
7
|
+
if (typeof endpoint !== 'string') return false;
|
|
8
|
+
if (endpoint.length === 0) return false;
|
|
9
|
+
return endpoint[0] === '/';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const isShootValid = (shoot) => typeof shoot === 'function';
|
|
13
|
+
|
|
14
|
+
const validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
15
|
+
|
|
16
|
+
class Target {
|
|
17
|
+
constructor(base = '') {
|
|
18
|
+
this.base = base;
|
|
19
|
+
this.targetMiddlewares = [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
base(base) {
|
|
23
|
+
if (!base || !base.startsWith('/')) return;
|
|
24
|
+
this.base = base;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
midair() {
|
|
28
|
+
if (!arguments) return;
|
|
29
|
+
const middlewares = [...arguments];
|
|
30
|
+
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
31
|
+
this.targetMiddlewares = this.targetMiddlewares.concat(validMiddlewares);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
register() {
|
|
35
|
+
let allowedMethods = validMethods;
|
|
36
|
+
let args = arguments;
|
|
37
|
+
if (!args) return;
|
|
38
|
+
|
|
39
|
+
if (validMethods.includes(args[0])) {
|
|
40
|
+
allowedMethods = [args[0]];
|
|
41
|
+
args = arguments[1];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const endpoint = args[0];
|
|
45
|
+
const shoot = args[args.length - 1];
|
|
46
|
+
const middlewares = Array.from(args).slice(1, args.length - 1);
|
|
47
|
+
|
|
48
|
+
if (!isEndpointValid(endpoint)) return;
|
|
49
|
+
if (!isShootValid(shoot)) return;
|
|
50
|
+
|
|
51
|
+
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
52
|
+
|
|
53
|
+
targetRegistry.targets.push({
|
|
54
|
+
allowedMethods: allowedMethods.length > 0 ? allowedMethods : validMethods,
|
|
55
|
+
endpoint: this.base + endpoint,
|
|
56
|
+
middlewares: this.targetMiddlewares.concat(validMiddlewares),
|
|
57
|
+
shoot,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default Target;
|