backend-manager 4.0.32 → 4.1.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.
package/CHANGELOG.md
CHANGED
|
@@ -14,7 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
---#
|
|
18
|
+
# [4.1.0] - 2024-12-19
|
|
19
|
+
### Changed
|
|
20
|
+
- Attach `schema` to `bm-properties` response header.
|
|
21
|
+
- `assistant.request.url` is now properly set for all environments (development, production, etc) and works whether called from custom domain or Firebase default function domain.
|
|
22
|
+
|
|
18
23
|
## [4.0.0] - 2024-05-08
|
|
19
24
|
### ⚠️ BREAKING
|
|
20
25
|
- Require Node.js version `18` or higher.
|
package/package.json
CHANGED
|
@@ -113,6 +113,7 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
113
113
|
self.analytics = null;
|
|
114
114
|
self.usage = null;
|
|
115
115
|
self.settings = null;
|
|
116
|
+
self.schema = null;
|
|
116
117
|
|
|
117
118
|
// Set meta
|
|
118
119
|
self.meta = {};
|
|
@@ -251,12 +252,6 @@ BackendAssistant.prototype.init = function (ref, options) {
|
|
|
251
252
|
self.constant.pastTime.timestamp = '1999-01-01T00:00:00Z';
|
|
252
253
|
self.constant.pastTime.timestampUNIX = 915148800;
|
|
253
254
|
|
|
254
|
-
// Schema
|
|
255
|
-
// self.schema = {
|
|
256
|
-
// dir: '',
|
|
257
|
-
// name: '',
|
|
258
|
-
// }
|
|
259
|
-
|
|
260
255
|
// Log options
|
|
261
256
|
if (
|
|
262
257
|
(self.isDevelopment())
|
|
@@ -568,9 +563,10 @@ function _attachHeaderProperties(self, options, error) {
|
|
|
568
563
|
code: options.code,
|
|
569
564
|
tag: self.tag,
|
|
570
565
|
usage: {
|
|
571
|
-
current: self
|
|
572
|
-
limits: self
|
|
566
|
+
current: self.usage ? self.usage.getUsage() : {},
|
|
567
|
+
limits: self.usage ? self.usage.getLimit() : {},
|
|
573
568
|
},
|
|
569
|
+
schema: self.schema || {},
|
|
574
570
|
additional: options.additional || {},
|
|
575
571
|
}
|
|
576
572
|
const req = self.ref.req;
|
|
@@ -582,9 +578,10 @@ function _attachHeaderProperties(self, options, error) {
|
|
|
582
578
|
|
|
583
579
|
// Add bm-properties to Access-Control-Expose-Headers
|
|
584
580
|
const existingExposed = res.get('Access-Control-Expose-Headers') || '';
|
|
585
|
-
const newExposed = `${existingExposed}, bm-properties`.replace(/^, /, '');
|
|
586
581
|
|
|
582
|
+
// If it does not exist, add it
|
|
587
583
|
if (!existingExposed.match(/bm-properties/i)) {
|
|
584
|
+
const newExposed = `${existingExposed}, bm-properties`.replace(/^, /, '');
|
|
588
585
|
res.header('Access-Control-Expose-Headers', newExposed);
|
|
589
586
|
}
|
|
590
587
|
}
|
|
@@ -722,15 +719,21 @@ BackendAssistant.prototype.resolveAccount = function (user) {
|
|
|
722
719
|
|
|
723
720
|
BackendAssistant.prototype.parseRepo = function (repo) {
|
|
724
721
|
let repoSplit = repo.split('/');
|
|
722
|
+
|
|
723
|
+
// Remove .git from the end
|
|
725
724
|
for (var i = 0; i < repoSplit.length; i++) {
|
|
726
725
|
repoSplit[i] = repoSplit[i].replace('.git', '');
|
|
727
726
|
}
|
|
727
|
+
|
|
728
|
+
// Remove unnecessary parts
|
|
728
729
|
repoSplit = repoSplit.filter(function(value, index, arr){
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
730
|
+
return value !== 'http:'
|
|
731
|
+
&& value !== 'https:'
|
|
732
|
+
&& value !== ''
|
|
733
|
+
&& value !== 'github.com';
|
|
733
734
|
});
|
|
735
|
+
|
|
736
|
+
// Return
|
|
734
737
|
return {
|
|
735
738
|
user: repoSplit[0],
|
|
736
739
|
name: repoSplit[1],
|
|
@@ -28,6 +28,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
28
28
|
const assistant = Manager.Assistant({req: req, res: res});
|
|
29
29
|
|
|
30
30
|
const data = assistant.request.data;
|
|
31
|
+
const headers = assistant.request.headers;
|
|
31
32
|
const geolocation = assistant.request.geolocation;
|
|
32
33
|
const client = assistant.request.client;
|
|
33
34
|
|
|
@@ -47,6 +48,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
47
48
|
|
|
48
49
|
// Log
|
|
49
50
|
assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city})`, JSON.stringify(data));
|
|
51
|
+
assistant.log(`Middleware.process(): Headers`, JSON.stringify(headers));
|
|
50
52
|
|
|
51
53
|
// Set paths
|
|
52
54
|
const routesDir = path.resolve(options.routesDir, libPath.replace('.js', ''));
|
|
@@ -118,7 +120,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
// Log
|
|
121
|
-
assistant.log(`Middleware.process(): Resolved settings with schema
|
|
123
|
+
assistant.log(`Middleware.process(): Resolved settings with schema=${options.schema}`, JSON.stringify(assistant.settings));
|
|
122
124
|
} else {
|
|
123
125
|
assistant.settings = data;
|
|
124
126
|
}
|
|
@@ -47,13 +47,16 @@ Settings.prototype.resolve = function (assistant, schema, settings, options) {
|
|
|
47
47
|
schema = loadSchema(assistant, schemaPath, settings, options);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// Resolve settings
|
|
51
|
-
self.settings = powertools.defaults(settings, schema);
|
|
52
|
-
|
|
53
50
|
// If schema is not an object, throw an error
|
|
54
51
|
if (!schema || typeof schema !== 'object') {
|
|
55
52
|
throw assistant.errorify(`Invalid schema provided`, {code: 400});
|
|
56
53
|
}
|
|
54
|
+
|
|
55
|
+
// Resolve settings
|
|
56
|
+
self.settings = powertools.defaults(settings, schema);
|
|
57
|
+
// self.schema = _.merge({}, schema);
|
|
58
|
+
const resolvedSchema = {};
|
|
59
|
+
|
|
57
60
|
// console.log('---schema', schema);
|
|
58
61
|
// console.log('---options', options);
|
|
59
62
|
// console.log('---self.settings', self.settings);
|
|
@@ -99,8 +102,27 @@ Settings.prototype.resolve = function (assistant, schema, settings, options) {
|
|
|
99
102
|
assistant.warn(`Replacing ${path}: originalValue=${originalValue}, resolvedValue=${resolvedValue}, replaceValue=${replaceValue}`);
|
|
100
103
|
_.set(self.settings, path, replaceValue);
|
|
101
104
|
}
|
|
105
|
+
|
|
106
|
+
// Set defaults
|
|
107
|
+
// @@@TODO: FINISH THIS
|
|
108
|
+
// !!! NOT SURE WHAT TO DO FOR DEFAULT SINCE IT CAN BE A FN SOMETIMES ???
|
|
109
|
+
const resolvedNode = {
|
|
110
|
+
types: schemaNode.types || [],
|
|
111
|
+
// value: typeof replaceValue === 'undefined' ? undefined : replaceValue,
|
|
112
|
+
// default: ???,
|
|
113
|
+
required: isRequired,
|
|
114
|
+
available: typeof schemaNode.available === 'undefined' ? true : schemaNode.available,
|
|
115
|
+
min: typeof schemaNode.min === 'undefined' ? undefined : schemaNode.min,
|
|
116
|
+
max: typeof schemaNode.max === 'undefined' ? undefined : schemaNode.max,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Update schema
|
|
120
|
+
_.set(resolvedSchema, path, resolvedNode);
|
|
102
121
|
});
|
|
103
122
|
|
|
123
|
+
// Set schema
|
|
124
|
+
self.schema = resolvedSchema;
|
|
125
|
+
|
|
104
126
|
// Resolve
|
|
105
127
|
return self.settings;
|
|
106
128
|
};
|
|
@@ -106,26 +106,23 @@ Usage.prototype.init = function (assistant, options) {
|
|
|
106
106
|
self.log(`Usage.init(): Checking if usage data needs to be fetched (${diff} hours)...`);
|
|
107
107
|
|
|
108
108
|
// Get app data to get plan limits using cached data if possible
|
|
109
|
+
assistant.log('*** init() 1 diff', diff);
|
|
110
|
+
assistant.log('*** init() 2 self.app', self.app);
|
|
109
111
|
if (diff > 1 || options.refetch) {
|
|
110
|
-
await
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
self.storage.set(`${self.paths.app}.data`, json).write();
|
|
120
|
-
self.storage.set(`${self.paths.app}.lastFetched`, new Date().toISOString()).write();
|
|
121
|
-
})
|
|
122
|
-
.catch(e => {
|
|
123
|
-
assistant.errorify(`Usage.init(): Error fetching app data: ${e}`, {code: 500, sentry: true});
|
|
124
|
-
})
|
|
112
|
+
await self.getApp(options.app)
|
|
113
|
+
.then((json) => {
|
|
114
|
+
// Write data and last fetched to storage
|
|
115
|
+
self.storage.set(`${self.paths.app}.data`, json).write();
|
|
116
|
+
self.storage.set(`${self.paths.app}.lastFetched`, new Date().toISOString()).write();
|
|
117
|
+
})
|
|
118
|
+
.catch(e => {
|
|
119
|
+
assistant.errorify(`Usage.init(): Error fetching app data: ${e}`, {code: 500, sentry: true});
|
|
120
|
+
});
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
// Get app data
|
|
128
124
|
self.app = self.storage.get(`${self.paths.app}.data`, {}).value();
|
|
125
|
+
assistant.log('*** init() 3 self.app', self.app);
|
|
129
126
|
|
|
130
127
|
// Check for app data
|
|
131
128
|
if (!self.app) {
|
|
@@ -311,14 +308,17 @@ Usage.prototype.getLimit = function (name) {
|
|
|
311
308
|
Usage.prototype.update = function () {
|
|
312
309
|
const self = this;
|
|
313
310
|
|
|
311
|
+
// Shortcuts
|
|
312
|
+
const Manager = self.Manager;
|
|
313
|
+
const assistant = self.assistant;
|
|
314
|
+
|
|
314
315
|
return new Promise(async function(resolve, reject) {
|
|
315
|
-
const
|
|
316
|
-
const assistant = self.assistant;
|
|
316
|
+
const { admin } = Manager.libraries;
|
|
317
317
|
|
|
318
318
|
// Write self.user to firestore or local if no user or if key is set
|
|
319
319
|
if (self.useUnauthenticatedStorage) {
|
|
320
320
|
if (self.options.unauthenticatedMode === 'firestore') {
|
|
321
|
-
|
|
321
|
+
admin.firestore().doc(`temporary/${self.key}`)
|
|
322
322
|
.set({
|
|
323
323
|
usage: self.user.usage,
|
|
324
324
|
}, {merge: true})
|
|
@@ -338,7 +338,7 @@ Usage.prototype.update = function () {
|
|
|
338
338
|
return resolve(self.user.usage);
|
|
339
339
|
}
|
|
340
340
|
} else {
|
|
341
|
-
|
|
341
|
+
admin.firestore().doc(`users/${self.user.auth.uid}`)
|
|
342
342
|
.set({
|
|
343
343
|
usage: self.user.usage,
|
|
344
344
|
}, {merge: true})
|
|
@@ -378,4 +378,53 @@ Usage.prototype.log = function () {
|
|
|
378
378
|
}
|
|
379
379
|
};
|
|
380
380
|
|
|
381
|
+
Usage.prototype.getApp = function (id) {
|
|
382
|
+
const self = this;
|
|
383
|
+
|
|
384
|
+
// Shortcuts
|
|
385
|
+
const Manager = self.Manager;
|
|
386
|
+
const assistant = self.assistant;
|
|
387
|
+
|
|
388
|
+
return new Promise(function(resolve, reject) {
|
|
389
|
+
const { admin } = Manager.libraries;
|
|
390
|
+
|
|
391
|
+
assistant.log('***getApp() 1 Manager.config.app.id', Manager.config.app.id);
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
// If we're on ITW, we can read directly from Firestore
|
|
395
|
+
// If we don't do this, calling getApp on ITW will call getApp on ITW again and again
|
|
396
|
+
if (Manager.config.app.id === 'itw-creative-works') {
|
|
397
|
+
assistant.log('***getApp() 2');
|
|
398
|
+
admin.firestore().doc(`apps/${id}`)
|
|
399
|
+
.get()
|
|
400
|
+
.then((r) => {
|
|
401
|
+
const data = r.data();
|
|
402
|
+
|
|
403
|
+
// Check for data
|
|
404
|
+
if (!data) {
|
|
405
|
+
return reject(new Error('No data found'));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Resolve
|
|
409
|
+
return resolve(data);
|
|
410
|
+
})
|
|
411
|
+
.catch((e) => reject(e));
|
|
412
|
+
} else {
|
|
413
|
+
assistant.log('***getApp() 3');
|
|
414
|
+
fetch('https://us-central1-itw-creative-works.cloudfunctions.net/getApp', {
|
|
415
|
+
method: 'post',
|
|
416
|
+
response: 'json',
|
|
417
|
+
body: {
|
|
418
|
+
id: id,
|
|
419
|
+
},
|
|
420
|
+
})
|
|
421
|
+
.then((json) => resolve(json))
|
|
422
|
+
.catch((e) => reject(e));
|
|
423
|
+
}
|
|
424
|
+
} catch (e) {
|
|
425
|
+
return reject(e);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
|
|
381
430
|
module.exports = Usage;
|