turbo-express-js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @project - TurboExpress
6
+ * @name TurboExpress
7
+ * @namespace TurboExpress::middlewares::static_middleware
8
+ * @license - MIT
9
+ * @author - Mike Karypidis
10
+ * @version - 1.0.0
11
+ * @link - https://npmjs.com/TurboExpress
12
+ * @github - https://github.com/TurboExpress
13
+ *
14
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
15
+ */
16
+
17
+ const fs = require("fs");
18
+ const FileEndToContentType = require("../enum/FileEndToContentType");
19
+ const RequestInterface = require("../types/RequestInterface");
20
+ const ResponseInterface = require("../types/ResponseInterface");
21
+
22
+ /**
23
+ *
24
+ * @param {RequestInterface} req
25
+ * @param {ResponseInterface} res
26
+ * @param {*} next
27
+ */
28
+ module.exports = function static_middleware (req, res, next)
29
+ {
30
+ const filePathParam = req.url().split(req.current.path_parts.slice(0, req.current.path_parts.length - 1).join("/"))[1];
31
+ const correctHeaders = FileEndToContentType(filePathParam);
32
+
33
+ const filePath = require("path").resolve() + this.folder + filePathParam;
34
+
35
+ //console.log(filePath)
36
+
37
+ if(fs.existsSync(filePath) === false) {
38
+ res.OutgoingMessage.writeHead(200, { 'Content-Type': 'text/html', });
39
+ return res.OutgoingMessage.end("NOT FOUND || 404")
40
+ }
41
+
42
+ /**
43
+ * @doc if in global property {TurboExpressCaching} exist the filepath
44
+ * then send file from cache;
45
+ */
46
+ if(global["TurboExpressCaching"][filePath]) {
47
+ res.OutgoingMessage.writeHead(200, { 'Content-Type': correctHeaders, });
48
+ res.OutgoingMessage.end(global["TurboExpressCaching"][filePath]);
49
+ return;
50
+ }
51
+
52
+ /**
53
+ * @doc read file using nodejs streams;
54
+ * when stream is over, check if cache property is true, then
55
+ * if true save the buffer to global {TurboExpressCaching}
56
+ * in any case send the file to the client;
57
+ */
58
+ const stream = fs.createReadStream(filePath);
59
+
60
+ res.OutgoingMessage.writeHead(200, { 'Content-Type': correctHeaders });
61
+
62
+ let chunks = [];
63
+
64
+ stream.on("data", (chunk) => chunks.push(chunk))
65
+ stream.on("end", () => {
66
+ let fileData = Buffer.concat(chunks);
67
+ if(this.cache) global["TurboExpressCaching"][filePath] = fileData;
68
+ res.OutgoingMessage.end(fileData);
69
+ });
70
+
71
+ stream.on('error', (err) => {
72
+ res.OutgoingMessage.writeHead(404);
73
+ res.OutgoingMessage.end(err.message);
74
+ });
75
+
76
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @project - TurboExpress
6
+ * @name TurboExpress
7
+ * @namespace TurboExpress::middlewares::static_middleware
8
+ * @license - MIT
9
+ * @author - Mike Karypidis
10
+ * @version - 1.0.0
11
+ * @link - https://npmjs.com/TurboExpress
12
+ * @github - https://github.com/TurboExpress
13
+ *
14
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
15
+ */
16
+
17
+ const RequestInterface = require("../types/RequestInterface");
18
+ const ResponseInterface = require("../types/ResponseInterface");
19
+
20
+ /**
21
+ *
22
+ * @param {RequestInterface} req
23
+ * @param {ResponseInterface} res
24
+ * @param {*} next
25
+ */
26
+ module.exports = async function validation_middleware (req, res, next)
27
+ {
28
+
29
+ const { validations } = this;
30
+
31
+ validations.map(v => req.addValidation(v));
32
+
33
+ const validationResult = await req.validateIncomingData();
34
+
35
+ if(validationResult === -1) return res.status(400).json({ errors: req._validation_errors });
36
+
37
+ next();
38
+
39
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @project - TurboExpress
6
+ * @name TurboExpress
7
+ * @namespace TurboExpress::StaticTypes::RequestMethods
8
+ * @license - MIT
9
+ * @author - Mike Karypidis
10
+ * @version - 1.0.0
11
+ * @link - https://npmjs.com/TurboExpress
12
+ * @github - https://github.com/TurboExpress
13
+ *
14
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
15
+ */
16
+
17
+
18
+ module.exports = class RequestMethods
19
+ {
20
+
21
+ static GET = "get";
22
+ static POST = "post";
23
+ static PUT = "put";
24
+ static DELETE = "delete";
25
+ static HEAD = "head";
26
+ static PATCH = "patch";
27
+ static OPTIONS = "options";
28
+ static LINK = "link";
29
+ static UNLINK = "unlink";
30
+ static VIEW = "view";
31
+ static LOCK = "lock";
32
+ static UNLOCK = "unlock";
33
+ static COPY = "copy";
34
+ static PURGE = "purge";
35
+ static PROPFIND = "propfind"
36
+
37
+ http_methods = ["get", "post", "delete", "put", "head", "patch", "options", "link", "unlink", "view", "lock", "unlock", "copy", "purge", "propfind"];
38
+
39
+ get;
40
+ post;
41
+ put;
42
+ delete;
43
+ head;
44
+ patch;
45
+ options;
46
+ link;
47
+ unlink;
48
+ view;
49
+ lock;
50
+ unlock;
51
+ copy;
52
+ purge;
53
+ propfind;
54
+
55
+
56
+ constructor ()
57
+ {
58
+ this.get = RequestMethods.GET;
59
+ this.post = RequestMethods.POST;
60
+ this.put = RequestMethods.PUT;
61
+ this.delete = RequestMethods.DELETE;
62
+ this.head = RequestMethods.HEAD;
63
+ this.patch = RequestMethods.PATCH;
64
+ this.options = RequestMethods.OPTIONS;
65
+ this.link = RequestMethods.LINK;
66
+ this.unlink = RequestMethods.UNLINK;
67
+ this.view = RequestMethods.VIEW;
68
+ this.lock = RequestMethods.LOCK;
69
+ this.unlock = RequestMethods.UNLOCK;
70
+ this.copy = RequestMethods.COPY;
71
+ this.purge = RequestMethods.PURGE;
72
+ this.propfind = RequestMethods.PROPFIND;
73
+ }
74
+
75
+ /**
76
+ *
77
+ * @param {Function} cb
78
+ * @returns {void}
79
+ */
80
+ forEach (cb)
81
+ {
82
+ this.http_methods.map(cb);
83
+ }
84
+
85
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @MikeKarypidis
6
+ * @project - TurboExpress
7
+ * @name Middleware
8
+ * @namespace TurboExpress::types::FileType
9
+ * @license - MIT
10
+ * @copyright - ©2022 PerfectEvolution Corporation;
11
+ * @author - Mike Karypidis
12
+ * @version - 1.0.0
13
+ * @link - https://turboserverjs.org
14
+ * @github - https://github.com/breathfunwithmindte/turbo-server.git
15
+ *
16
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
17
+ */
18
+
19
+ /** @type */
20
+ module.exports = class FileType
21
+ {
22
+ /** @type {String} */ name;
23
+ /** @type {String} */ fileName;
24
+ /** @type {String} */ extension;
25
+ /** @type {String} */ mime_type;
26
+ /** @type {String} */ description;
27
+ /** @type {boolean} */ is_saved;
28
+ /** @type {boolean} */ is_binary;
29
+ /** @type {boolean} */ is_compressible;
30
+ /** @type {boolean} */ is_encrypted;
31
+ /** @type {number} */ buffer_size;
32
+ /** @type {Buffer} */ buffer;
33
+ /** @type {String} */ owner;
34
+ /** @type {*} */ metadata;
35
+
36
+
37
+ save(path, cb)
38
+ {
39
+
40
+ }
41
+
42
+ saveSync (path)
43
+ {
44
+
45
+ }
46
+
47
+ /**
48
+ * @param {*} by
49
+ */
50
+ fileExtension (by)
51
+ {
52
+
53
+ }
54
+
55
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @MikeKarypidis
6
+ * @project - TurboExpress
7
+ * @name Middleware
8
+ * @namespace TurboExpress::ServerCallback
9
+ * @license - MIT
10
+ * @copyright - ©2022 PerfectEvolution Corporation;
11
+ * @author - Mike Karypidis
12
+ * @version - 1.0.0
13
+ * @link - https://turboserverjs.org
14
+ * @github - https://github.com/breathfunwithmindte/turbo-server.git
15
+ *
16
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
17
+ */
18
+
19
+ module.exports = class Middleware
20
+ {
21
+
22
+ /**
23
+ * @doc all this properties will be used once to find if middleware is matching the route.
24
+ * if it does then the callbacks will be pushed at the begining of the route.callbacks
25
+ *
26
+ * @useMinLength actually if path endwith /* that group all of these middlewares in their own category
27
+ * on server callback will be executed 2 matching functions
28
+ *
29
+ * 1rst one is exact that mean it will try to match route that not end with /* and has the same length of path parts;
30
+ *
31
+ * if the first not find something, only then it will go to the next function
32
+ * in the second function, here is where useMinLength = true group is used.
33
+ *
34
+ *
35
+ */
36
+
37
+
38
+ /** @type {String} */ path;
39
+ /** @type {String[]} */ path_parts;
40
+ /** @type {Boolean} */ useMinlength;
41
+ /** @type {Number} */ size;
42
+ /** @type {Function[]} */ callbacks = new Array();
43
+
44
+ /**
45
+ * todo validation check and warn for wrong type;
46
+ * @param {String} path
47
+ * @param {Function[]} callbacks
48
+ */
49
+ constructor(path, callbacks)
50
+ {
51
+ this.path = path;
52
+ this.path_parts = path.split("/").filter((f) => f !== "");
53
+ this.useMinlength = path.endsWith("/*");
54
+ this.size = path.endsWith("/*")
55
+ ? this.path_parts.length - 1
56
+ : this.path_parts.length;
57
+ this.callbacks = callbacks || [];
58
+ }
59
+ };
@@ -0,0 +1,398 @@
1
+ /**
2
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
3
+ *
4
+ * @PerfecTEvolutioN
5
+ * @MikeKarypidis
6
+ * @project - TurboServer
7
+ * @name TurboServer
8
+ * @license - MIT
9
+ * @copyright - ©2022 PerfectEvolution Corporation;
10
+ * @author - Mike Karypidis
11
+ * @version - 1.0.0
12
+ * @link - https://turboserverjs.org
13
+ * @github - https://github.com/breathfunwithmindte/turbo-server.git
14
+ *
15
+ * -->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>-->>
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} BasicAuth
20
+ * @property {String} user
21
+ * @property {String} pass
22
+ *
23
+ * @typedef {http.IncomingMessage} incme
24
+ */
25
+
26
+ const http = require("http");
27
+ const FileType = require("./FileType");
28
+ const Route = require("./Route");
29
+ const ValidationSchema = require("./ValidationSchema");
30
+
31
+
32
+ /** @interface */
33
+ module.exports = class RequestInterface
34
+ {
35
+ /** @type {http.IncomingMessage} */ IncomingMessage;
36
+ /** @type {Route} */ current;
37
+ /** @type {import("../TurboServer")} */ instance;
38
+ /** @type {Map<String, import("mongoose").Model>} */ models;
39
+
40
+ /**
41
+ * @type {Map<String, *>}
42
+ * @doc protected variable
43
+ * usually it will be Map<String, String>
44
+ * but also can be <String, Number> or <String, Boolean>
45
+ */
46
+ _params;
47
+
48
+ /**
49
+ * @type {Buffer}
50
+ * @doc protected variable
51
+ */
52
+ _body = Buffer.alloc(0);
53
+
54
+ /**
55
+ * @type {FileType[]}
56
+ * @doc protected variable
57
+ */
58
+ _files = new Array();
59
+
60
+ /**
61
+ * @type {ValidationSchema[]}
62
+ * @doc protected variable
63
+ */
64
+ _validations = new Array();
65
+
66
+ /**
67
+ * @type {Map<String, any>}
68
+ * @doc protected variable
69
+ */
70
+ validData = new Map();
71
+
72
+ /**
73
+ * @type {ValidationSchema[]}
74
+ * @doc protected variable
75
+ */
76
+ _validation_errors = new Array();
77
+
78
+ /**
79
+ * @type {Boolean}
80
+ * @doc protected variable
81
+ */
82
+ bodyFetched = false;
83
+
84
+ /**
85
+ * @param {http.IncomingMessage} req
86
+ * @param {Route} current
87
+ * @param {import("../TurboServer")} instance
88
+ */
89
+ constructor(req, current, instance)
90
+ {
91
+ this.current = current;
92
+ this.IncomingMessage = req;
93
+ this.state = new Map();
94
+ this.instance = instance;
95
+ this.models = instance.models;
96
+ }
97
+
98
+ addValidation (validation) {
99
+ if(validation instanceof ValidationSchema === true) {
100
+ this._validations.push(validation);
101
+ }
102
+ }
103
+
104
+ async execute_validation (res, injectedData, next) {
105
+ const r = await this.validateIncomingData();
106
+ if(r === -1) return res.status(400).json({ errors: this._validation_errors })
107
+ next();
108
+ }
109
+
110
+ runmethod_addvalidation (res, injectedData, next) {
111
+ this.addValidation(injectedData.validation);
112
+ next()
113
+ }
114
+
115
+ getAppState () {
116
+ return this.instance.appState;
117
+ }
118
+
119
+ validateDataObj () {
120
+ let obj = {};
121
+ this.validData.forEach((v, k) => obj[k] = v);
122
+ return obj;
123
+ }
124
+
125
+
126
+ /**
127
+ *
128
+ * @param {String} name
129
+ * @returns {import("mongoose").Model}
130
+ */
131
+ getModel (name) {
132
+ return this.models.get(name) || null;
133
+ }
134
+
135
+ files () {
136
+
137
+ }
138
+
139
+ /**
140
+ * @returns {Number} 0 | -1 for error;
141
+ */
142
+ async validateIncomingData() {
143
+ await this.body();
144
+ for (let index = 0; index < this._validations.length; index++) await this._validations[index].validate(this);
145
+ if(this._validation_errors.length > 0) return -1;
146
+ return 0;
147
+ }
148
+
149
+ // ! methods not implemented //
150
+
151
+ /** @require_implemetation @returns {String} token */
152
+ getBearerAuth() {
153
+ console.log("You have not implemeneted method 'getBearerAuth'");
154
+ }
155
+ /** @require_implemetation @returns {BasicAuth} {user:str, pass:str} */
156
+ getBasicAuth() {
157
+ console.log("You have not implemeneted method 'getBasicAuth'");
158
+ }
159
+ /** @require_implemetation @returns {Object} */
160
+ getBearerJWTData() {
161
+ console.log("You have not implemeneted method 'getBearerJWTData'");
162
+ }
163
+ /** @require_implemetation @returns {Number} 0 ok or -1 for error */
164
+ validateFormdata() {
165
+ console.log("You have not implemeneted method 'validateFormdata'");
166
+ }
167
+
168
+ /**
169
+ * @interface_method
170
+ * @param {String | Buffer} rowData
171
+ * @returns {String} xml string
172
+ */
173
+ getXmlBody(rowData) {
174
+ console.log("You have not implemeneted method 'getXmlBody'");
175
+ return rowData;
176
+ }
177
+
178
+ /**
179
+ * @interface_method
180
+ * @doc - you can override this method;
181
+ * @param {String | Buffer} rowData
182
+ * @returns {Object | Object[]}
183
+ */
184
+ getFormUrlEncoded(rowData) {
185
+ console.log("You have not implemeneted method 'getFormUrlEncoded'");
186
+ return rowData;
187
+ }
188
+
189
+ /**
190
+ * @interface_method
191
+ * @doc - you can override this method;
192
+ * @param {String | Buffer} rowData
193
+ * @returns {*} default is string
194
+ */
195
+ getDefaultBody(rowData) {return rowData.toString()}
196
+
197
+ // ? implemented methods //
198
+
199
+ async genericBody()
200
+ {
201
+ if(this.bodyFetched === true) return this._body.toString(); // on 1 milion loops -> gain ~100ms performance;
202
+ return new Promise((resolve, reject) => {
203
+ let data = [];
204
+ this.IncomingMessage.on("data", (e) => {
205
+ data.push(e);
206
+ });
207
+ this.IncomingMessage.on("end", (e) => {
208
+ this.bodyFetched = true;
209
+ this._body = Buffer.concat(data);
210
+ resolve(this._body.toString());
211
+ });
212
+ });
213
+ }
214
+
215
+ /**
216
+ * @doc some methods are not implmenent by default
217
+ * @returns {*} base on headers["content-type"]
218
+ */
219
+ async body()
220
+ {
221
+ switch (this.headers()["content-type"]) {
222
+ case "application/json":
223
+ return this.getJsonBody(await this.genericBody());
224
+ case "application/javascript":
225
+ return this.getJavascriptBody(await this.genericBody());
226
+ case "application/xml":
227
+ return this.getXmlBody(await this.genericBody());
228
+ case "text/html":
229
+ return this.getHtmlBody(await this.genericBody());
230
+ case "text/plain":
231
+ return (await this.genericBody()).toString();
232
+ case "application/x-www-form-urlencoded":
233
+ return this.getFormUrlEncoded(await this.genericBody());
234
+ default:
235
+ return this.getDefaultBody(await this.genericBody());
236
+ break;
237
+ }
238
+ }
239
+
240
+ /**
241
+ * @param {String | Buffer} rowData
242
+ * @returns {Object | Object[]}
243
+ */
244
+ getJsonBody(rowData)
245
+ {
246
+ return JSON.parse(rowData.toString());
247
+ }
248
+
249
+ /**
250
+ * @param {String | Buffer} rowData
251
+ * @returns {String}
252
+ */
253
+ getHtmlBody(rowData)
254
+ {
255
+ return rowData.toString();
256
+ }
257
+
258
+ /**
259
+ * @doc - you can override this method;
260
+ * @param {String | Buffer} rowData
261
+ * @returns {String} string of javascript code;
262
+ */
263
+ getJavascriptBody(rowData)
264
+ {
265
+ return rowData.toString();
266
+ }
267
+
268
+ /**
269
+ * @doc - the first time this function is running, will cache the params for next use;
270
+ * @returns {Map<String, Object>}
271
+ */
272
+ params()
273
+ {
274
+ if (this._params) return this._params; // ! this line improving performance 2-3x times per 1000 lopping params();
275
+ this._params = new Map();
276
+ this.current.dynamic_path_parts.map((i) => {
277
+ this._params.set(i.property, this.current.current_path_parts[i.index]);
278
+ });
279
+ return this._params;
280
+ }
281
+
282
+ /**
283
+ * @doc - the first time this function is running, will cache the params for next use;
284
+ * @returns {Object} Object
285
+ */
286
+ paramsObj()
287
+ {
288
+ let returnObj = new Object();
289
+ if (this._params) { // ! this line improving performance 2-3x times per 1000 lopping params();
290
+ this._params.forEach((v, k) => returnObj[k] = v);
291
+ return returnObj;
292
+ }
293
+ this._params = new Map();
294
+ this.current.dynamic_path_parts.map((i) => {
295
+ this._params.set(i.property, this.current.current_path_parts[i.index]);
296
+ });
297
+ this._params.forEach((v, k) => returnObj[k] = v);
298
+ return returnObj;
299
+ }
300
+
301
+ /** @returns {URLSearchParams} URLSearchParams */
302
+ queries()
303
+ {
304
+ return new URLSearchParams(this.IncomingMessage.url.split("?")[1] || "");
305
+ }
306
+
307
+ /** @returns {Object} Object */
308
+ queriesObj ()
309
+ {
310
+ let queriesObj = new Object()
311
+ new URLSearchParams(this.IncomingMessage.url.split("?")[1] || "").forEach((v, k) => {
312
+ if(queriesObj[k]) {
313
+ if(queriesObj[k] instanceof Array === true) {
314
+ queriesObj[k].push(v);
315
+ } else {
316
+ queriesObj[k] = [queriesObj[k], v];
317
+ }
318
+ } else {
319
+ queriesObj[k] = v
320
+ }
321
+ });
322
+ return queriesObj;
323
+ }
324
+
325
+ /** @returns {http.IncomingMessage.headers} http.IncomingMessage.headers */
326
+ headers()
327
+ {
328
+ return this.IncomingMessage.headers;
329
+ }
330
+
331
+ /** @returns {String} */
332
+ method()
333
+ {
334
+ return this.current.method;
335
+ }
336
+
337
+ /** @returns {String} */
338
+ httpVersion()
339
+ {
340
+ return this.IncomingMessage.httpVersion;
341
+ }
342
+
343
+ /** @returns {String} */
344
+ remoteAddress()
345
+ {
346
+ return this.IncomingMessage.socket.remoteAddress;
347
+ }
348
+
349
+ /** @returns {Number} */
350
+ remotePort()
351
+ {
352
+ return this.IncomingMessage.socket.remotePort;
353
+ }
354
+
355
+ /** @returns {String} */
356
+ localAddress()
357
+ {
358
+ return this.IncomingMessage.socket.localAddress;
359
+ }
360
+
361
+ /** @returns {Number} */
362
+ localPort()
363
+ {
364
+ return this.IncomingMessage.socket.localPort;
365
+ }
366
+
367
+ /** @returns {String} */
368
+ localPort()
369
+ {
370
+ return this.IncomingMessage.socket.localPort;
371
+ }
372
+
373
+ /** @returns {String} */
374
+ url()
375
+ {
376
+ return this.IncomingMessage.url;
377
+ }
378
+
379
+ /**
380
+ * @param {String} key
381
+ * @param {*} value
382
+ * @returns {void}
383
+ */
384
+ setState(key, value)
385
+ {
386
+ this.state.set(key, value);
387
+ }
388
+
389
+ /**
390
+ * @param {String} key
391
+ * @returns {* | null}
392
+ */
393
+ useState(key)
394
+ {
395
+ return this.state.get(key) || null;
396
+ }
397
+
398
+ };