@webtypen/webframez-core 0.1.0 → 0.1.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/dev/app.ts CHANGED
@@ -32,6 +32,11 @@ app.boot({
32
32
  });
33
33
  });
34
34
 
35
+ Route.get("/download", async (req: Request, res: Response) => {
36
+ const file = path.join(__dirname, "uploads", "2024-06-09_test.zip");
37
+ return await res.download(file);
38
+ });
39
+
35
40
  Route.post("/fileupload", (req: Request, res: Response) => {
36
41
  if (!req.message) {
37
42
  throw Error("Missing IncomingMessage ...");
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { IncomingMessage } from "http";
3
3
  export declare class Request {
4
+ [key: string]: any;
4
5
  method: string;
5
6
  url: string;
6
7
  headers: any;
@@ -1,19 +1,4 @@
1
1
  "use strict";
2
- // export type Request = {
3
- // [key: string]: any;
4
- // body?: any;
5
- // bodyPlain?: any;
6
- // params: object;
7
- // httpVersionMajor: any;
8
- // httpVersionMinor: any;
9
- // httpVersion: any;
10
- // headers: object;
11
- // rawHeaders: object;
12
- // url: string;
13
- // method: string;
14
- // on?: any;
15
- // request?: any;
16
- // };
17
2
  Object.defineProperty(exports, "__esModule", { value: true });
18
3
  exports.Request = void 0;
19
4
  class Request {
@@ -42,6 +42,7 @@ export declare class Response {
42
42
  * @returns Response
43
43
  */
44
44
  send(content: any): this;
45
+ download(filepath: string, options?: any): Promise<unknown>;
45
46
  end(): this;
46
47
  registerEvent(eventKey: string, func: any): Promise<void>;
47
48
  /**
@@ -8,8 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.Response = void 0;
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const mime_types_1 = __importDefault(require("mime-types"));
13
19
  class Response {
14
20
  constructor(options) {
15
21
  this.statusCode = 200;
@@ -67,22 +73,58 @@ class Response {
67
73
  * @returns Response
68
74
  */
69
75
  send(content) {
70
- var _a, _b, _c;
76
+ var _a, _b, _c, _d;
71
77
  this.content = content;
72
78
  if (this.mode === "aws-lambda") {
73
79
  // Do nothing ... Store content in variable and use it later ...
74
80
  }
75
81
  else {
76
82
  if (typeof content === "object" && !Buffer.isBuffer(content)) {
77
- (_a = this.res) === null || _a === void 0 ? void 0 : _a.setHeader("Content-Type", "application/json");
78
- (_b = this.res) === null || _b === void 0 ? void 0 : _b.write(JSON.stringify(content));
83
+ if (!((_a = this.res) === null || _a === void 0 ? void 0 : _a.headersSent)) {
84
+ (_b = this.res) === null || _b === void 0 ? void 0 : _b.setHeader("Content-Type", "application/json");
85
+ }
86
+ (_c = this.res) === null || _c === void 0 ? void 0 : _c.write(JSON.stringify(content));
79
87
  }
80
88
  else {
81
- (_c = this.res) === null || _c === void 0 ? void 0 : _c.write(content);
89
+ (_d = this.res) === null || _d === void 0 ? void 0 : _d.write(content);
82
90
  }
83
91
  }
84
92
  return this;
85
93
  }
94
+ download(filepath, options) {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ return new Promise((resolve, reject) => {
97
+ if (!this.res) {
98
+ return reject("Cannot download: Original response object is missing");
99
+ }
100
+ if (!fs_1.default.existsSync(filepath)) {
101
+ return reject("Cannot download: File '" + filepath + "' does not exist");
102
+ }
103
+ let mimeType = options && options.contentType ? options.contentType : "application/octet-stream";
104
+ if (options && options.inline) {
105
+ let tempMime = mime_types_1.default.lookup(filepath);
106
+ if (tempMime) {
107
+ mimeType = tempMime;
108
+ }
109
+ }
110
+ const stats = fs_1.default.lstatSync(filepath);
111
+ this.header("Content-Disposition", (options && options.inline ? "inline" : "attachment") +
112
+ "; filename=" +
113
+ (options && options.filename ? options.filename : path_1.default.basename(filepath)));
114
+ this.header("Content-Type", mimeType);
115
+ this.header("Content-Length", stats.size.toString());
116
+ const readStream = fs_1.default.createReadStream(filepath);
117
+ readStream.pipe(this.res);
118
+ readStream.on("close", () => {
119
+ resolve();
120
+ });
121
+ readStream.on("error", (streamErr) => {
122
+ console.error(streamErr);
123
+ reject(streamErr);
124
+ });
125
+ });
126
+ });
127
+ }
86
128
  end() {
87
129
  var _a;
88
130
  (_a = this.res) === null || _a === void 0 ? void 0 : _a.end();
@@ -4,7 +4,9 @@ import { Response } from "./Response";
4
4
  import { Request } from "./Request";
5
5
  type RouteObject = {
6
6
  path: string;
7
- component: any;
7
+ component?: any;
8
+ controller?: any;
9
+ method_name?: string;
8
10
  options: {
9
11
  [key: string]: any;
10
12
  };
@@ -69,9 +71,17 @@ declare class RouterFacade {
69
71
  };
70
72
  /**
71
73
  * Load the application-routes
72
- *
73
74
  */
74
75
  init(options?: any): void;
76
+ /**
77
+ * Dissolves 'MyController@myFunction'
78
+ *
79
+ * @param str
80
+ */
81
+ dissolveStringComponent(str: string, path: string): {
82
+ controller: any;
83
+ method_name: string;
84
+ };
75
85
  /**
76
86
  * Register a route
77
87
  *
@@ -50,7 +50,6 @@ class RouterFacade {
50
50
  }
51
51
  /**
52
52
  * Load the application-routes
53
- *
54
53
  */
55
54
  init(options) {
56
55
  this.basename = options && options.basename ? options.basename : null;
@@ -64,6 +63,25 @@ class RouterFacade {
64
63
  require(process.cwd() + "/app/routes");
65
64
  }
66
65
  }
66
+ /**
67
+ * Dissolves 'MyController@myFunction'
68
+ *
69
+ * @param str
70
+ */
71
+ dissolveStringComponent(str, path) {
72
+ const split = str.split("@");
73
+ if (!split || !split[0] || split[0].trim() === "" || !split[1] || split[1].trim() === "") {
74
+ throw new Error("Missing Controller Route for '" + path + "'");
75
+ }
76
+ const controller = this.kernel.controller[split[0]];
77
+ if (!controller) {
78
+ throw new Error("Could not dissvole Controller '" + split[0] + "' from route ' + path+ '");
79
+ }
80
+ return {
81
+ controller: controller,
82
+ method_name: split[1],
83
+ };
84
+ }
67
85
  /**
68
86
  * Register a route
69
87
  *
@@ -76,33 +94,29 @@ class RouterFacade {
76
94
  if (this.basename) {
77
95
  path = this.basename + path;
78
96
  }
97
+ let routeConf = {
98
+ path: path,
99
+ options: options,
100
+ };
101
+ if (typeof component === "string") {
102
+ const stringComponent = this.dissolveStringComponent(component, path);
103
+ routeConf.controller = stringComponent.controller;
104
+ routeConf.method_name = stringComponent.method_name;
105
+ }
106
+ else {
107
+ routeConf.component = component;
108
+ }
79
109
  if (type === "GET") {
80
- this.routesGET[path] = {
81
- path: path,
82
- component: component,
83
- options: options,
84
- };
110
+ this.routesGET[path] = routeConf;
85
111
  }
86
112
  else if (type === "POST") {
87
- this.routesPOST[path] = {
88
- path: path,
89
- component: component,
90
- options: options,
91
- };
113
+ this.routesPOST[path] = routeConf;
92
114
  }
93
115
  else if (type === "PUT") {
94
- this.routesPUT[path] = {
95
- path: path,
96
- component: component,
97
- options: options,
98
- };
116
+ this.routesPUT[path] = routeConf;
99
117
  }
100
118
  else if (type === "DELETE") {
101
- this.routesDELETE[path] = {
102
- path: path,
103
- component: component,
104
- options: options,
105
- };
119
+ this.routesDELETE[path] = routeConf;
106
120
  }
107
121
  }
108
122
  /**
@@ -209,7 +223,7 @@ class RouterFacade {
209
223
  if (!route) {
210
224
  return this.handleError(request, response, 404, "Not found ...");
211
225
  }
212
- if (!route.component) {
226
+ if (!(route.component && typeof route.component === "function") && !(route.controller && route.method_name)) {
213
227
  return this.handleError(request, response, 404, "Missing route function ...");
214
228
  }
215
229
  try {
@@ -217,7 +231,17 @@ class RouterFacade {
217
231
  if (request.method === "OPTIONS" && !request.skipOptionsForward) {
218
232
  return this.handleReturn(request, response, "");
219
233
  }
220
- const result = yield route.component(request, response);
234
+ let result = null;
235
+ if (route.controller && route.method_name) {
236
+ const controllerInstance = new route.controller();
237
+ if (!controllerInstance[route.method_name]) {
238
+ return this.handleError(request, response, 404, "Unknown method '" + route.method_name + "'.");
239
+ }
240
+ result = yield controllerInstance[route.method_name].bind(controllerInstance)(request, response);
241
+ }
242
+ else {
243
+ result = yield route.component(request, response);
244
+ }
221
245
  this.handleReturn(request, response, result);
222
246
  }
223
247
  catch (e) {
@@ -289,7 +313,8 @@ class RouterFacade {
289
313
  // Content-Type Verarbeitung
290
314
  if (contentType.includes("application/json")) {
291
315
  try {
292
- resolve({ plain: body, parsed: JSON.parse(body.toString()) });
316
+ const text = body.toString();
317
+ resolve({ plain: body, parsed: text && text.trim() !== "" ? JSON.parse(text) : {} });
293
318
  }
294
319
  catch (e) {
295
320
  reject(new Error("Invalid JSON"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",
@@ -19,11 +19,13 @@
19
19
  },
20
20
  "scripts": {
21
21
  "build": "tsc",
22
- "build:check": "tsc --noEmit"
22
+ "build:check": "tsc --noEmit",
23
+ "dev": "PORT=3210 ts-node ./dev/app.ts"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/busboy": "^1.5.4",
26
27
  "@types/lodash": "^4.14.190",
28
+ "@types/mime-types": "^2.1.4",
27
29
  "@types/node": "^18.11.9",
28
30
  "@types/qs": "^6.9.7",
29
31
  "ts-node": "^10.9.1",
@@ -34,6 +36,7 @@
34
36
  "commander": "^9.4.1",
35
37
  "dotenv": "^8.2.0",
36
38
  "lodash": "^4.17.20",
39
+ "mime-types": "^2.1.35",
37
40
  "qs": "^6.11.0",
38
41
  "querystring": "^0.2.1"
39
42
  }
@@ -1,22 +1,7 @@
1
- // export type Request = {
2
- // [key: string]: any;
3
- // body?: any;
4
- // bodyPlain?: any;
5
- // params: object;
6
- // httpVersionMajor: any;
7
- // httpVersionMinor: any;
8
- // httpVersion: any;
9
- // headers: object;
10
- // rawHeaders: object;
11
- // url: string;
12
- // method: string;
13
- // on?: any;
14
- // request?: any;
15
- // };
16
-
17
1
  import { IncomingMessage } from "http";
18
2
 
19
3
  export class Request {
4
+ [key: string]: any;
20
5
  method: string = "GET";
21
6
  url: string = "";
22
7
  headers: any = {};
@@ -1,3 +1,6 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import mime from "mime-types";
1
4
  import { ServerResponse } from "http";
2
5
 
3
6
  export class Response {
@@ -75,7 +78,9 @@ export class Response {
75
78
  // Do nothing ... Store content in variable and use it later ...
76
79
  } else {
77
80
  if (typeof content === "object" && !Buffer.isBuffer(content)) {
78
- this.res?.setHeader("Content-Type", "application/json");
81
+ if (!this.res?.headersSent) {
82
+ this.res?.setHeader("Content-Type", "application/json");
83
+ }
79
84
  this.res?.write(JSON.stringify(content));
80
85
  } else {
81
86
  this.res?.write(content);
@@ -84,6 +89,48 @@ export class Response {
84
89
  return this;
85
90
  }
86
91
 
92
+ async download(filepath: string, options?: any) {
93
+ return new Promise((resolve: Function, reject: Function) => {
94
+ if (!this.res) {
95
+ return reject("Cannot download: Original response object is missing");
96
+ }
97
+
98
+ if (!fs.existsSync(filepath)) {
99
+ return reject("Cannot download: File '" + filepath + "' does not exist");
100
+ }
101
+
102
+ let mimeType = options && options.contentType ? options.contentType : "application/octet-stream";
103
+ if (options && options.inline) {
104
+ let tempMime = mime.lookup(filepath);
105
+ if (tempMime) {
106
+ mimeType = tempMime;
107
+ }
108
+ }
109
+
110
+ const stats = fs.lstatSync(filepath);
111
+ this.header(
112
+ "Content-Disposition",
113
+ (options && options.inline ? "inline" : "attachment") +
114
+ "; filename=" +
115
+ (options && options.filename ? options.filename : path.basename(filepath))
116
+ );
117
+ this.header("Content-Type", mimeType);
118
+ this.header("Content-Length", stats.size.toString());
119
+
120
+ const readStream = fs.createReadStream(filepath);
121
+ readStream.pipe(this.res);
122
+
123
+ readStream.on("close", () => {
124
+ resolve();
125
+ });
126
+
127
+ readStream.on("error", (streamErr) => {
128
+ console.error(streamErr);
129
+ reject(streamErr);
130
+ });
131
+ });
132
+ }
133
+
87
134
  end() {
88
135
  this.res?.end();
89
136
  return this;
@@ -5,7 +5,9 @@ import querystring from "querystring";
5
5
 
6
6
  type RouteObject = {
7
7
  path: string;
8
- component: any;
8
+ component?: any;
9
+ controller?: any;
10
+ method_name?: string;
9
11
  options: { [key: string]: any };
10
12
  params: object;
11
13
  };
@@ -76,7 +78,6 @@ class RouterFacade {
76
78
 
77
79
  /**
78
80
  * Load the application-routes
79
- *
80
81
  */
81
82
  init(options?: any) {
82
83
  this.basename = options && options.basename ? options.basename : null;
@@ -91,6 +92,29 @@ class RouterFacade {
91
92
  }
92
93
  }
93
94
 
95
+ /**
96
+ * Dissolves 'MyController@myFunction'
97
+ *
98
+ * @param str
99
+ */
100
+ dissolveStringComponent(str: string, path: string) {
101
+ const split = str.split("@");
102
+
103
+ if (!split || !split[0] || split[0].trim() === "" || !split[1] || split[1].trim() === "") {
104
+ throw new Error("Missing Controller Route for '" + path + "'");
105
+ }
106
+
107
+ const controller = this.kernel.controller[split[0]];
108
+ if (!controller) {
109
+ throw new Error("Could not dissvole Controller '" + split[0] + "' from route ' + path+ '");
110
+ }
111
+
112
+ return {
113
+ controller: controller,
114
+ method_name: split[1],
115
+ };
116
+ }
117
+
94
118
  /**
95
119
  * Register a route
96
120
  *
@@ -104,30 +128,27 @@ class RouterFacade {
104
128
  path = this.basename + path;
105
129
  }
106
130
 
131
+ let routeConf: any = {
132
+ path: path,
133
+ options: options,
134
+ };
135
+
136
+ if (typeof component === "string") {
137
+ const stringComponent = this.dissolveStringComponent(component, path);
138
+ routeConf.controller = stringComponent.controller;
139
+ routeConf.method_name = stringComponent.method_name;
140
+ } else {
141
+ routeConf.component = component;
142
+ }
143
+
107
144
  if (type === "GET") {
108
- this.routesGET[path] = {
109
- path: path,
110
- component: component,
111
- options: options,
112
- };
145
+ this.routesGET[path] = routeConf;
113
146
  } else if (type === "POST") {
114
- this.routesPOST[path] = {
115
- path: path,
116
- component: component,
117
- options: options,
118
- };
147
+ this.routesPOST[path] = routeConf;
119
148
  } else if (type === "PUT") {
120
- this.routesPUT[path] = {
121
- path: path,
122
- component: component,
123
- options: options,
124
- };
149
+ this.routesPUT[path] = routeConf;
125
150
  } else if (type === "DELETE") {
126
- this.routesDELETE[path] = {
127
- path: path,
128
- component: component,
129
- options: options,
130
- };
151
+ this.routesDELETE[path] = routeConf;
131
152
  }
132
153
  }
133
154
 
@@ -238,7 +259,7 @@ class RouterFacade {
238
259
  return this.handleError(request, response, 404, "Not found ...");
239
260
  }
240
261
 
241
- if (!route.component) {
262
+ if (!(route.component && typeof route.component === "function") && !(route.controller && route.method_name)) {
242
263
  return this.handleError(request, response, 404, "Missing route function ...");
243
264
  }
244
265
 
@@ -249,7 +270,18 @@ class RouterFacade {
249
270
  return this.handleReturn(request, response, "");
250
271
  }
251
272
 
252
- const result = await route.component(request, response);
273
+ let result = null;
274
+ if (route.controller && route.method_name) {
275
+ const controllerInstance = new route.controller();
276
+
277
+ if (!controllerInstance[route.method_name]) {
278
+ return this.handleError(request, response, 404, "Unknown method '" + route.method_name + "'.");
279
+ }
280
+
281
+ result = await controllerInstance[route.method_name].bind(controllerInstance)(request, response);
282
+ } else {
283
+ result = await route.component(request, response);
284
+ }
253
285
  this.handleReturn(request, response, result);
254
286
  } catch (e: any) {
255
287
  return this.handleError(request, response, 500, e);
@@ -320,7 +352,8 @@ class RouterFacade {
320
352
  // Content-Type Verarbeitung
321
353
  if (contentType.includes("application/json")) {
322
354
  try {
323
- resolve({ plain: body, parsed: JSON.parse(body.toString()) });
355
+ const text = body.toString();
356
+ resolve({ plain: body, parsed: text && text.trim() !== "" ? JSON.parse(text) : {} });
324
357
  } catch (e) {
325
358
  reject(new Error("Invalid JSON"));
326
359
  }