mastercontroller 1.2.1 → 1.2.3

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/MasterAction.js CHANGED
@@ -133,6 +133,18 @@ class MasterAction{
133
133
  }
134
134
  }
135
135
 
136
+ returnReact(data, location){
137
+
138
+ var masterView = null;
139
+ data = data === undefined ? {} : data;
140
+ this.params = this.params === undefined ? {} : this.params;
141
+ this.params = tools.combineObjects(data, this.params);
142
+ var func = master.viewList;
143
+ this.params = tools.combineObjects(this.params, func);
144
+ var html = master.reactView.compile(this.__currentRoute.toController, this.__currentRoute.toAction, this.__currentRoute.root);
145
+
146
+ }
147
+
136
148
  returnView(data, location){
137
149
 
138
150
  var masterView = null;
@@ -165,7 +177,32 @@ class MasterAction{
165
177
  response.end(end);
166
178
  }
167
179
 
180
+ // Utility method to check if response is ready for writing
181
+ // Returns true if safe to continue, false if response already sent
182
+ waitUntilReady(){
183
+ // Check the primary response object first (matches existing returnJson pattern)
184
+ if (this.__response) {
185
+ return !this.__response._headerSent;
186
+ }
187
+ // Check request object response as fallback (matches existing redirectTo pattern)
188
+ if (this.__requestObject && this.__requestObject.response) {
189
+ return !this.__requestObject.response._headerSent;
190
+ }
191
+ // If neither exists, assume it's safe to continue (early in request lifecycle)
192
+ return true;
193
+ }
194
+
195
+ // Enhanced returnJson that checks readiness first
196
+ safeReturnJson(data){
197
+ if (this.waitUntilReady()) {
198
+ this.returnJson(data);
199
+ return true;
200
+ }
201
+ console.warn('Attempted to send JSON response but headers already sent');
202
+ return false;
203
+ }
204
+
168
205
 
169
206
  }
170
207
 
171
- master.extendController(MasterAction);
208
+ master.extendController(MasterAction);
@@ -1,4 +1,4 @@
1
- // version 1.6
1
+ // version 1.7
2
2
  var master = require('./MasterControl');
3
3
 
