backend-manager 3.0.62 → 3.0.64
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/package.json +1 -1
- package/src/manager/functions/core/actions/api/user/sign-up.js +1 -1
- package/src/manager/functions/core/cron/daily/reset-usage.js +9 -0
- package/src/manager/functions/core/events/auth/before-create.js +2 -0
- package/src/manager/helpers/assistant.js +89 -11
- package/src/manager/helpers/middleware.js +70 -0
- package/src/manager/helpers/usage.js +60 -21
- package/src/manager/index.js +6 -0
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ Module.prototype.main = function () {
|
|
|
23
23
|
// Get auth user from firebase
|
|
24
24
|
const ip = assistant.request.geolocation.ip;
|
|
25
25
|
const authUser = await Manager.libraries.admin.auth().getUser(user.auth.uid).catch(e => e);
|
|
26
|
-
const usage = await Manager.Usage().init(assistant, {log: true,
|
|
26
|
+
const usage = await Manager.Usage().init(assistant, {log: true, key: ip});
|
|
27
27
|
|
|
28
28
|
if (authUser instanceof Error) {
|
|
29
29
|
return reject(assistant.errorManager(`Failed to get auth user: ${authUser}`, {code: 500, sentry: false, send: false, log: false}).error)
|
|
@@ -163,6 +163,15 @@ Module.prototype.clearFirestore = function() {
|
|
|
163
163
|
})
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// Clear temporary/usage in firestore by deleting the doc
|
|
167
|
+
await libraries.admin.firestore().doc(`temporary/usage`).delete()
|
|
168
|
+
.then(r => {
|
|
169
|
+
assistant.log(`cron/daily/reset-usage() [firestore]: Deleted temporary/usage`);
|
|
170
|
+
})
|
|
171
|
+
.catch(e => {
|
|
172
|
+
assistant.errorManager(`Error deleting temporary/usage: ${e}`, {sentry: false, send: false, log: true})
|
|
173
|
+
})
|
|
174
|
+
|
|
166
175
|
return resolve();
|
|
167
176
|
});
|
|
168
177
|
}
|
|
@@ -31,6 +31,8 @@ Module.prototype.main = function () {
|
|
|
31
31
|
|
|
32
32
|
assistant.log(`Request: ${user.uid}`, user, context);
|
|
33
33
|
|
|
34
|
+
// TODO: ⛔️⛔️⛔️ UTILIZE THE NEW .usage() system (similar to src/manager/functions/core/actions/api/user/sign-up.js)
|
|
35
|
+
|
|
34
36
|
// if (context.additionalUserInfo.recaptchaScore < 0.5) {
|
|
35
37
|
// assistant.error(`Recaptcha score (${context.additionalUserInfo.recaptchaScore}) too low for ${user.uid}`);
|
|
36
38
|
|
|
@@ -33,6 +33,11 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
33
33
|
|
|
34
34
|
const now = new Date();
|
|
35
35
|
|
|
36
|
+
// Attached libraries - used in .errorify()
|
|
37
|
+
self.analytics = null;
|
|
38
|
+
self.usage = null;
|
|
39
|
+
|
|
40
|
+
// Set meta
|
|
36
41
|
self.meta = {};
|
|
37
42
|
|
|
38
43
|
self.meta.startTime = {};
|
|
@@ -43,6 +48,7 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
43
48
|
self.meta.environment = options.environment || self.getEnvironment();
|
|
44
49
|
self.meta.type = options.functionType || process.env.FUNCTION_SIGNATURE_TYPE || 'unknown';
|
|
45
50
|
|
|
51
|
+
// Set ref
|
|
46
52
|
self.ref = {};
|
|
47
53
|
ref = ref || {};
|
|
48
54
|
self.ref.res = ref.res || {};
|
|
@@ -50,14 +56,17 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
50
56
|
self.ref.admin = ref.admin || {};
|
|
51
57
|
self.ref.functions = ref.functions || {};
|
|
52
58
|
self.ref.Manager = ref.Manager || {};
|
|
59
|
+
self.Manager = self.ref.Manager;
|
|
53
60
|
|
|
54
61
|
// Set ID
|
|
55
62
|
try {
|
|
56
|
-
self.id = self.
|
|
63
|
+
self.id = self.Manager.Utilities().randomId();
|
|
57
64
|
} catch {
|
|
58
65
|
self.id = now.getTime();
|
|
59
66
|
}
|
|
60
67
|
|
|
68
|
+
self.tag = `${self.meta.name}/${self.id}`;
|
|
69
|
+
|
|
61
70
|
// Set stuff about request
|
|
62
71
|
self.request = {};
|
|
63
72
|
self.request.referrer = (self.ref.req.headers || {}).referrer || (self.ref.req.headers || {}).referer || '';
|
|
@@ -146,6 +155,7 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
146
155
|
self.constant.pastTime.timestamp = '1999-01-01T00:00:00Z';
|
|
147
156
|
self.constant.pastTime.timestampUNIX = 915148800;
|
|
148
157
|
|
|
158
|
+
// Log options
|
|
149
159
|
if (
|
|
150
160
|
(self.meta.environment === 'development')
|
|
151
161
|
&& ((self.request.method !== 'OPTIONS') || (self.request.method === 'OPTIONS' && options.showOptionsLog))
|
|
@@ -155,8 +165,10 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
155
165
|
console.log(options.optionsLogString);
|
|
156
166
|
}
|
|
157
167
|
|
|
168
|
+
// Set tmpdir
|
|
158
169
|
self.tmpdir = path.resolve(os.tmpdir(), options.fileSavePath, uuid.v4());
|
|
159
170
|
|
|
171
|
+
// Set initialized
|
|
160
172
|
self.initialized = true;
|
|
161
173
|
|
|
162
174
|
return self;
|
|
@@ -279,7 +291,7 @@ BackendAssistant.prototype._log = function() {
|
|
|
279
291
|
const logs = [...Array.prototype.slice.call(arguments)];
|
|
280
292
|
|
|
281
293
|
// 2. Prepend log prefix log string
|
|
282
|
-
logs.unshift(`[${self.
|
|
294
|
+
logs.unshift(`[${self.tag} @ ${new Date().toISOString()}]:`);
|
|
283
295
|
|
|
284
296
|
// 3. Pass along arguments to console.log
|
|
285
297
|
if (logs[1] === 'error') {
|
|
@@ -335,22 +347,36 @@ BackendAssistant.prototype._log = function() {
|
|
|
335
347
|
BackendAssistant.prototype.errorManager = function(e, options) {
|
|
336
348
|
const self = this;
|
|
337
349
|
|
|
350
|
+
// Set options
|
|
338
351
|
options = options || {};
|
|
339
|
-
options.code = typeof options.code === 'undefined'
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
options.
|
|
343
|
-
|
|
352
|
+
options.code = typeof options.code === 'undefined'
|
|
353
|
+
? 500
|
|
354
|
+
: options.code;
|
|
355
|
+
options.log = typeof options.log === 'undefined'
|
|
356
|
+
? true
|
|
357
|
+
: options.log;
|
|
358
|
+
options.sentry = typeof options.sentry === 'undefined'
|
|
359
|
+
? true
|
|
360
|
+
: options.sentry;
|
|
361
|
+
options.send = typeof options.send === 'undefined'
|
|
362
|
+
? true
|
|
363
|
+
: options.send;
|
|
364
|
+
|
|
365
|
+
// Construct error
|
|
344
366
|
const newError = e instanceof Error
|
|
345
367
|
? e
|
|
346
368
|
: new Error(stringify(e));
|
|
347
369
|
|
|
370
|
+
options.code = newError.code || options.code;
|
|
371
|
+
|
|
348
372
|
// Attach properties
|
|
349
373
|
Object.keys(options)
|
|
350
374
|
.forEach((item, i) => {
|
|
351
|
-
Object.assign(newError
|
|
375
|
+
Object.assign(newError, { [item]: options[item] });
|
|
352
376
|
});
|
|
353
377
|
|
|
378
|
+
// Attach properties
|
|
379
|
+
_attachHeaderProperties(self, options);
|
|
354
380
|
|
|
355
381
|
// Log the error
|
|
356
382
|
if (options.log) {
|
|
@@ -359,12 +385,17 @@ BackendAssistant.prototype.errorManager = function(e, options) {
|
|
|
359
385
|
|
|
360
386
|
// Send error to Sentry
|
|
361
387
|
if (options.sentry) {
|
|
362
|
-
self.
|
|
388
|
+
self.Manager.libraries.sentry.captureException(newError);
|
|
363
389
|
}
|
|
364
390
|
|
|
365
391
|
// Quit and respond to the request
|
|
366
392
|
if (options.send && self.ref.res && self.ref.res.status) {
|
|
367
|
-
self.ref.res
|
|
393
|
+
self.ref.res
|
|
394
|
+
.status(options.code)
|
|
395
|
+
.send(newError
|
|
396
|
+
? newError.message || newError
|
|
397
|
+
: 'Unknown error'
|
|
398
|
+
);
|
|
368
399
|
}
|
|
369
400
|
|
|
370
401
|
return {
|
|
@@ -372,6 +403,38 @@ BackendAssistant.prototype.errorManager = function(e, options) {
|
|
|
372
403
|
}
|
|
373
404
|
}
|
|
374
405
|
|
|
406
|
+
BackendAssistant.prototype.errorify = BackendAssistant.prototype.errorManager;
|
|
407
|
+
|
|
408
|
+
BackendAssistant.prototype.respond = function(response, options) {
|
|
409
|
+
const self = this;
|
|
410
|
+
|
|
411
|
+
// Set options
|
|
412
|
+
options = options || {};
|
|
413
|
+
options.code = typeof options.code === 'undefined'
|
|
414
|
+
? 200
|
|
415
|
+
: options.code;
|
|
416
|
+
options.log = typeof options.log === 'undefined'
|
|
417
|
+
? true
|
|
418
|
+
: options.log;
|
|
419
|
+
|
|
420
|
+
// Attach properties
|
|
421
|
+
_attachHeaderProperties(self, options);
|
|
422
|
+
|
|
423
|
+
// Log the error
|
|
424
|
+
if (options.log) {
|
|
425
|
+
self.log(`Responding with ${options.code} code:`, JSON.stringify(response));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Send response
|
|
429
|
+
self.ref.res.status(options.code);
|
|
430
|
+
|
|
431
|
+
if (typeof response === 'string') {
|
|
432
|
+
self.ref.res.send(response);
|
|
433
|
+
} else {
|
|
434
|
+
self.ref.res.json(response);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
375
438
|
function stringify(e) {
|
|
376
439
|
if (typeof e === 'string') {
|
|
377
440
|
return e;
|
|
@@ -380,6 +443,21 @@ function stringify(e) {
|
|
|
380
443
|
}
|
|
381
444
|
}
|
|
382
445
|
|
|
446
|
+
function _attachHeaderProperties(self, options) {
|
|
447
|
+
// Create headers
|
|
448
|
+
const headers = {
|
|
449
|
+
code: options.code,
|
|
450
|
+
tag: self.tag,
|
|
451
|
+
usage: {
|
|
452
|
+
current: self?.usage?.getUsage() || {},
|
|
453
|
+
limits: self?.usage?.getLimit() || {},
|
|
454
|
+
},
|
|
455
|
+
additional: options.additional || {},
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Attach properties
|
|
459
|
+
self.ref.res.header('bm-properties', JSON.stringify(headers));
|
|
460
|
+
}
|
|
383
461
|
|
|
384
462
|
BackendAssistant.prototype.authenticate = async function (options) {
|
|
385
463
|
const self = this;
|
|
@@ -422,7 +500,7 @@ BackendAssistant.prototype.authenticate = async function (options) {
|
|
|
422
500
|
// Check with custom BEM Token
|
|
423
501
|
let storedApiKey;
|
|
424
502
|
try {
|
|
425
|
-
const workingConfig = _.get(self.
|
|
503
|
+
const workingConfig = _.get(self.Manager, 'config') || functions.config();
|
|
426
504
|
storedApiKey = _.get(workingConfig, 'backend_manager.key', '')
|
|
427
505
|
} catch (e) {
|
|
428
506
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware
|
|
3
|
+
* Used to handle middleware for the assistant
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
function Middleware(m) {
|
|
9
|
+
const self = this;
|
|
10
|
+
|
|
11
|
+
self.Manager = m;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Middleware.prototype.run = function (library, req, res, options) {
|
|
15
|
+
const self = this;
|
|
16
|
+
const Manager = self.Manager;
|
|
17
|
+
const { cors } = Manager.libraries;
|
|
18
|
+
|
|
19
|
+
return cors(req, res, async () => {
|
|
20
|
+
const assistant = Manager.Assistant({req: req, res: res});
|
|
21
|
+
|
|
22
|
+
const data = assistant.request.data;
|
|
23
|
+
const geolocation = assistant.request.geolocation;
|
|
24
|
+
const client = assistant.request.client;
|
|
25
|
+
|
|
26
|
+
// Set options
|
|
27
|
+
options = options || {};
|
|
28
|
+
options.setupAnalytics = typeof options.setupAnalytics === 'boolean' ? options.setupAnalytics : true;
|
|
29
|
+
options.setupUsage = typeof options.setupUsage === 'boolean' ? options.setupUsage : true;
|
|
30
|
+
|
|
31
|
+
// Log
|
|
32
|
+
assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city})`, JSON.stringify(data));
|
|
33
|
+
|
|
34
|
+
// Load library
|
|
35
|
+
try {
|
|
36
|
+
library = path.resolve(process.cwd(), `${library}.js`);
|
|
37
|
+
library = new (require(library))();
|
|
38
|
+
} catch (e) {
|
|
39
|
+
assistant.errorManager(`Unable to load library @ (${library}): ${e.message}`, {sentry: true, send: true, log: true});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Setup analytics
|
|
43
|
+
if (options.setupAnalytics) {
|
|
44
|
+
assistant.analytics = Manager.Analytics({
|
|
45
|
+
assistant: assistant,
|
|
46
|
+
uuid: assistant.request.geolocation.ip,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Setup usage
|
|
51
|
+
if (options.setupUsage) {
|
|
52
|
+
assistant.usage = await Manager.Usage().init(assistant);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Process
|
|
56
|
+
try {
|
|
57
|
+
library.main(assistant, req, res)
|
|
58
|
+
// .then(result => {
|
|
59
|
+
// return res.status(200).json(result);
|
|
60
|
+
// })
|
|
61
|
+
.catch(e => {
|
|
62
|
+
assistant.errorManager(e, {sentry: true, send: true, log: true});
|
|
63
|
+
});
|
|
64
|
+
} catch (e) {
|
|
65
|
+
assistant.errorManager(e, {sentry: true, send: true, log: true});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
module.exports = Middleware;
|
|
@@ -41,7 +41,8 @@ Usage.prototype.init = function (assistant, options) {
|
|
|
41
41
|
options.refetch = typeof options.refetch === 'undefined' ? false : options.refetch;
|
|
42
42
|
options.clear = typeof options.clear === 'undefined' ? false : options.clear;
|
|
43
43
|
options.today = typeof options.today === 'undefined' ? undefined : options.today;
|
|
44
|
-
options.
|
|
44
|
+
options.key = typeof options.key === 'undefined' ? undefined : options.key;
|
|
45
|
+
options.unauthenticatedMode = typeof options.unauthenticatedMode === 'undefined' ? 'firestore' : options.unauthenticatedMode;
|
|
45
46
|
options.log = typeof options.log === 'undefined' ? false : options.log;
|
|
46
47
|
|
|
47
48
|
// Check for required options
|
|
@@ -59,11 +60,11 @@ Usage.prototype.init = function (assistant, options) {
|
|
|
59
60
|
self.storage = Manager.storage({name: 'usage', temporary: true, clear: options.clear, log: options.log});
|
|
60
61
|
|
|
61
62
|
// Set local key
|
|
62
|
-
|
|
63
|
+
self.key = (options.key || self.assistant.request.geolocation.ip || '')
|
|
63
64
|
.replace(/[\.:]/g, '_');
|
|
64
65
|
|
|
65
66
|
// Set paths
|
|
66
|
-
self.paths.user = `users.${
|
|
67
|
+
self.paths.user = `users.${self.key}`;
|
|
67
68
|
self.paths.app = `apps.${options.app}`;
|
|
68
69
|
|
|
69
70
|
// Get storage data
|
|
@@ -73,10 +74,26 @@ Usage.prototype.init = function (assistant, options) {
|
|
|
73
74
|
// Authenticate user (user will be resolved as well)
|
|
74
75
|
self.user = await assistant.authenticate();
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
self.useUnauthenticatedStorage = !self.user.auth.uid || self.options.key;
|
|
78
|
+
|
|
79
|
+
// Load usage with temporary if unauthenticated
|
|
80
|
+
if (self.useUnauthenticatedStorage) {
|
|
81
|
+
let foundUsage;
|
|
82
|
+
|
|
83
|
+
if (options.unauthenticatedMode === 'firestore') {
|
|
84
|
+
foundUsage = await Manager.libraries.admin.firestore().doc(`temporary/usage`)
|
|
85
|
+
.get()
|
|
86
|
+
.then((r) => {
|
|
87
|
+
return r.data()?.[`${self.key}`];
|
|
88
|
+
})
|
|
89
|
+
.catch((e) => {
|
|
90
|
+
assistant.errorManager(`Usage.init(): Error fetching usage data: ${e}`, {sentry: true, send: false, log: true});
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
foundUsage = self.storage.get(`${self.paths.user}.usage`, {}).value();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
self.user.usage = foundUsage ? foundUsage : self.user.usage;
|
|
80
97
|
}
|
|
81
98
|
|
|
82
99
|
// Log
|
|
@@ -209,7 +226,7 @@ Usage.prototype.set = function (path, value) {
|
|
|
209
226
|
const assistant = self.assistant;
|
|
210
227
|
|
|
211
228
|
// Update total and period
|
|
212
|
-
const resolved = `usage.${path}
|
|
229
|
+
const resolved = `usage.${path}.period`;
|
|
213
230
|
|
|
214
231
|
value = value || 0;
|
|
215
232
|
|
|
@@ -227,7 +244,11 @@ Usage.prototype.getUsage = function (path) {
|
|
|
227
244
|
const Manager = self.Manager;
|
|
228
245
|
const assistant = self.assistant;
|
|
229
246
|
|
|
230
|
-
|
|
247
|
+
if (path) {
|
|
248
|
+
return _.get(self.user, `usage.${path}.period`, 0);
|
|
249
|
+
} else {
|
|
250
|
+
return self.user.usage;
|
|
251
|
+
}
|
|
231
252
|
};
|
|
232
253
|
|
|
233
254
|
Usage.prototype.getLimit = function (path) {
|
|
@@ -235,7 +256,13 @@ Usage.prototype.getLimit = function (path) {
|
|
|
235
256
|
const Manager = self.Manager;
|
|
236
257
|
const assistant = self.assistant;
|
|
237
258
|
|
|
238
|
-
|
|
259
|
+
const key = `products.${self.options.app}-${self.user.plan.id}.limits`;
|
|
260
|
+
|
|
261
|
+
if (path) {
|
|
262
|
+
return _.get(self.app, `${key}.${path}`, 0);
|
|
263
|
+
} else {
|
|
264
|
+
return _.get(self.app, key, {});
|
|
265
|
+
}
|
|
239
266
|
};
|
|
240
267
|
|
|
241
268
|
Usage.prototype.update = function () {
|
|
@@ -245,11 +272,29 @@ Usage.prototype.update = function () {
|
|
|
245
272
|
const Manager = self.Manager;
|
|
246
273
|
const assistant = self.assistant;
|
|
247
274
|
|
|
248
|
-
// Write self.user to firestore or local if no user or if
|
|
249
|
-
if (
|
|
250
|
-
self.
|
|
251
|
-
|
|
252
|
-
|
|
275
|
+
// Write self.user to firestore or local if no user or if key is set
|
|
276
|
+
if (self.useUnauthenticatedStorage) {
|
|
277
|
+
if (self.options.unauthenticatedMode === 'firestore') {
|
|
278
|
+
Manager.libraries.admin.firestore().doc(`temporary/usage`)
|
|
279
|
+
.set({
|
|
280
|
+
[`${self.key}`]: self.user.usage,
|
|
281
|
+
}, {merge: true})
|
|
282
|
+
.then(() => {
|
|
283
|
+
self.log(`Usage.update(): Updated user.usage in firestore`, self.user.usage);
|
|
284
|
+
|
|
285
|
+
return resolve();
|
|
286
|
+
})
|
|
287
|
+
.catch(e => {
|
|
288
|
+
return reject(assistant.errorManager(e, {sentry: true, send: false, log: false}));
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
self.storage.set(`${self.paths.user}.usage`, self.user.usage).write();
|
|
292
|
+
|
|
293
|
+
self.log(`Usage.update(): Updated user.usage in local storage`, self.user.usage);
|
|
294
|
+
|
|
295
|
+
return resolve();
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
253
298
|
Manager.libraries.admin.firestore().doc(`users/${self.user.auth.uid}`)
|
|
254
299
|
.set({
|
|
255
300
|
usage: self.user.usage,
|
|
@@ -262,12 +307,6 @@ Usage.prototype.update = function () {
|
|
|
262
307
|
.catch(e => {
|
|
263
308
|
return reject(assistant.errorManager(e, {sentry: true, send: false, log: false}));
|
|
264
309
|
});
|
|
265
|
-
} else {
|
|
266
|
-
self.storage.set(`${self.paths.user}.usage`, self.user.usage).write();
|
|
267
|
-
|
|
268
|
-
self.log(`Usage.update(): Updated user.usage in local storage`, self.user.usage);
|
|
269
|
-
|
|
270
|
-
return resolve();
|
|
271
310
|
}
|
|
272
311
|
});
|
|
273
312
|
};
|
package/src/manager/index.js
CHANGED
|
@@ -606,6 +606,12 @@ Manager.prototype.Usage = function () {
|
|
|
606
606
|
return new self.libraries.Usage(self, ...arguments);
|
|
607
607
|
};
|
|
608
608
|
|
|
609
|
+
Manager.prototype.Middleware = function () {
|
|
610
|
+
const self = this;
|
|
611
|
+
self.libraries.Middleware = self.libraries.Middleware || require('./helpers/middleware.js');
|
|
612
|
+
return new self.libraries.Middleware(self, ...arguments);
|
|
613
|
+
};
|
|
614
|
+
|
|
609
615
|
Manager.prototype.Settings = function () {
|
|
610
616
|
const self = this;
|
|
611
617
|
self.libraries.Settings = self.libraries.Settings || require('./helpers/settings.js');
|