backend-manager 4.1.3 → 4.2.1
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.
|
@@ -1,1051 +0,0 @@
|
|
|
1
|
-
const os = require('os');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const uuid = require('uuid');
|
|
5
|
-
let JSON5;
|
|
6
|
-
|
|
7
|
-
const LOG_LEVELS = {
|
|
8
|
-
error: 'error',
|
|
9
|
-
warn: 'warn',
|
|
10
|
-
info: 'info',
|
|
11
|
-
debug: 'debug',
|
|
12
|
-
log: 'log',
|
|
13
|
-
notice: 'NOTICE',
|
|
14
|
-
critical: 'CRITICAL',
|
|
15
|
-
emergency: 'EMERGENCY'
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function BackendAssistant() {
|
|
19
|
-
const self = this;
|
|
20
|
-
|
|
21
|
-
// Set ref
|
|
22
|
-
self.meta = {};
|
|
23
|
-
self.initialized = false;
|
|
24
|
-
|
|
25
|
-
// Add log methods
|
|
26
|
-
Object.keys(LOG_LEVELS)
|
|
27
|
-
.forEach((level) => {
|
|
28
|
-
// Skip log because it is already a method
|
|
29
|
-
// if (level === 'log') {
|
|
30
|
-
// return;
|
|
31
|
-
// }
|
|
32
|
-
|
|
33
|
-
// Add log method
|
|
34
|
-
BackendAssistant.prototype[level] = function() {
|
|
35
|
-
const self = this;
|
|
36
|
-
const args = Array.prototype.slice.call(arguments);
|
|
37
|
-
|
|
38
|
-
// Prepend level to args
|
|
39
|
-
args.unshift(level);
|
|
40
|
-
// self.log.apply(this, args);
|
|
41
|
-
self._log.apply(this, args);
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
return self;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function tryParse(input) {
|
|
49
|
-
var ret;
|
|
50
|
-
|
|
51
|
-
JSON5 = JSON5 || require('json5');
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
ret = JSON5.parse(input);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
ret = input
|
|
57
|
-
}
|
|
58
|
-
return ret;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
BackendAssistant.prototype.init = function (ref, options) {
|
|
62
|
-
const self = this;
|
|
63
|
-
|
|
64
|
-
// Set options
|
|
65
|
-
options = options || {};
|
|
66
|
-
options.accept = options.accept || 'json';
|
|
67
|
-
options.showOptionsLog = typeof options.showOptionsLog !== 'undefined' ? options.showOptionsLog : false;
|
|
68
|
-
options.optionsLogString = typeof options.optionsLogString !== 'undefined' ? options.optionsLogString : '\n\n\n\n\n';
|
|
69
|
-
options.fileSavePath = options.fileSavePath || process.env.npm_package_name || '';
|
|
70
|
-
|
|
71
|
-
// Set now
|
|
72
|
-
const now = new Date();
|
|
73
|
-
|
|
74
|
-
// Attached libraries - used in .errorify()
|
|
75
|
-
self.analytics = null;
|
|
76
|
-
self.usage = null;
|
|
77
|
-
self.settings = null;
|
|
78
|
-
|
|
79
|
-
// Set meta
|
|
80
|
-
self.meta = {};
|
|
81
|
-
|
|
82
|
-
self.meta.startTime = {};
|
|
83
|
-
self.meta.startTime.timestamp = now.toISOString();
|
|
84
|
-
self.meta.startTime.timestampUNIX = Math.round((now.getTime()) / 1000);
|
|
85
|
-
|
|
86
|
-
self.meta.name = options.functionName || process.env.FUNCTION_TARGET || 'unnamed';
|
|
87
|
-
self.meta.environment = options.environment || self.getEnvironment();
|
|
88
|
-
self.meta.type = options.functionType || process.env.FUNCTION_SIGNATURE_TYPE || 'unknown';
|
|
89
|
-
|
|
90
|
-
// Set ref
|
|
91
|
-
self.ref = {};
|
|
92
|
-
ref = ref || {};
|
|
93
|
-
self.ref.res = ref.res || {};
|
|
94
|
-
self.ref.req = ref.req || {};
|
|
95
|
-
self.ref.admin = ref.admin || {};
|
|
96
|
-
self.ref.functions = ref.functions || {};
|
|
97
|
-
self.ref.Manager = ref.Manager || {};
|
|
98
|
-
self.Manager = self.ref.Manager;
|
|
99
|
-
|
|
100
|
-
// Set ID
|
|
101
|
-
try {
|
|
102
|
-
const headers = self?.ref?.req.headers || {};
|
|
103
|
-
|
|
104
|
-
self.id = headers['function-execution-id']
|
|
105
|
-
|| headers['X-Cloud-Trace-Context']
|
|
106
|
-
|| self.Manager.Utilities().randomId();
|
|
107
|
-
} catch {
|
|
108
|
-
self.id = now.getTime();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Set tag
|
|
112
|
-
self.tag = `${self.meta.name}/${self.id}`;
|
|
113
|
-
|
|
114
|
-
// Set logger prefix
|
|
115
|
-
self.logPrefix = '';
|
|
116
|
-
|
|
117
|
-
// Set stuff about request
|
|
118
|
-
self.request = {};
|
|
119
|
-
self.request.referrer = (self.ref.req.headers || {}).referrer || (self.ref.req.headers || {}).referer || '';
|
|
120
|
-
self.request.method = (self.ref.req.method || undefined);
|
|
121
|
-
|
|
122
|
-
// Set geolocation data
|
|
123
|
-
self.request.geolocation = {
|
|
124
|
-
ip: self.getHeaderIp(self.ref.req.headers),
|
|
125
|
-
continent: self.getHeaderContinent(self.ref.req.headers),
|
|
126
|
-
country: self.getHeaderCountry(self.ref.req.headers),
|
|
127
|
-
region: self.getHeaderRegion(self.ref.req.headers),
|
|
128
|
-
city: self.getHeaderCity(self.ref.req.headers),
|
|
129
|
-
latitude: self.getHeaderLatitude(self.ref.req.headers),
|
|
130
|
-
longitude: self.getHeaderLongitude(self.ref.req.headers),
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Set client data
|
|
134
|
-
self.request.client = {
|
|
135
|
-
userAgent: self.getHeaderUserAgent(self.ref.req.headers),
|
|
136
|
-
language: self.getHeaderLanguage(self.ref.req.headers),
|
|
137
|
-
platform: self.getHeaderPlatform(self.ref.req.headers),
|
|
138
|
-
mobile: self.getHeaderMobile(self.ref.req.headers),
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// Deprecated notice for old properties
|
|
142
|
-
Object.defineProperty(self.request, 'ip', {
|
|
143
|
-
get: function() {
|
|
144
|
-
console.error('⛔️ [Deprecation]: request.ip is deprecated, use request.geolocation.ip instead');
|
|
145
|
-
return self.request.geolocation.ip;
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
Object.defineProperty(self.request, 'country', {
|
|
149
|
-
get: function() {
|
|
150
|
-
console.error('⛔️ [Deprecation]: request.country is deprecated, use request.geolocation.country instead');
|
|
151
|
-
return self.request.geolocation.country;
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
Object.defineProperty(self.request, 'userAgent', {
|
|
155
|
-
get: function() {
|
|
156
|
-
console.error('⛔️ [Deprecation]: request.userAgent is deprecated, use request.client.userAgent instead');
|
|
157
|
-
return self.request.client.userAgent;
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
/*
|
|
162
|
-
MORE HEADERS TO GET
|
|
163
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Platform-Version
|
|
164
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Model
|
|
165
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Mobile
|
|
166
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Full-Version-List
|
|
167
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Full-Version
|
|
168
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA-Arch
|
|
169
|
-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-UA
|
|
170
|
-
*/
|
|
171
|
-
|
|
172
|
-
// Set request type
|
|
173
|
-
if (
|
|
174
|
-
self.ref.req.xhr || (self.ref.req?.headers?.accept || '').includes('json')
|
|
175
|
-
|| (self.ref.req?.headers?.['content-type'] || '').includes('json')
|
|
176
|
-
) {
|
|
177
|
-
self.request.type = 'ajax';
|
|
178
|
-
} else {
|
|
179
|
-
self.request.type = 'form';
|
|
180
|
-
}
|
|
181
|
-
self.request.path = self.ref.req.path || '';
|
|
182
|
-
self.request.user = self.resolveAccount({authenticated: false});
|
|
183
|
-
|
|
184
|
-
// Set body and query
|
|
185
|
-
if (options.accept === 'json') {
|
|
186
|
-
self.request.body = tryParse(self.ref.req.body || '{}');
|
|
187
|
-
self.request.query = tryParse(self.ref.req.query || '{}');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Set headers
|
|
191
|
-
self.request.headers = self.ref.req.headers || {};
|
|
192
|
-
|
|
193
|
-
// Merge data
|
|
194
|
-
self.request.data = _.merge({}, self.request.body, self.request.query);
|
|
195
|
-
|
|
196
|
-
// Set multipart data
|
|
197
|
-
self.request.multipartData = {
|
|
198
|
-
fields: {},
|
|
199
|
-
files: {},
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// Log the request
|
|
203
|
-
// if (Object.keys(self.request.data).length > 0) {
|
|
204
|
-
// self.log('Request:', self.request.data, {
|
|
205
|
-
// ip: self.request.ip,
|
|
206
|
-
// });
|
|
207
|
-
// }
|
|
208
|
-
|
|
209
|
-
// Constants
|
|
210
|
-
self.constant = {};
|
|
211
|
-
self.constant.pastTime = {};
|
|
212
|
-
self.constant.pastTime.timestamp = '1999-01-01T00:00:00Z';
|
|
213
|
-
self.constant.pastTime.timestampUNIX = 915148800;
|
|
214
|
-
|
|
215
|
-
// Schema
|
|
216
|
-
// self.schema = {
|
|
217
|
-
// dir: '',
|
|
218
|
-
// name: '',
|
|
219
|
-
// }
|
|
220
|
-
|
|
221
|
-
// Log options
|
|
222
|
-
if (
|
|
223
|
-
(self.isDevelopment())
|
|
224
|
-
&& ((self.request.method !== 'OPTIONS') || (self.request.method === 'OPTIONS' && options.showOptionsLog))
|
|
225
|
-
&& (self.request.method !== 'undefined')
|
|
226
|
-
// && (self.request.method !== 'undefined' && typeof self.request.method !== 'undefined')
|
|
227
|
-
) {
|
|
228
|
-
console.log(options.optionsLogString);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Set tmpdir
|
|
232
|
-
self.tmpdir = path.resolve(os.tmpdir(), options.fileSavePath, uuid.v4());
|
|
233
|
-
|
|
234
|
-
// Set initialized
|
|
235
|
-
self.initialized = true;
|
|
236
|
-
|
|
237
|
-
return self;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
BackendAssistant.prototype.getEnvironment = function () {
|
|
241
|
-
// return (process.env.FUNCTIONS_EMULATOR === true || process.env.FUNCTIONS_EMULATOR === 'true' || process.env.ENVIRONMENT !== 'production' ? 'development' : 'production')
|
|
242
|
-
if (process.env.ENVIRONMENT === 'production') {
|
|
243
|
-
return 'production';
|
|
244
|
-
} else if (
|
|
245
|
-
process.env.ENVIRONMENT === 'development'
|
|
246
|
-
|| process.env.FUNCTIONS_EMULATOR === true
|
|
247
|
-
|| process.env.FUNCTIONS_EMULATOR === 'true'
|
|
248
|
-
|| process.env.TERM_PROGRAM === 'Apple_Terminal'
|
|
249
|
-
|| process.env.TERM_PROGRAM === 'vscode'
|
|
250
|
-
) {
|
|
251
|
-
return 'development';
|
|
252
|
-
} else {
|
|
253
|
-
return 'production'
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
BackendAssistant.prototype.isDevelopment = function () {
|
|
258
|
-
const self = this;
|
|
259
|
-
|
|
260
|
-
return self.meta.environment === 'development';
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
BackendAssistant.prototype.isProduction = function () {
|
|
264
|
-
const self = this;
|
|
265
|
-
|
|
266
|
-
return self.meta.environment === 'production';
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
BackendAssistant.prototype.logProd = function () {
|
|
270
|
-
const self = this;
|
|
271
|
-
|
|
272
|
-
self._log.apply(self, args);
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
BackendAssistant.prototype._log = function () {
|
|
276
|
-
const self = this;
|
|
277
|
-
const logs = [...arguments];
|
|
278
|
-
const prefix = self.logPrefix ? ` ${self.logPrefix}:` : ':';
|
|
279
|
-
|
|
280
|
-
// Prepend log prefix log string
|
|
281
|
-
logs.unshift(`[${self.tag} @ ${new Date().toISOString()}]${prefix}`);
|
|
282
|
-
|
|
283
|
-
// Get the log level
|
|
284
|
-
const level = logs[1];
|
|
285
|
-
|
|
286
|
-
// Pass along arguments to console.log
|
|
287
|
-
if (LOG_LEVELS[level]) {
|
|
288
|
-
logs.splice(1, 1);
|
|
289
|
-
|
|
290
|
-
// Determine how to log
|
|
291
|
-
if (level in console) {
|
|
292
|
-
console[level].apply(console, logs);
|
|
293
|
-
} else if (self.isDevelopment()) {
|
|
294
|
-
console.log.apply(console, logs);
|
|
295
|
-
} else {
|
|
296
|
-
self.ref.functions.logger.write({
|
|
297
|
-
severity: LOG_LEVELS[level].toUpperCase(),
|
|
298
|
-
message: logs,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Write with wonderful-log
|
|
303
|
-
if (self.Manager?.libraries?.logger?.[level]) {
|
|
304
|
-
self.Manager?.libraries?.logger?.[level](...logs)
|
|
305
|
-
}
|
|
306
|
-
} else {
|
|
307
|
-
console.log.apply(console, logs);
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
BackendAssistant.prototype.setLogPrefix = function (s) {
|
|
312
|
-
const self = this;
|
|
313
|
-
|
|
314
|
-
// Set logger prefix
|
|
315
|
-
self.logPrefix = s
|
|
316
|
-
|
|
317
|
-
return self;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
BackendAssistant.prototype.clearLogPrefix = function () {
|
|
321
|
-
const self = this;
|
|
322
|
-
|
|
323
|
-
// Set logger prefix
|
|
324
|
-
self.logPrefix = '';
|
|
325
|
-
|
|
326
|
-
return self;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
BackendAssistant.prototype.getLogPrefix = function () {
|
|
330
|
-
const self = this;
|
|
331
|
-
|
|
332
|
-
return self.logPrefix;
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
BackendAssistant.prototype.getUser = function () {
|
|
336
|
-
const self = this;
|
|
337
|
-
|
|
338
|
-
return self?.usage?.user || self.request.user;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
BackendAssistant.prototype.errorify = function (e, options) {
|
|
342
|
-
const self = this;
|
|
343
|
-
const res = self.ref.res;
|
|
344
|
-
|
|
345
|
-
// Set options
|
|
346
|
-
options = options || {};
|
|
347
|
-
|
|
348
|
-
// Code: default to 500, or else use the user's option
|
|
349
|
-
const isCodeSet = typeof options.code !== 'undefined';
|
|
350
|
-
options.code = !isCodeSet
|
|
351
|
-
? 500
|
|
352
|
-
: options.code;
|
|
353
|
-
|
|
354
|
-
// Sentry: default to false, or else use the user's option
|
|
355
|
-
options.sentry = typeof options.sentry === 'undefined'
|
|
356
|
-
? false
|
|
357
|
-
: options.sentry;
|
|
358
|
-
|
|
359
|
-
// Log: default to sentry, or else use the user's option
|
|
360
|
-
options.log = typeof options.log === 'undefined'
|
|
361
|
-
? options.sentry
|
|
362
|
-
: options.log;
|
|
363
|
-
|
|
364
|
-
// Send: default to false, or else use the user's option
|
|
365
|
-
options.send = typeof options.send === 'undefined'
|
|
366
|
-
? false
|
|
367
|
-
: options.send;
|
|
368
|
-
|
|
369
|
-
// Stack: default to false, or else use the user's option
|
|
370
|
-
options.stack = typeof options.stack === 'undefined'
|
|
371
|
-
? false
|
|
372
|
-
: options.stack;
|
|
373
|
-
|
|
374
|
-
// Construct error
|
|
375
|
-
const newError = e instanceof Error
|
|
376
|
-
? e
|
|
377
|
-
: new Error(stringifyNonStrings(e));
|
|
378
|
-
|
|
379
|
-
// Fix code
|
|
380
|
-
// options.code = newError.code || options.code;
|
|
381
|
-
options.code = isCodeSet ? options.code : newError.code || options.code;
|
|
382
|
-
options.code = parseInt(options.code);
|
|
383
|
-
options.code = isBetween(options.code, 400, 599) ? options.code : 500;
|
|
384
|
-
|
|
385
|
-
// Attach properties
|
|
386
|
-
_attachHeaderProperties(self, options, newError);
|
|
387
|
-
|
|
388
|
-
// Log the error
|
|
389
|
-
if (options.log) {
|
|
390
|
-
self.error(newError);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Send error to Sentry
|
|
394
|
-
if (options.sentry) {
|
|
395
|
-
self.Manager.libraries.sentry.captureException(newError);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Quit and respond to the request only if the assistant has a res (it sometimes does not, like in auth().onCreate() triggers)
|
|
399
|
-
if (options.send && res?.status) {
|
|
400
|
-
let sendable = newError?.stack && options.stack
|
|
401
|
-
? newError?.stack
|
|
402
|
-
: newError?.message;
|
|
403
|
-
|
|
404
|
-
// Set error
|
|
405
|
-
sendable = `${sendable || newError || 'Unknown error'}`;
|
|
406
|
-
|
|
407
|
-
// Attach tag
|
|
408
|
-
if (newError.tag) {
|
|
409
|
-
// sendable = `(${newError.tag}) ${sendable}`;
|
|
410
|
-
sendable = `${sendable} (${newError.tag})`;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Clear log prefix before sending
|
|
414
|
-
self.clearLogPrefix();
|
|
415
|
-
|
|
416
|
-
// Log
|
|
417
|
-
if (options.log) {
|
|
418
|
-
self.log(`Sending response (${options.code}):`, JSON.stringify(sendable));
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Send response
|
|
422
|
-
res
|
|
423
|
-
.status(options.code)
|
|
424
|
-
.send(sendable);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return newError;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
BackendAssistant.prototype.errorManager = BackendAssistant.prototype.errorify;
|
|
431
|
-
|
|
432
|
-
BackendAssistant.prototype.redirect = function(response, options) {
|
|
433
|
-
const self = this;
|
|
434
|
-
const res = self.ref.res;
|
|
435
|
-
|
|
436
|
-
// Set options
|
|
437
|
-
options = options || {};
|
|
438
|
-
options.code = typeof options.code === 'undefined'
|
|
439
|
-
? 302
|
|
440
|
-
: options.code;
|
|
441
|
-
|
|
442
|
-
return self.respond(response, options);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
BackendAssistant.prototype.respond = function(response, options) {
|
|
446
|
-
const self = this;
|
|
447
|
-
const res = self.ref.res;
|
|
448
|
-
|
|
449
|
-
// Set options
|
|
450
|
-
options = options || {};
|
|
451
|
-
options.code = typeof options.code === 'undefined'
|
|
452
|
-
? 200
|
|
453
|
-
: options.code;
|
|
454
|
-
options.log = typeof options.log === 'undefined'
|
|
455
|
-
? true
|
|
456
|
-
: options.log;
|
|
457
|
-
|
|
458
|
-
// Fix code
|
|
459
|
-
options.code = parseInt(options.code);
|
|
460
|
-
|
|
461
|
-
// Handle error
|
|
462
|
-
const isErrorCode = isBetween(options.code, 400, 599);
|
|
463
|
-
if (
|
|
464
|
-
response instanceof Error
|
|
465
|
-
|| isErrorCode
|
|
466
|
-
) {
|
|
467
|
-
options.code = isErrorCode ? options.code : undefined;
|
|
468
|
-
options.send = true;
|
|
469
|
-
|
|
470
|
-
return self.errorify(response, options);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Attach properties
|
|
474
|
-
_attachHeaderProperties(self, options);
|
|
475
|
-
|
|
476
|
-
// Send response
|
|
477
|
-
res.status(options.code);
|
|
478
|
-
|
|
479
|
-
// Log function
|
|
480
|
-
function _log(text) {
|
|
481
|
-
if (options.log) {
|
|
482
|
-
self.log(`${text} (${options.code}):`, JSON.stringify(response));
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Clear log prefix before sending
|
|
487
|
-
self.clearLogPrefix();
|
|
488
|
-
|
|
489
|
-
// Redirect
|
|
490
|
-
const isRedirect = isBetween(options.code, 300, 399);
|
|
491
|
-
if (isRedirect) {
|
|
492
|
-
// Log
|
|
493
|
-
_log(`Redirecting`);
|
|
494
|
-
|
|
495
|
-
// Send
|
|
496
|
-
return res.redirect(response);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Log
|
|
500
|
-
_log(`Sending response`);
|
|
501
|
-
|
|
502
|
-
// If it is an object, send as json
|
|
503
|
-
if (
|
|
504
|
-
response
|
|
505
|
-
&& typeof response === 'object'
|
|
506
|
-
&& typeof res.json === 'function'
|
|
507
|
-
) {
|
|
508
|
-
return res.json(response);
|
|
509
|
-
} else {
|
|
510
|
-
return res.send(response);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function isBetween(value, min, max) {
|
|
515
|
-
return value >= min && value <= max;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function stringifyNonStrings(e) {
|
|
519
|
-
if (typeof e === 'string') {
|
|
520
|
-
return e;
|
|
521
|
-
} else {
|
|
522
|
-
return JSON.stringify(e);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function _attachHeaderProperties(self, options, error) {
|
|
527
|
-
// Create headers
|
|
528
|
-
const headers = {
|
|
529
|
-
code: options.code,
|
|
530
|
-
tag: self.tag,
|
|
531
|
-
usage: {
|
|
532
|
-
current: self?.usage?.getUsage() || {},
|
|
533
|
-
limits: self?.usage?.getLimit() || {},
|
|
534
|
-
},
|
|
535
|
-
additional: options.additional || {},
|
|
536
|
-
}
|
|
537
|
-
const req = self.ref.req;
|
|
538
|
-
const res = self.ref.res;
|
|
539
|
-
|
|
540
|
-
// Attach properties if this assistant has a res (it sometimes does not, like in auth().onCreate() triggers)
|
|
541
|
-
if (res?.header && res?.get) {
|
|
542
|
-
res.header('bm-properties', JSON.stringify(headers));
|
|
543
|
-
|
|
544
|
-
// Add bm-properties to Access-Control-Expose-Headers
|
|
545
|
-
const existingExposed = res.get('Access-Control-Expose-Headers') || '';
|
|
546
|
-
const newExposed = `${existingExposed}, bm-properties`.replace(/^, /, '');
|
|
547
|
-
|
|
548
|
-
if (!existingExposed.match(/bm-properties/i)) {
|
|
549
|
-
res.header('Access-Control-Expose-Headers', newExposed);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Attach properties
|
|
554
|
-
if (error) {
|
|
555
|
-
Object.keys(headers)
|
|
556
|
-
.forEach((item, i) => {
|
|
557
|
-
error[item] = headers[item];
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
BackendAssistant.prototype.authenticate = async function (options) {
|
|
563
|
-
const self = this;
|
|
564
|
-
|
|
565
|
-
// Shortcuts
|
|
566
|
-
let admin = self.ref.admin;
|
|
567
|
-
let functions = self.ref.functions;
|
|
568
|
-
let req = self.ref.req;
|
|
569
|
-
let res = self.ref.res;
|
|
570
|
-
let data = self.request.data;
|
|
571
|
-
|
|
572
|
-
// Build the ID token from the request
|
|
573
|
-
let idToken;
|
|
574
|
-
let backendManagerKey;
|
|
575
|
-
// let user;
|
|
576
|
-
|
|
577
|
-
// Set options
|
|
578
|
-
options = options || {};
|
|
579
|
-
options.resolve = typeof options.resolve === 'undefined' ? true : options.resolve;
|
|
580
|
-
options.debug = typeof options.debug === 'undefined' ? false : options.debug;
|
|
581
|
-
|
|
582
|
-
function _resolve(user) {
|
|
583
|
-
user = user || {};
|
|
584
|
-
user.authenticated = typeof user.authenticated === 'undefined'
|
|
585
|
-
? false
|
|
586
|
-
: user.authenticated;
|
|
587
|
-
|
|
588
|
-
if (options.resolve) {
|
|
589
|
-
self.request.user = self.resolveAccount(user);
|
|
590
|
-
return self.request.user;
|
|
591
|
-
} else {
|
|
592
|
-
return user;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Extract the BEM token
|
|
597
|
-
// Having this is separate from the ID token allows for the user to be authenticated as an ADMIN
|
|
598
|
-
if (options.backendManagerKey || data.backendManagerKey) {
|
|
599
|
-
// Read token from backendManagerKey or authenticationToken or apiKey
|
|
600
|
-
backendManagerKey = options.backendManagerKey || data.backendManagerKey;
|
|
601
|
-
|
|
602
|
-
// Log the token
|
|
603
|
-
self.log('Found "backendManagerKey" parameter', backendManagerKey);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// Extract the token / API key
|
|
607
|
-
// This is the main token that will be used to authenticate the user (it can be a JWT or a user's API key)
|
|
608
|
-
if (req?.headers?.authorization && req?.headers?.authorization?.startsWith('Bearer ')) {
|
|
609
|
-
// Read the ID Token from the Authorization header.
|
|
610
|
-
idToken = req.headers.authorization.split('Bearer ')[1];
|
|
611
|
-
|
|
612
|
-
// Log the token
|
|
613
|
-
self.log('Found "Authorization" header', idToken);
|
|
614
|
-
} else if (req?.cookies?.__session) {
|
|
615
|
-
// Read the ID Token from cookie.
|
|
616
|
-
idToken = req.cookies.__session;
|
|
617
|
-
|
|
618
|
-
// Log the token
|
|
619
|
-
self.log('Found "__session" cookie', idToken);
|
|
620
|
-
} else if (
|
|
621
|
-
options.authenticationToken || data.authenticationToken
|
|
622
|
-
|| options.apiKey || data.apiKey
|
|
623
|
-
) {
|
|
624
|
-
// Read token OR API Key from options or data
|
|
625
|
-
idToken = options.authenticationToken || data.authenticationToken
|
|
626
|
-
|| options.apiKey || data.apiKey;
|
|
627
|
-
|
|
628
|
-
// Log the token
|
|
629
|
-
self.log('Found "authenticationToken" parameter', idToken);
|
|
630
|
-
} else {
|
|
631
|
-
// No token found
|
|
632
|
-
return _resolve(self.request.user);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// Check if the token is a JWT
|
|
636
|
-
if (isJWT(idToken)) {
|
|
637
|
-
// Check with firebase
|
|
638
|
-
try {
|
|
639
|
-
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
|
640
|
-
|
|
641
|
-
// Log the token
|
|
642
|
-
if (options.debug) {
|
|
643
|
-
self.log('JWT token decoded', decodedIdToken.email, decodedIdToken.user_id);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// Get the user
|
|
647
|
-
await admin.firestore().doc(`users/${decodedIdToken.user_id}`)
|
|
648
|
-
.get()
|
|
649
|
-
.then((doc) => {
|
|
650
|
-
// Set the user
|
|
651
|
-
if (doc.exists) {
|
|
652
|
-
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
653
|
-
self.request.user.authenticated = true;
|
|
654
|
-
self.request.user.auth.uid = decodedIdToken.user_id;
|
|
655
|
-
self.request.user.auth.email = decodedIdToken.email;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// Log the user
|
|
659
|
-
if (options.debug) {
|
|
660
|
-
self.log('Found user doc', self.request.user)
|
|
661
|
-
}
|
|
662
|
-
})
|
|
663
|
-
|
|
664
|
-
// Return the user
|
|
665
|
-
return _resolve(self.request.user);
|
|
666
|
-
} catch (error) {
|
|
667
|
-
self.error('Error while verifying JWT:', error);
|
|
668
|
-
return _resolve(self.request.user);
|
|
669
|
-
}
|
|
670
|
-
} else {
|
|
671
|
-
// Query by API key
|
|
672
|
-
await admin.firestore().collection(`users`)
|
|
673
|
-
.where('api.privateKey', '==', apiKey)
|
|
674
|
-
.get()
|
|
675
|
-
.then(function(querySnapshot) {
|
|
676
|
-
querySnapshot.forEach(function(doc) {
|
|
677
|
-
self.request.user = doc.data();
|
|
678
|
-
self.request.user = Object.assign({}, self.request.user, doc.data());
|
|
679
|
-
self.request.user.authenticated = true;
|
|
680
|
-
});
|
|
681
|
-
})
|
|
682
|
-
.catch(function(error) {
|
|
683
|
-
console.error('Error getting documents: ', error);
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
return _resolve(self.request.user);
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
|
|
690
|
-
BackendAssistant.prototype.resolveAccount = function (user) {
|
|
691
|
-
const ResolveAccount = new (require('resolve-account'))();
|
|
692
|
-
|
|
693
|
-
return ResolveAccount.resolve(undefined, user)
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
BackendAssistant.prototype.parseRepo = function (repo) {
|
|
697
|
-
let repoSplit = repo.split('/');
|
|
698
|
-
for (var i = 0; i < repoSplit.length; i++) {
|
|
699
|
-
repoSplit[i] = repoSplit[i].replace('.git', '');
|
|
700
|
-
}
|
|
701
|
-
repoSplit = repoSplit.filter(function(value, index, arr){
|
|
702
|
-
return value !== 'http:' &&
|
|
703
|
-
value !== 'https:' &&
|
|
704
|
-
value !== '' &&
|
|
705
|
-
value !== 'github.com';
|
|
706
|
-
});
|
|
707
|
-
return {
|
|
708
|
-
user: repoSplit[0],
|
|
709
|
-
name: repoSplit[1],
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
BackendAssistant.prototype.getHeaderIp = function (headers) {
|
|
714
|
-
headers = headers || {};
|
|
715
|
-
|
|
716
|
-
return (
|
|
717
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
718
|
-
headers['cf-connecting-ip']
|
|
719
|
-
|| headers['fastly-temp-xff']
|
|
720
|
-
|
|
721
|
-
// these are present for non-cloudflare requests (11/21/2020)
|
|
722
|
-
|| headers['x-appengine-user-ip']
|
|
723
|
-
|| headers['x-forwarded-for']
|
|
724
|
-
|
|
725
|
-
// Not sure about these
|
|
726
|
-
// || headers['fastly-client-ip']
|
|
727
|
-
|
|
728
|
-
// If unsure, return local IP
|
|
729
|
-
|| '127.0.0.1'
|
|
730
|
-
)
|
|
731
|
-
.split(',')[0]
|
|
732
|
-
.trim();
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
BackendAssistant.prototype.getHeaderContinent = function (headers) {
|
|
736
|
-
headers = headers || {};
|
|
737
|
-
|
|
738
|
-
return (
|
|
739
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
740
|
-
headers['cf-ipcontinent']
|
|
741
|
-
|
|
742
|
-
// If unsure, return ZZ
|
|
743
|
-
|| 'ZZ'
|
|
744
|
-
)
|
|
745
|
-
.split(',')[0]
|
|
746
|
-
.trim();
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
BackendAssistant.prototype.getHeaderCountry = function (headers) {
|
|
750
|
-
headers = headers || {};
|
|
751
|
-
|
|
752
|
-
return (
|
|
753
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
754
|
-
headers['cf-ipcountry']
|
|
755
|
-
|
|
756
|
-
//
|
|
757
|
-
|| headers['x-country-code']
|
|
758
|
-
|
|
759
|
-
// these are present for non-cloudflare requests (11/21/2020)
|
|
760
|
-
|| headers['x-appengine-country']
|
|
761
|
-
|
|
762
|
-
// If unsure, return ZZ
|
|
763
|
-
|| 'ZZ'
|
|
764
|
-
)
|
|
765
|
-
.split(',')[0]
|
|
766
|
-
.trim();
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
BackendAssistant.prototype.getHeaderRegion = function (headers) {
|
|
770
|
-
headers = headers || {};
|
|
771
|
-
|
|
772
|
-
return (
|
|
773
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
774
|
-
headers['cf-region']
|
|
775
|
-
|
|
776
|
-
// these are present for non-cloudflare requests (11/21/2020)
|
|
777
|
-
|| headers['x-appengine-region']
|
|
778
|
-
|
|
779
|
-
// If unsure, return unknown
|
|
780
|
-
|| 'Unknown'
|
|
781
|
-
)
|
|
782
|
-
.split(',')[0]
|
|
783
|
-
.trim();
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
BackendAssistant.prototype.getHeaderCity = function (headers) {
|
|
787
|
-
headers = headers || {};
|
|
788
|
-
|
|
789
|
-
return (
|
|
790
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
791
|
-
headers['cf-ipcity']
|
|
792
|
-
|
|
793
|
-
|| headers['x-appengine-city']
|
|
794
|
-
|
|
795
|
-
// If unsure, return unknown
|
|
796
|
-
|| 'Unknown'
|
|
797
|
-
)
|
|
798
|
-
.split(',')[0]
|
|
799
|
-
.trim();
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
BackendAssistant.prototype.getHeaderLatitude = function (headers) {
|
|
803
|
-
headers = headers || {};
|
|
804
|
-
|
|
805
|
-
return parseFloat((
|
|
806
|
-
// these are present for cloudflare requests (11/21/2020)
|
|
807
|
-
headers['cf-iplatitude']
|
|
808
|
-
|
|
809
|
-
|| (headers['x-appengine-citylatlong'] || '').split(',')[0]
|
|
810
|
-
|
|
811
|
-
// If unsure, return unknown
|
|
812
|
-
|| '0'
|
|
813
|
-
)
|
|
814
|
-
.split(',')[0]
|
|
815
|
-
.trim());
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
BackendAssistant.prototype.getHeaderLongitude = function (headers) {
|
|
819
|
-
headers = headers || {};
|
|
820
|
-
|
|
821
|
-
return parseFloat((
|
|
822
|
-
// Cloudflare requests
|
|
823
|
-
headers['cf-iplongitude']
|
|
824
|
-
|
|
825
|
-
|| (headers['x-appengine-citylatlong'] || '').split(',')[1]
|
|
826
|
-
|
|
827
|
-
// If unsure, return unknown
|
|
828
|
-
|| '0'
|
|
829
|
-
)
|
|
830
|
-
.split(',')[0]
|
|
831
|
-
.trim());
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
BackendAssistant.prototype.getHeaderUserAgent = function (headers) {
|
|
836
|
-
headers = headers || {};
|
|
837
|
-
|
|
838
|
-
return (
|
|
839
|
-
headers['user-agent']
|
|
840
|
-
|| ''
|
|
841
|
-
)
|
|
842
|
-
.trim();
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
BackendAssistant.prototype.getHeaderLanguage = function (headers) {
|
|
846
|
-
headers = headers || {};
|
|
847
|
-
|
|
848
|
-
return (
|
|
849
|
-
headers['accept-language']
|
|
850
|
-
|| headers['x-orig-accept-language']
|
|
851
|
-
|| ''
|
|
852
|
-
)
|
|
853
|
-
.trim();
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
BackendAssistant.prototype.getHeaderPlatform = function (headers) {
|
|
857
|
-
headers = headers || {};
|
|
858
|
-
|
|
859
|
-
return (
|
|
860
|
-
headers['sec-ch-ua-platform']
|
|
861
|
-
|| ''
|
|
862
|
-
)
|
|
863
|
-
.replace(/"/ig, '')
|
|
864
|
-
.trim();
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
BackendAssistant.prototype.getHeaderMobile = function (headers) {
|
|
868
|
-
headers = headers || {};
|
|
869
|
-
|
|
870
|
-
// Will be ?0 if fale or ?1 if true
|
|
871
|
-
const mobile = (headers['sec-ch-ua-mobile'] || '').replace(/\?/ig, '');
|
|
872
|
-
|
|
873
|
-
return mobile === '1' || mobile === true || mobile === 'true';
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
/**
|
|
877
|
-
* Parses a 'multipart/form-data' upload request
|
|
878
|
-
*
|
|
879
|
-
* @param {Object} req Cloud Function request context.
|
|
880
|
-
* @param {Object} res Cloud Function response context.
|
|
881
|
-
*/
|
|
882
|
-
// https://cloud.google.com/functions/docs/writing/http#multipart_data
|
|
883
|
-
BackendAssistant.prototype.parseMultipartFormData = function (options) {
|
|
884
|
-
const self = this;
|
|
885
|
-
return new Promise(function(resolve, reject) {
|
|
886
|
-
if (!self.initialized) {
|
|
887
|
-
return reject(new Error('Cannot run .parseMultipartForm() until .init() has been called'));
|
|
888
|
-
}
|
|
889
|
-
const existingData = self.request.multipartData;
|
|
890
|
-
const getFields = existingData?.fields || {};
|
|
891
|
-
const getFiles = existingData?.files || {};
|
|
892
|
-
|
|
893
|
-
// If there are already fields or files, return them
|
|
894
|
-
if (Object.keys(getFields).length + Object.keys(getFiles).length > 0) {
|
|
895
|
-
return resolve(existingData);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Set options
|
|
899
|
-
options = options || {};
|
|
900
|
-
|
|
901
|
-
// Set headers
|
|
902
|
-
const fs = require('fs');
|
|
903
|
-
const req = self.ref.req;
|
|
904
|
-
const res = self.ref.res;
|
|
905
|
-
|
|
906
|
-
// Node.js doesn't have a built-in multipart/form-data parsing library.
|
|
907
|
-
// Instead, we can use the 'busboy' library from NPM to parse these requests.
|
|
908
|
-
const busboy = require('busboy');
|
|
909
|
-
const jetpack = require('fs-jetpack');
|
|
910
|
-
|
|
911
|
-
// if (req.method !== 'POST') {
|
|
912
|
-
// // Return a "method not allowed" error
|
|
913
|
-
// return res.status(405).end();
|
|
914
|
-
// }
|
|
915
|
-
options.headers = options.headers || req.headers;
|
|
916
|
-
options.limits = options.limits || {};
|
|
917
|
-
|
|
918
|
-
// console.log('++++++++options.headers', options.headers);
|
|
919
|
-
// console.log('++++++++req.rawBody', req.rawBody);
|
|
920
|
-
// console.log('++++++++options.limits', options.limits);
|
|
921
|
-
// console.log('----req.rawBody', req.rawBody);
|
|
922
|
-
|
|
923
|
-
// https://github.com/mscdex/busboy
|
|
924
|
-
// https://github.com/mscdex/busboy/issues/266
|
|
925
|
-
const bb = busboy({
|
|
926
|
-
headers: options.headers,
|
|
927
|
-
limits: options.limits,
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
// This object will accumulate all the fields, keyed by their name
|
|
931
|
-
const fields = {};
|
|
932
|
-
|
|
933
|
-
// This object will accumulate all the uploaded files, keyed by their name.
|
|
934
|
-
const uploads = {};
|
|
935
|
-
|
|
936
|
-
// This code will process each non-file field in the form.
|
|
937
|
-
bb.on('field', (fieldname, val, info) => {
|
|
938
|
-
// console.log(`Processed field ${fieldname}: ${val}.`);
|
|
939
|
-
fields[fieldname] = val;
|
|
940
|
-
});
|
|
941
|
-
|
|
942
|
-
const fileWrites = [];
|
|
943
|
-
|
|
944
|
-
// This code will process each file uploaded.
|
|
945
|
-
bb.on('file', (fieldname, file, info) => {
|
|
946
|
-
// file.on('error', (e) => {
|
|
947
|
-
// console.error('File error', e);
|
|
948
|
-
// });
|
|
949
|
-
// Note: os.tmpdir() points to an in-memory file system on GCF
|
|
950
|
-
// Thus, any files in it must fit in the instance's memory.
|
|
951
|
-
jetpack.dir(self.tmpdir)
|
|
952
|
-
|
|
953
|
-
const filename = info.filename;
|
|
954
|
-
const filepath = path.join(self.tmpdir, filename);
|
|
955
|
-
uploads[fieldname] = filepath;
|
|
956
|
-
const writeStream = fs.createWriteStream(filepath);
|
|
957
|
-
file.pipe(writeStream);
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
// File was processed by Busboy; wait for it to be written.
|
|
961
|
-
// Note: GCF may not persist saved files across invocations.
|
|
962
|
-
// Persistent files must be kept in other locations
|
|
963
|
-
// (such as Cloud Storage buckets).
|
|
964
|
-
const promise = new Promise((resolve, reject) => {
|
|
965
|
-
file.on('end', () => {
|
|
966
|
-
writeStream.end();
|
|
967
|
-
});
|
|
968
|
-
writeStream.on('finish', resolve);
|
|
969
|
-
writeStream.on('error', reject);
|
|
970
|
-
});
|
|
971
|
-
fileWrites.push(promise);
|
|
972
|
-
});
|
|
973
|
-
|
|
974
|
-
// bb.on('error', async (e) => {
|
|
975
|
-
// console.error('Busboy error', e);
|
|
976
|
-
// })
|
|
977
|
-
|
|
978
|
-
// Triggered once all uploaded files are processed by Busboy.
|
|
979
|
-
// We still need to wait for the disk writes (saves) to complete.
|
|
980
|
-
bb.on('finish', async () => {
|
|
981
|
-
await Promise.all(fileWrites);
|
|
982
|
-
|
|
983
|
-
/**
|
|
984
|
-
* TODO(developer): Process saved files here
|
|
985
|
-
*/
|
|
986
|
-
// for (const file in uploads) {
|
|
987
|
-
// fs.unlinkSync(uploads[file]);
|
|
988
|
-
// }
|
|
989
|
-
// res.send();
|
|
990
|
-
self.request.multipartData = {
|
|
991
|
-
fields: fields,
|
|
992
|
-
files: uploads,
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
return resolve(self.request.multipartData);
|
|
996
|
-
});
|
|
997
|
-
|
|
998
|
-
// Because of an error when using in both Optiic glitch server and ITWCW firebase functions
|
|
999
|
-
if (req.rawBody) {
|
|
1000
|
-
return bb.end(req.rawBody);
|
|
1001
|
-
} else {
|
|
1002
|
-
return req.pipe(bb);
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
const isJWT = (token) => {
|
|
1008
|
-
// Ensure the token has three parts separated by dots
|
|
1009
|
-
const parts = token.split('.');
|
|
1010
|
-
|
|
1011
|
-
try {
|
|
1012
|
-
// Decode the header (first part) to verify it is JSON
|
|
1013
|
-
const header = JSON.parse(Buffer.from(parts[0], 'base64').toString('utf8'));
|
|
1014
|
-
// Check for expected JWT keys in the header
|
|
1015
|
-
return header.alg && header.typ === 'JWT';
|
|
1016
|
-
} catch (err) {
|
|
1017
|
-
// If parsing fails, it's not a valid JWT
|
|
1018
|
-
return false;
|
|
1019
|
-
}
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
// Not sure what this is for? But it has a good serializer code
|
|
1023
|
-
// Disabled 2024-03-21 because there was another stringify() function that i was intending to use but it was actually using this
|
|
1024
|
-
// It was adding escaped quotes to strings
|
|
1025
|
-
// function stringify(obj, replacer, spaces, cycleReplacer) {
|
|
1026
|
-
// return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces)
|
|
1027
|
-
// }
|
|
1028
|
-
|
|
1029
|
-
// // https://github.com/moll/json-stringify-safe/blob/master/stringify.js
|
|
1030
|
-
// function serializer(replacer, cycleReplacer) {
|
|
1031
|
-
// var stack = [], keys = []
|
|
1032
|
-
|
|
1033
|
-
// if (cycleReplacer == null) cycleReplacer = function(key, value) {
|
|
1034
|
-
// if (stack[0] === value) return '[Circular ~]'
|
|
1035
|
-
// return `[Circular ~.${keys.slice(0, stack.indexOf(value)).join('.')}]`;
|
|
1036
|
-
// }
|
|
1037
|
-
|
|
1038
|
-
// return function(key, value) {
|
|
1039
|
-
// if (stack.length > 0) {
|
|
1040
|
-
// var thisPos = stack.indexOf(this)
|
|
1041
|
-
// ~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
|
|
1042
|
-
// ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
|
|
1043
|
-
// if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value)
|
|
1044
|
-
// }
|
|
1045
|
-
// else stack.push(value)
|
|
1046
|
-
|
|
1047
|
-
// return replacer == null ? value : replacer.call(this, key, value)
|
|
1048
|
-
// }
|
|
1049
|
-
// }
|
|
1050
|
-
|
|
1051
|
-
module.exports = BackendAssistant;
|