4
4
  var _beforeActionFunc = {
@@ -54,7 +54,9 @@ class MasterActionFilters {
54
54
  var flag = false;
55
55
  if(_beforeActionFunc.namespace === obj.__namespace){
56
56
  for (var a = 0; a < _beforeActionFunc.actionList.length; a++) {
57
- if(_beforeActionFunc.actionList[a] === request.toAction){
57
+ var action = request.toAction.replace(/\s/g, '');
58
+ var incomingAction = _beforeActionFunc.actionList[a].replace(/\s/g, '');
59
+ if(incomingAction === action){
58
60
  flag = true;
59
61
  }
60
62
  }
@@ -65,7 +67,9 @@ class MasterActionFilters {
65
67
  __callBeforeAction(obj, request, emitter) {
66
68
  if(_beforeActionFunc.namespace === obj.__namespace){
67
69
  _beforeActionFunc.actionList.forEach(action => {
68
- if(action === request.toAction){
70
+ var action = action.replace(/\s/g, '');
71
+ var reqAction = request.toAction.replace(/\s/g, '');
72
+ if(action === reqAction){
69
73
  emit = emitter;
70
74
  // call function inside controller
71
75
  _beforeActionFunc.callBack.call(_beforeActionFunc.that, request);
@@ -77,7 +81,9 @@ class MasterActionFilters {
77
81
  __callAfterAction(obj, request) {
78
82
  if(_afterActionFunc.namespace === obj.__namespace){
79
83
  _afterActionFunc.actionList.forEach(action => {
80
- if(action === request.toAction){
84
+ var action = action.replace(/\s/g, '');
85
+ var reqAction = request.toAction.replace(/\s/g, '');
86
+ if(action === reqAction){
81
87
  _afterActionFunc.callBack.call(_afterActionFunc.that, request);
82
88
  }
83
89
  });
package/MasterControl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // MasterControl - by Alexander rich
2
- // version 1.0.22
2
+ // version 1.0.245
3
3
 
4
4
  var url = require('url');
5
5
  var fileserver = require('fs');
@@ -191,28 +191,71 @@ class MasterControl {
191
191
 
192
192
  // sets up https or http server protocals
193
193
  setupServer(type, credentials ){
194
- var $that = this;
195
- if(type === "http"){
196
- $that.serverProtocol = "http";
197
- return http.createServer(async function(req, res) {
198
- $that.serverRun(req, res);
199
- });
200
- }
201
- if(type === "https"){
202
- $that.serverProtocol = "https";
203
- if(credentials){
204
- return https.createServer(credentials, async function(req, res) {
194
+ try {
195
+ var $that = this;
196
+ if(type === "http"){
197
+ $that.serverProtocol = "http";
198
+ return http.createServer(async function(req, res) {
205
199
  $that.serverRun(req, res);
206
- });
207
- }else{
208
- throw "Credentials needed to setup https"
200
+ });
201
+ }
202
+ if(type === "https"){
203
+ $that.serverProtocol = "https";
204
+ if(credentials){
205
+ return https.createServer(credentials, async function(req, res) {
206
+ $that.serverRun(req, res);
207
+ });
208
+ }else{
209
+ throw "Credentials needed to setup https"
210
+ }
209
211
  }
210
212
  }
213
+ catch(error){
214
+ console.error("Failed to setup server:", error);
215
+ throw error;
216
+ }
211
217
  }
212
218
 
213
219
  async serverRun(req, res){
214
220
  var $that = this;
215
221
  console.log("path", `${req.method} ${req.url}`);
222
+
223
+ // Handle CORS preflight (OPTIONS) requests early and positively
224
+ if (req.method === 'OPTIONS') {
225
+ try {
226
+ if (this.cors && typeof this.cors.load === 'function') {
227
+ if (!this.cors.options) {
228
+ this.cors.init({
229
+ origin: true,
230
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
231
+ allowedHeaders: true,
232
+ credentials: false,
233
+ maxAge: 86400
234
+ });
235
+ }
236
+ this.cors.load({ request: req, response: res });
237
+ } else {
238
+ res.setHeader('access-control-allow-origin', '*');
239
+ res.setHeader('access-control-allow-methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
240
+ if (req.headers['access-control-request-headers']) {
241
+ res.setHeader('access-control-allow-headers', req.headers['access-control-request-headers']);
242
+ }
243
+ res.setHeader('access-control-max-age', '86400');
244
+ }
245
+ } catch (e) {
246
+ res.setHeader('access-control-allow-origin', '*');
247
+ res.setHeader('access-control-allow-methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
248
+ if (req.headers['access-control-request-headers']) {
249
+ res.setHeader('access-control-allow-headers', req.headers['access-control-request-headers']);
250
+ }
251
+ res.setHeader('access-control-max-age', '86400');
252
+ }
253
+ res.statusCode = 204;
254
+ res.setHeader('content-length', '0');
255
+ res.end();
256
+ return;
257
+ }
258
+
216
259
  // parse URL
217
260
  const parsedUrl = url.parse(req.url);
218
261
  // extract URL path
@@ -221,6 +264,9 @@ class MasterControl {
221
264
  // based on the URL path, extract the file extension. e.g. .js, .doc, ...
222
265
  const ext = path.parse(pathname).ext;
223
266
 
267
+ // handle simple preflight configuration - might need a complex approch for all scenarios
268
+
269
+
224
270
  // if extension exist then its a file.
225
271
  if(ext === ""){
226
272
  var requestObject = await this.middleware(req, res);
@@ -228,11 +274,14 @@ class MasterControl {
228
274
  var loadedDone = false;
229
275
  if (typeof $that._loadedFunc === 'function') {
230
276
  loadedDone = $that._loadedFunc(requestObject);
231
-
277
+ if (loadedDone){
278
+ require(`${this.root}/config/load`)(requestObject);
279
+ }
232
280
  }
233
- if (loadedDone){
281
+ else{
234
282
  require(`${this.root}/config/load`)(requestObject);
235
283
  }
284
+
236
285
 
237
286
  }
238
287
  }
@@ -315,6 +364,7 @@ class MasterControl {
315
364
  return -1;
316
365
  }
317
366
  else{
367
+
318
368
  var params = await this.request.getRequestParam(request, response);
319
369
  return {
320
370
  request : request,
package/MasterCors.js CHANGED
@@ -1,4 +1,4 @@
1
- // version 1.1.10
1
+ // version 0.0.3 - robust origin handling (all envs), creds-safe reflection, function origins, extended Vary
2
2
  var master = require('./MasterControl');
3
3
 
4
4
  // todo - res.setHeader('Access-Control-Request-Method', '*');
@@ -16,7 +16,9 @@ class MasterCors{
16
16
  load(params){
17
17
  if(params){
18
18
  this.response = params.response;
19
- this.request = params. request;
19
+ this.request = params.request;
20
+ // Always signal that response may vary by Origin and requested headers/method
21
+ try { this.response.setHeader('Vary', 'Origin, Access-Control-Request-Headers, Access-Control-Request-Method'); } catch(_) {}
20
22
  this.configureOrigin();
21
23
  this.configureMethods()
22
24
  this.configureAllowedHeaders();
@@ -46,7 +48,13 @@ class MasterCors{
46
48
  }
47
49
 
48
50
  if(this.options.origin === true){
49
- this.setHeader('access-control-allow-origin', '*');
51
+ // If credentials are enabled, reflect request origin per spec
52
+ var requestOrigin = this.request.headers.origin;
53
+ if (this.options.credentials === true && requestOrigin) {
54
+ this.setHeader('access-control-allow-origin', requestOrigin);
55
+ } else {
56
+ this.setHeader('access-control-allow-origin', '*');
57
+ }
50
58
  }
51
59
 
52
60
  // remove all origins
@@ -54,11 +62,29 @@ class MasterCors{
54
62
  this.removeHeader('access-control-allow-origin');
55
63
  }
56
64
 
57
- if(this.options.origin.constructor === Array){
58
- for (const element of this.options.origin) {
59
- this.setHeader('access-control-allow-origin', element);
65
+ if(this.options.origin.constructor === Array){
66
+ // Get the origin from the incoming request
67
+ var requestOrigin = this.request.headers.origin;
68
+
69
+ // Check if the request origin is in our allowed list
70
+ if(requestOrigin && this.options.origin.includes(requestOrigin)){
71
+ this.setHeader('access-control-allow-origin', requestOrigin);
60
72
  }
61
-
73
+ // If no specific origin matches, don't set the header
74
+ }
75
+
76
+ // Function predicate support: (origin, req) => boolean|string
77
+ if (typeof this.options.origin === 'function'){
78
+ try {
79
+ var requestOrigin = this.request.headers.origin;
80
+ var res = this.options.origin(requestOrigin, this.request);
81
+ if (res === true && requestOrigin){
82
+ this.setHeader('access-control-allow-origin', requestOrigin);
83
+ }
84
+ else if (typeof res === 'string' && res){
85
+ this.setHeader('access-control-allow-origin', res);
86
+ }
87
+ } catch(_) {}
62
88
  }
63
89
 
64
90
  }
@@ -143,4 +169,4 @@ class MasterCors{
143
169
  }
144
170
  }
145
171
 
146
- master.extend("cors", MasterCors);
172
+ master.extend("cors", MasterCors);
package/MasterError.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- // version 1.0.19
2
+ // version 1.0.20 - improved console/error logging with syntax error code frames
3
3
  var master = require('./MasterControl');
4
4
  var winston = require('winston');
5
5
  var fileserver = require('fs');
@@ -22,19 +22,23 @@ class MasterError{
22
22
  ]
23
23
  });
24
24
 
25
- process.on('uncaughtException', function (reason, promise) {
26
- that.log(reason, "error");
27
- //master.request.clear(500, reason.message);
28
- });
29
-
30
- // will catch all promise exceptions
31
- process.on('unhandledRejection', function (reason, promise) {
32
- that.log(reason, "warn");
33
- });
25
+ // Global error handlers with better diagnostics (stack and code frame)
26
+ process.on('uncaughtException', function (err) {
27
+ that._reportError(err, 'uncaughtException');
28
+ });
34
29
 
35
- process.on('rejectionHandled', function (reason, promise) {
36
- that.log(reason, "warn");
37
- });
30
+ // will catch all promise exceptions
31
+ process.on('unhandledRejection', function (reason, promise) {
32
+ that._reportError(reason instanceof Error ? reason : new Error(String(reason)), 'unhandledRejection');
33
+ });
34
+
35
+ process.on('rejectionHandled', function (reason, promise) {
36
+ that._reportError(reason instanceof Error ? reason : new Error(String(reason)), 'rejectionHandled');
37
+ });
38
+
39
+ process.on('warning', function (warning) {
40
+ that._reportError(warning instanceof Error ? warning : new Error(String(warning)), 'warning');
41
+ });
38
42
 
39
43
  }
40
44
 
@@ -111,6 +115,63 @@ class MasterError{
111
115
  stack : msg.stack
112
116
  });
113
117
  }
118
+
119
+ // Enhanced error reporter: logs formatted stack and nearby source code
120
+ _reportError(err, tag){
121
+ try{
122
+ const name = err && err.name ? err.name : 'Error';
123
+ const message = err && err.message ? err.message : String(err);
124
+ const stack = err && err.stack ? String(err.stack) : '';
125
+ const header = `[${tag}] ${name}: ${message}`;
126
+ console.error('\u001b[31m' + header + '\u001b[0m');
127
+ if (stack) {
128
+ console.error(stack);
129
+ const loc = this._extractTopFrame(stack);
130
+ if (loc && loc.file && loc.line) {
131
+ const frame = this._buildCodeFrame(loc.file, loc.line, loc.column);
132
+ if (frame) {
133
+ console.error('\n\u001b[33mCode frame:\u001b[0m');
134
+ console.error(frame);
135
+ }
136
+ }
137
+ }
138
+ this.log(err, 'error');
139
+ } catch(e){
140
+ try { console.error('Error while reporting error:', e); } catch(_) {}
141
+ }
142
+ }
143
+
144
+ _extractTopFrame(stack){
145
+ try{
146
+ const lines = stack.split('\n');
147
+ for (let i = 0; i < lines.length; i++) {
148
+ const m = lines[i].match(/\((.*):(\d+):(\d+)\)/) || lines[i].match(/at ([^\s]+):(\d+):(\d+)/);
149
+ if (m) {
150
+ return { file: m[1], line: parseInt(m[2]), column: parseInt(m[3]||'0') };
151
+ }
152
+ }
153
+ return null;
154
+ } catch(_) { return null; }
155
+ }
156
+
157
+ _buildCodeFrame(file, line, column){
158
+ try{
159
+ if (!fileserver.existsSync(file)) return null;
160
+ const src = fileserver.readFileSync(file, 'utf8').split(/\r?\n/);
161
+ const start = Math.max(1, line - 3);
162
+ const end = Math.min(src.length, line + 3);
163
+ const digits = String(end).length;
164
+ let out = '';
165
+ for (let i = start; i <= end; i++) {
166
+ const prefix = (i === line ? '>' : ' ') + String(i).padStart(digits, ' ') + ' | ';
167
+ out += prefix + src[i - 1] + '\n';
168
+ if (i === line && column && column > 0) {
169
+ out += ' '.repeat(prefix.length + column - 1) + '^\n';
170
+ }
171
+ }
172
+ return out;
173
+ } catch(_) { return null; }
174
+ }
114
175
  }
115
176
 
116
177
  master.extend("error", MasterError);
package/MasterRequest.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- // version 0.0.1
2
+ // version 0.0.2
3
3
 
4
4
  var master = require('./MasterControl');
5
5
  var url = require('url');
@@ -91,9 +91,17 @@ class MasterRequest{
91
91
  resolve($that.parsedURL);
92
92
  });
93
93
 
94
+ break
95
+ case "text/plain" :
96
+ $that.fetchData(request, function(data){
97
+ $that.parsedURL.formData = data;
98
+ resolve($that.parsedURL);
99
+
100
+ });
101
+
94
102
  break
95
103
  default:
96
- var errorMessage = `Cannot parse - We currently support text/html, application/json, multipart/form-data, and application/x-www-form-urlencoded - your sending us = ${contentType.type}`;
104
+ var errorMessage = `Cannot parse - We currently support text/plain, text/html, application/json, multipart/form-data, and application/x-www-form-urlencoded - your sending us = ${contentType.type}`;
97
105
  resolve(errorMessage);
98
106
  console.log(errorMessage);
99
107
  }
@@ -110,6 +118,47 @@ class MasterRequest{
110
118
  }
111
119
  };
112
120
 
121
+
122
+ fetchData(request, func) {
123
+
124
+ let body = '';
125
+ let receivedBytes = 0;
126
+ const maxBytes = 1 * 1024 * 1024; // 1MB limit
127
+
128
+
129
+ try {
130
+
131
+ request.on('data', (chunk) => {
132
+ receivedBytes += chunk.length;
133
+
134
+ // Prevent memory overload
135
+ if (receivedBytes > maxBytes) {
136
+ req.destroy(); // Close the connection
137
+ return;
138
+ }
139
+
140
+ // Append chunk to body
141
+ body += chunk.toString('utf8');
142
+ });
143
+
144
+ request.on('end', () => {
145
+ try {
146
+ // Process the plain text data here
147
+ const responseData = body;
148
+ func(responseData );
149
+ } catch (err) {
150
+
151
+ console.error('Processing error handling text/plain:', err);
152
+ throw err;
153
+ }
154
+
155
+ });
156
+ } catch (error) {
157
+ console.error("Failed to fetch data:", error);
158
+ throw error;
159
+ }
160
+ }
161
+
113
162
  deleteFileBuffer(filePath){
114
163
  fs.unlink(filePath, function (err) {
115
164
  if (err) {
@@ -134,6 +183,10 @@ class MasterRequest{
134
183
  });
135
184
 
136
185
  }
186
+
187
+ stringToJson(request, func){
188
+
189
+ }
137
190
 
138
191
  jsonStream(request, func){
139
192
  //request.pipe(decoder);
@@ -143,8 +196,13 @@ class MasterRequest{
143
196
  });
144
197
 
145
198
  request.on('end', () => {
146
- var buff = qs.parse(buffer);
147
- func(buff);
199
+ try {
200
+ var buff = JSON.parse(buffer);
201
+ func(buff);
202
+ } catch (e) {
203
+ var buff = qs.parse(buffer);
204
+ func(buff);
205
+ }
148
206
  });
149
207
 
150
208
  }
package/MasterRouter.js CHANGED
@@ -1,4 +1,4 @@
1
- // version 0.0.24
1
+ // version 0.0.247
2
2
 
3
3
  var master = require('./MasterControl');
4
4
  var toolClass = require('./MasterTools');
@@ -35,53 +35,65 @@ var tools = new toolClass();
35
35
  var routeList = routeObject.routes;
36
36
  var root = routeObject.root;
37
37
  var isComponent = routeObject.isComponent;
38
+ try{
39
+ if(routeList.length > 0){
40
+ // loop through routes
41
+ for(var item in routeList){
38
42
 
39
- if(routeList.length > 0){
40
- // loop through routes
41
- for(var item in routeList){
43
+ requestObject.toController = routeList[item].toController;
44
+ requestObject.toAction = routeList[item].toAction;
45
+ var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, requestObject.params);
46
+ // if we find the route that matches the request
47
+ if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
42
48
 
43
- requestObject.toController = routeList[item].toController;
44
- requestObject.toAction = routeList[item].toAction;
45
- var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, requestObject.params)
46
- // if we find the route that matches the request
47
- if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
49
+ // call Constraint
50
+ if(typeof routeList[item].constraint === "function"){
51
+
52
+ var newObj = {};
53
+ //tools.combineObjects(newObj, master.controllerList);
54
+ newObj.next = function(){
55
+ currentRoute.root = root;
56
+ currentRoute.pathName = requestObject.pathName;
57
+ currentRoute.toAction = requestObject.toAction;
58
+ currentRoute.toController = requestObject.toController;
59
+ currentRoute.response = requestObject.response;
60
+ currentRoute.isComponent = isComponent;
61
+ emitter.emit("routeConstraintGood", requestObject);
62
+ };
63
+ routeList[item].constraint.call(newObj, requestObject);
64
+ return true;
65
+ }else{
48
66
 
49
- // call Constraint
50
- if(typeof routeList[item].constraint === "function"){
51
-
52
- var newObj = {};
53
- //tools.combineObjects(newObj, master.controllerList);
54
- newObj.next = function(){
55
- currentRoute.root = root;
56
- currentRoute.pathName = requestObject.pathName;
57
- currentRoute.toAction = requestObject.toAction;
58
- currentRoute.toController = requestObject.toController;
59
- currentRoute.response = requestObject.response;
60
- currentRoute.isComponent = isComponent;
61
- emitter.emit("routeConstraintGood", requestObject);
62
- };
63
- routeList[item].constraint.call(newObj, requestObject);
64
- return true;
65
- }else{
67
+ currentRoute.root = root;
68
+ currentRoute.pathName = requestObject.pathName;
69
+ currentRoute.toAction = requestObject.toAction;
70
+ currentRoute.toController = requestObject.toController;
71
+ currentRoute.response = requestObject.response;
72
+ currentRoute.isComponent = isComponent;
73
+ emitter.emit("routeConstraintGood", requestObject);
74
+ return true;
75
+ }
76
+
77
+ }
66
78
 
67
- currentRoute.root = root;
68
- currentRoute.pathName = requestObject.pathName;
69
- currentRoute.toAction = requestObject.toAction;
70
- currentRoute.toController = requestObject.toController;
71
- currentRoute.response = requestObject.response;
72
- currentRoute.isComponent = isComponent;
73
- emitter.emit("routeConstraintGood", requestObject);
74
- return true;
75
- }
76
-
79
+ if(pathObj.requestPath === pathObj.routePath && "options" ===requestObject.type.toLowerCase()){
80
+ // this means that the request is correct but its an options request means its the browser checking to see if the request is allowed
81
+ requestObject.response.writeHead(200, {'Content-Type': 'application/json'});
82
+ requestObject.response.end(JSON.stringify({"done": "true"}));
83
+ return true;
84
+ }
85
+
86
+ };
87
+ return -1;
77
88
  }
78
- };
79
- return -1;
80
- }
81
- else{
82
- master.error.log(`route list is not an array`, "Error");
83
- return -1;
84
- }
89
+ else{
90
+ master.error.log(`route list is not an array`, "Error");
91
+ return -1;
92
+ }
93
+ }
94
+ catch(e){
95
+ throw new Error("Error processing routes: " + e.stack);
96
+ }
85
97
  };
86
98
 
87
99
  var loadScopedListClasses = function(){
package/MasterSession.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- // version 0.0.19
2
+ // version 0.0.22
3
3
 
4
4
  var master = require('./MasterControl');
5
5
  var cookie = require('cookie');
@@ -22,10 +22,17 @@ class MasterSession{
22
22
  secret : this.createSessionID()
23
23
  };
24
24
 
25
- init(TID){
25
+ init(options){
26
26
  var $that = this;
27
- if(TID){
28
- $that.options.secret = TID;
27
+
28
+ // Combine the rest of the options carefully
29
+ this.options = {
30
+ ...this.options,
31
+ ...options
32
+ };
33
+
34
+ if(this.options.TID){
35
+ this.options.secret = TID;
29
36
  }
30
37
 
31
38
  return {
@@ -84,10 +91,17 @@ class MasterSession{
84
91
  return this.secret;
85
92
  }
86
93
 
87
- setCookie(name, payload, response, secret, options){
94
+ setCookie(name, payload, response, options){
88
95
  var cookieOpt = options === undefined? this.options : options;
89
- if(secret){
90
- response.setHeader('Set-Cookie', cookie.serialize(name, tools.encrypt(payload, secret), cookieOpt));
96
+ if(typeof options === "object"){
97
+ if(options.secret){
98
+ response.setHeader('Set-Cookie', cookie.serialize(name, tools.encrypt(payload, cookieOpt.secret), cookieOpt));
99
+ }else{
100
+
101
+ response.setHeader('Set-Cookie', cookie.serialize(name, payload, cookieOpt));
102
+
103
+ }
104
+
91
105
  }
92
106
  else{
93
107
  response.setHeader('Set-Cookie', cookie.serialize(name, payload, cookieOpt));
@@ -119,10 +133,10 @@ class MasterSession{
119
133
  }
120
134
  }
121
135
 
122
- deleteCookie (name, response){
123
- this.options.expires = new Date(0);
124
- response.setHeader('Set-Cookie', cookie.serialize(name, "", this.options));
125
- this.options.expires = undefined;
136
+ deleteCookie (name, response, options){
137
+ var cookieOpt = options === undefined? this.options : options;
138
+ response.setHeader('Set-Cookie', cookie.serialize(name, "", cookieOpt));
139
+ cookieOpt.expires = undefined;
126
140
  }
127
141
 
128
142
  // delete session and cookie
package/MasterSocket.js CHANGED
@@ -1,5 +1,9 @@
1
- // version 0.0.3
1
+ // version 0.1.1
2
+
2
3
  var master = require('./MasterControl');
4
+ const { Server } = require('socket.io');
5
+ const fs = require('fs');
6
+ const path = require('path');
3
7
 
4
8
  var jsUcfirst = function(string){
5
9
  return string.charAt(0).toUpperCase() + string.slice(1);
@@ -7,8 +11,82 @@ var jsUcfirst = function(string){
7
11
 
8
12
  class MasterSocket{
9
13
 
10
- init(){
14
+ init(serverOrIo, options = {}){
11
15
  this._baseurl = master.root;
16
+
17
+ // Build Socket.IO options using master cors initializer when available
18
+ const defaults = this._buildDefaultIoOptions();
19
+ const ioOptions = mergeDeep(defaults, options || {});
20
+
21
+ // Determine whether we're given an io instance or an HTTP server
22
+ if (serverOrIo && typeof serverOrIo.of === 'function') {
23
+ // It's already an io instance
24
+ this.io = serverOrIo;
25
+ } else {
26
+ // Prefer explicit server, fallback to master.server
27
+ const httpServer = serverOrIo || master.server;
28
+ if (!httpServer) {
29
+ throw new Error('MasterSocket.init requires an HTTP server or a pre-created Socket.IO instance');
30
+ }
31
+ this.io = new Server(httpServer, ioOptions);
32
+ }
33
+
34
+ this._bind();
35
+ }
36
+
37
+ _buildDefaultIoOptions(){
38
+ const corsCfg = this._loadCorsConfig();
39
+ const transports = ['websocket', 'polling'];
40
+ const cors = {};
41
+ try {
42
+ if (corsCfg) {
43
+ if (typeof corsCfg.origin !== 'undefined') cors.origin = corsCfg.origin;
44
+ if (typeof corsCfg.credentials !== 'undefined') cors.credentials = !!corsCfg.credentials;
45
+ if (Array.isArray(corsCfg.methods)) cors.methods = corsCfg.methods;
46
+ if (Array.isArray(corsCfg.allowedHeaders)) cors.allowedHeaders = corsCfg.allowedHeaders;
47
+ } else {
48
+ // sensible defaults for dev
49
+ cors.origin = true;
50
+ cors.credentials = true;
51
+ cors.methods = ['GET','POST'];
52
+ }
53
+ } catch (_) {}
54
+ return { cors, transports };
55
+ }
56
+
57
+ _loadCorsConfig(){
58
+ try {
59
+ const cfgPath = path.join(master.root, 'config', 'initializers', 'cors.json');
60
+ if (fs.existsSync(cfgPath)) {
61
+ const raw = fs.readFileSync(cfgPath, 'utf8');
62
+ return JSON.parse(raw);
63
+ }
64
+ } catch (e) {
65
+ try { console.warn('[MasterSocket] Failed to load cors.json:', e && e.message ? e.message : e); } catch(_){}
66
+ }
67
+ return null;
68
+ }
69
+
70
+ _bind(){
71
+ const io = this.io;
72
+ io.on('connection', (socket) => {
73
+ try{
74
+ // Route all events through MasterSocket loader
75
+ socket.onAny((eventName, payload) => {
76
+ try{
77
+ // MasterSocket.load expects [action, payload]
78
+ const data = [eventName, payload];
79
+ if (master && master.socket && typeof master.socket.load === 'function') {
80
+ master.socket.load(data, socket, io);
81
+ }
82
+ }catch(e){
83
+ try { console.error('Socket routing error:', e?.message || e); } catch(_){}
84
+ }
85
+ });
86
+ }catch(e){
87
+ try { console.error('Socket connection handler error:', e?.message || e); } catch(_){}
88
+ }
89
+ });
12
90
  }
13
91
 
14
92
  async load(data, socket, io){
@@ -45,4 +123,44 @@ class MasterSocket{
45
123
  }
46
124
  }
47
125
 
48
- master.extend("socket", MasterSocket);
126
+ master.extend("socket", MasterSocket);
127
+
128
+ // shallow+deep merge helper
129
+ function isObject(item) {
130
+ return (item && typeof item === 'object' && !Array.isArray(item));
131
+ }
132
+ function mergeDeep(target, source) {
133
+ const output = Object.assign({}, target);
134
+ if (isObject(target) && isObject(source)) {
135
+ Object.keys(source).forEach(key => {
136
+ if (isObject(source[key])) {
137
+ if (!(key in target)) Object.assign(output, { [key]: source[key] });
138
+ else output[key] = mergeDeep(target[key], source[key]);
139
+ } else {
140
+ Object.assign(output, { [key]: source[key] });
141
+ }
142
+ });
143
+ }
144
+ return output;
145
+ }
146
+
147
+ /**
148
+ *
149
+ *
150
+ *
151
+ * It loads CORS and methods from config/initializers/cors.json automatically. During init, it reads master.root/config/initializers/cors.json and builds the Socket.IO options from:
152
+ origin, credentials, methods, allowedHeaders (if present)
153
+ transports defaults to ['websocket', 'polling']
154
+ If cors.json is missing or a field isn’t present, it falls back to:
155
+ cors: { origin: true, credentials: true, methods: ['GET','POST'] }
156
+ transports: ['websocket','polling']
157
+ You can still override anything explicitly:
158
+ master.socket.init(master.server, { cors: { origin: ['https://foo.com'], methods: ['GET','POST','PUT'] }, transports: ['websocket'] })
159
+
160
+ If you don’t pass a server/io, init() falls back to master.server:
161
+ master.socket.init() → uses master.server automatically
162
+ You can pass overrides as the second arg:
163
+ master.socket.init(undefined, { cors: { origin: ['https://app.com'] }, transports: ['websocket'] })
164
+ Or pass a prebuilt io:
165
+ const io = new Server(master.server, opts); master.socket.init(io)
166
+ */
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "dependencies": {
3
- "qs" : "^6.11.2",
4
- "formidable": "^3.5.1",
5
- "cookie": "^0.5.0",
6
- "winston": "^3.10.0",
7
- "glob" :"^10.3.4"
3
+ "qs" : "^6.14.0",
4
+ "formidable": "^3.5.4",
5
+ "cookie": "^1.0.2",
6
+ "winston": "^3.17.0",
7
+ "glob" :"^11.0.3"
8
8
  },
9
9
  "description": "A class library that makes using the Master Framework a breeze",
10
10
  "homepage": "https://github.com/Tailor/MasterController#readme",
@@ -18,5 +18,5 @@
18
18
  "scripts": {
19
19
  "test": "echo \"Error: no test specified\" && exit 1"
20
20
  },
21
- "version": "1.2.1"
21
+ "version": "1.2.3"
22
22
  }
package/MasterJWT.js DELETED
@@ -1,112 +0,0 @@
1
-
2
- // version 0.0.17
3
-
4
- var master = require('./MasterControl');
5
- var crypto = require('crypto');
6
- var toolClass = require('./MasterTools');
7
- var tools = new toolClass();
8
-
9
- //https://www.youtube.com/watch?v=67mezK3NzpU&t=2492s
10
- class MasterJWT{
11
-
12
- init(TID){
13
- this.alg = "sha256";
14
- var $that = this;
15
- this.secret = this.createJWTID();
16
- if(TID){
17
- this.secret = TID;
18
- }
19
- return {
20
- sha256 : function(){
21
- $that.alg = "sha256"
22
- }
23
- };
24
- }
25
-
26
- createJWTID(){
27
- return crypto.randomBytes(20).toString('hex');
28
- }
29
-
30
- getJWTID(){
31
- return this.secret;
32
- }
33
-
34
- create(payload, encrypted, encryptionKey){
35
- var hmac = null;
36
- var now = new Date();
37
- var twoHoursLater = new Date(now.getTime() + (2*1000*60*60))
38
- var encrypted = typeof encrypted === "undefined" ? false : true;
39
- var encodePayload = null;
40
-
41
- if(typeof payload === "string"){
42
- throw "payload must be object not string";
43
- }
44
-
45
- var header = {
46
- typ: 'JWT',
47
- alg: this.alg
48
- };
49
-
50
- var body = {
51
- jti: tools.generateRandomKey('sha256'),
52
- iat: Math.floor(new Date() / 1000),
53
- exp: twoHoursLater
54
- };
55
-
56
- body = tools.combineObjects(body, payload);
57
-
58
- if(encrypted === true){
59
- header["encrypt"] = 'aes-256-ctr';
60
- if(encryptionKey === undefined){
61
- encodePayload = tools.base64().encode(tools.encrypt(JSON.stringify(body), this.secret));
62
- }else{
63
- encodePayload = tools.base64().encode(tools.encrypt(JSON.stringify(body), encryptionKey));
64
- }
65
- }
66
- else{
67
- encodePayload = tools.base64().encode(JSON.stringify(body));
68
- }
69
-
70
-
71
- if(encryptionKey === undefined){
72
- hmac = crypto.createHmac(this.alg, this.secret);
73
- }else{
74
- hmac = crypto.createHmac(this.alg, encryptionKey);
75
- }
76
-
77
- var encodeHeader = tools.base64().encode(JSON.stringify(header));
78
- hmac.update(encodeHeader + "." + encodePayload);
79
- var sig = hmac.digest('base64');
80
-
81
- return encodeHeader + "." + encodePayload + "." + sig;
82
- }
83
-
84
- verify(signature, encrypted, secret){
85
- var secret = secret === undefined ? this.secret : secret;
86
- var encrypted = typeof encrypted === "undefined" ? false : true;
87
- var jwt = signature.split(".");
88
- var decodeHeader = JSON.parse(tools.base64().decode(jwt[0]));
89
-
90
- var hmac = crypto.createHmac(decodeHeader.alg, secret );
91
- hmac.update(jwt[0] + "." + jwt[1]);
92
- var ourSignature = hmac.digest('base64');
93
-
94
- if(ourSignature === jwt[2]){
95
- // if they are the same return json payload or un ecrypt payload
96
- var decodePayload = tools.base64().decode(jwt[1]);
97
- if(encrypted === true){
98
- var decryptPayload = JSON.parse(tools.decrypt(decodePayload, secret ));
99
- return decryptPayload;
100
- }
101
- else{
102
- return decodePayload;
103
- }
104
- }
105
- else{
106
- return -1;
107
- }
108
- }
109
- }
110
-
111
-
112
- master.extend("jwt", MasterJWT);