backend-manager 2.6.0 → 3.0.0

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
@@ -15,10 +15,62 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
17
  ---
18
+ ## [3.0.0] - 2023-09-05
19
+ ### ⚠️ BREAKING
20
+ - Updated `firebase-admin` from `9.12.0` --> `11.10.1`
21
+ - Updated `firebase-functions` from `3.24.1` --> `4.4.1`
22
+ - This project now requires `firebase-tools` from `10.9.2` --> `12.5.2`
23
+
24
+ - Updated required Node.js version from `12` --> `16`
25
+
26
+ - Updated `@google-cloud/storage` from `5.20.5` --> `7.0.1`
27
+ - Updated `fs-jetpack` from `4.3.1` --> `5.1.0`
28
+ - Updated `uuid` from `8.3.2` --> `9.0.0`
29
+
30
+ - Removed `backend-assistant` dependency and moved to custom library within this module at `./src/manager/helpers/assistant.js`
31
+ - Updated geolocation and client data retrieval to new format:
32
+ #### New Way
33
+ ```js
34
+ const assistant = new Assistant();
35
+
36
+ // Get geolocation data
37
+ assistant.request.geolocation.ip;
38
+ assistant.request.geolocation.continent;
39
+ assistant.request.geolocation.country;
40
+ assistant.request.geolocation.region;
41
+ assistant.request.geolocation.city;
42
+ assistant.request.geolocation.latitude;
43
+ assistant.request.geolocation.longitude;
44
+
45
+ // Get Client data
46
+ assistant.request.client.userAgent;
47
+ assistant.request.client.language;
48
+ assistant.request.client.platform;
49
+ assistant.request.client.mobile;
50
+ ```
51
+
52
+ #### Old Way
53
+ ```js
54
+ const assistant = new Assistant();
55
+
56
+ // Get geolocation data
57
+ assistant.request.ip;
58
+ assistant.request.continent;
59
+ assistant.request.country;
60
+ assistant.request.region;
61
+ assistant.request.city;
62
+ assistant.request.latitude;
63
+ assistant.request.longitude;
64
+
65
+ // Get Client data
66
+ assistant.request.userAgent;
67
+ assistant.request.language;
68
+ assistant.request.platform;
69
+ assistant.request.mobile;
70
+ ```
18
71
 
19
72
  ## [2.6.0] - 2023-09-05
20
73
  ### Added
21
74
  - Identity Platform auth/before-create.js
22
75
  - Identity Platform auth/before-signin.js
23
76
  - Disable these by passing `options.setupFunctionsIdentity: false`
24
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "2.6.0",
3
+ "version": "3.0.0",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -12,6 +12,9 @@
12
12
  "test": "./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
13
13
  "start": "node src/index.js"
14
14
  },
15
+ "engines": {
16
+ "node": "16"
17
+ },
15
18
  "repository": {
16
19
  "type": "git",
17
20
  "url": "git+https://github.com/itw-creative-works/backend-manager.git"
@@ -29,17 +32,16 @@
29
32
  "homepage": "https://itwcreativeworks.com",
30
33
  "dependencies": {
31
34
  "@firebase/rules-unit-testing": "^2.0.7",
32
- "@google-cloud/storage": "^5.20.5",
35
+ "@google-cloud/storage": "^7.0.1",
33
36
  "@sendgrid/mail": "^7.7.0",
34
37
  "@sentry/node": "^6.19.7",
35
- "backend-assistant": "^1.0.11",
36
38
  "busboy": "^1.6.0",
37
39
  "chalk": "^4.1.2",
38
40
  "cors": "^2.8.5",
39
41
  "dotenv": "^16.3.1",
40
- "firebase-admin": "^9.12.0",
41
- "firebase-functions": "^3.24.1",
42
- "fs-jetpack": "^4.3.1",
42
+ "firebase-admin": "^11.10.1",
43
+ "firebase-functions": "^4.4.1",
44
+ "fs-jetpack": "^5.1.0",
43
45
  "hcaptcha": "^0.1.1",
44
46
  "inquirer": "^8.2.5",
45
47
  "json5": "^2.2.3",
@@ -51,17 +53,18 @@
51
53
  "moment": "^2.29.4",
52
54
  "nanoid": "^3.3.6",
53
55
  "node-fetch": "^2.7.0",
54
- "node-powertools": "^1.0.10",
56
+ "node-powertools": "^1.1.0",
55
57
  "npm-api": "^1.0.1",
56
58
  "paypal-server-api": "^1.0.3",
57
59
  "pushid": "^1.0.0",
60
+ "resolve-account": "^1.0.2",
58
61
  "semver": "^7.5.4",
59
62
  "shortid": "^2.2.16",
60
- "sizeitup": "^1.0.3",
63
+ "sizeitup": "^1.0.7",
61
64
  "uid-generator": "^2.0.0",
62
65
  "ultimate-jekyll-poster": "^0.0.15",
63
66
  "universal-analytics": "^0.5.3",
64
- "uuid": "^8.3.2",
67
+ "uuid": "^9.0.1",
65
68
  "wonderful-fetch": "^1.0.0",
66
69
  "wonderful-log": "^1.0.5",
67
70
  "yargs": "^17.7.2"
package/src/cli/cli.js CHANGED
@@ -50,7 +50,6 @@ let NPM_CLEAN_SCRIPT = 'rm -fr node_modules && rm -fr package-lock.json && npm c
50
50
  let NOFIX_TEXT = chalk.red(`There is no automatic fix for this check.`);
51
51
  let runtimeconfigTemplate = JSON.parse((jetpack.read(path.resolve(`${__dirname}/../../templates/runtimeconfig.json`))) || '{}');
52
52
  let bemConfigTemplate = JSON.parse((jetpack.read(path.resolve(`${__dirname}/../../templates/backend-manager-config.json`))) || '{}');
53
- let CLI_CONFIG = JSON5.parse((jetpack.read(path.resolve(`${__dirname}/config.json`))) || '{}');
54
53
 
55
54
  function Main() {
56
55
  }
@@ -98,13 +97,10 @@ Main.prototype.process = async function (args) {
98
97
  if ((self.options.i || self.options.install) && (self.options.local || self.options.dev || self.options.development)) {
99
98
  await uninstallPkg('backend-manager');
100
99
  return await installPkg('file:../../../ITW-Creative-Works/backend-manager');
101
- // await uninstallPkg('backend-assistant');
102
- // return await installPkg('file:../../backend-assistant');
103
100
  }
104
101
  if ((self.options.i || self.options.install) && (self.options.live || self.options.prod || self.options.production)) {
105
102
  await uninstallPkg('backend-manager');
106
103
  return await installPkg('backend-manager');
107
- // return await installPkg('backend-assistant');
108
104
  }
109
105
  if (self.options.serve) {
110
106
  if (!self.options.quick && !self.options.q) {
@@ -261,7 +257,7 @@ Main.prototype.setup = async function () {
261
257
 
262
258
  self.bemApiURL = `https://us-central1-${_.get(self.firebaseRC, 'projects.default')}.cloudfunctions.net/bm_api?authenticationToken=${_.get(self.runtimeConfigJSON, 'backend_manager.key')}`;
263
259
  // const prepareStatsURL = `https://us-central1-${_.get(self.firebaseRC, 'projects.default')}.cloudfunctions.net/bm_api?authenticationToken=undefined`;
264
-
260
+
265
261
  // Log
266
262
  log(`ID: `, chalk.bold(`${self.projectName}`));
267
263
  log(`URL:`, chalk.bold(`${self.projectUrl}`));
@@ -273,27 +269,34 @@ Main.prototype.setup = async function () {
273
269
  // Tests
274
270
  await self.test('is a firebase project', async function () {
275
271
  let exists = jetpack.exists(`${self.firebaseProjectPath}/firebase.json`);
276
-
272
+
277
273
  return exists;
278
274
  }, fix_isFirebase);
279
275
 
280
- await self.test('.nvmrc file has proper version', async function () {
281
- // return !!self.package.dependencies && !!self.package.devDependencies;
282
- // let gitignore = jetpack.read(path.resolve(`${__dirname}/../../templates/gitignore.md`));
283
- let nvmrc = jetpack.read(`${self.firebaseProjectPath}/functions/.nvmrc`) || '';
284
-
285
- return nvmrc === `v${CLI_CONFIG.node}/*`
286
- }, fix_nvmrc);
276
+ await self.test(`using node ${self.packageJSON.engines.node}`, function () {
277
+ const engineReqMajor = parseInt(self.packageJSON.engines.node.split('.')[0]);
278
+ const engineHasMajor = parseInt(self.package.engines.node.split('.')[0]);
279
+ const processMajor = parseInt(process.versions.node.split('.')[0]);
280
+
281
+ if (processMajor < engineReqMajor) {
282
+ return new Error(`Please use at least version ${engineReqMajor} of Node.js with this project. You need to update your package.json and your .nvmrc file. Then, make sure to run ${chalk.bold(`nvm use ${engineReqMajor}`)}`)
283
+ }
287
284
 
288
- await self.test(`using node ${CLI_CONFIG.node}`, function () {
289
- let processMajor = parseInt(process.versions.node.split('.')[0]);
290
- let engineMajor = parseInt(self.package.engines.node.split('.')[0]);
291
- if (processMajor < engineMajor) {
292
- return new Error(`Please use Node.js version ${CLI_CONFIG.node} with this project. You can run: nvm use`)
285
+ if (engineHasMajor !== engineReqMajor) {
286
+ console.log(chalk.yellow(`You are using Node.js version ${processMajor} but this project suggests ${engineReqMajor}.`));
293
287
  }
294
- return self.package.engines.node.toString() === CLI_CONFIG.node && processMajor >= engineMajor;
288
+
289
+ return engineHasMajor >= engineReqMajor;
295
290
  }, fix_nodeVersion);
296
291
 
292
+ await self.test('.nvmrc file has proper version', async function () {
293
+ const engineReqMajor = parseInt(self.packageJSON.engines.node.split('.')[0]);
294
+ const nvmrc = parseInt((jetpack.read(`${self.firebaseProjectPath}/functions/.nvmrc`) || '0').trim().replace(/v|\/|\*/g, ''));
295
+
296
+ // return nvmrc === `v${self.packageJSON.engines.node}/*`
297
+ return nvmrc >= engineReqMajor;
298
+ }, fix_nvmrc);
299
+
297
300
  // await self.test('project level package.json exists', async function () {
298
301
  // return !!(self.projectPackage && self.projectPackage.version && self.projectPackage.name);
299
302
  // }, fix_projpackage);
@@ -351,20 +354,6 @@ Main.prototype.setup = async function () {
351
354
  return isLocal(mine) || !(semver.gt(latest, mine)) || majorVersionMismatch;
352
355
  }, fix_bem);
353
356
 
354
- (async function() {
355
- let pkg = 'backend-assistant';
356
- let latest = semver.clean(await getPkgVersion(pkg));
357
- let bemv = cleanPackageVersion(self.packageJSON.dependencies[pkg]);
358
- bemPackageVersionWarning(pkg, bemv, latest);
359
- }());
360
-
361
- // await self.test('using updated backend-assistant', async function () {
362
- // let pkg = 'backend-assistant';
363
- // let latest = semver.clean(await getPkgVersion(pkg));
364
- // let mine = (self.package.dependencies[pkg] || '0.0.0').replace('^', '').replace('~', '');
365
- // return isLocal(mine) || !(semver.gt(latest, mine));
366
- // }, fix_bea);
367
-
368
357
  // await self.test('using updated ultimate-jekyll-poster', async function () {
369
358
  // let pkg = 'ultimate-jekyll-poster';
370
359
  // let latest = semver.clean(await getPkgVersion(pkg));
@@ -756,31 +745,32 @@ async function fix_serviceAccount(self) {
756
745
 
757
746
  function fix_nodeVersion(self) {
758
747
  return new Promise(function(resolve, reject) {
759
- _.set(self.package, 'engines.node', CLI_CONFIG.node)
748
+ if (false) {
749
+ _.set(self.package, 'engines.node', self.packageJSON.engines.node)
750
+ jetpack.write(`${self.firebaseProjectPath}/functions/package.json`, JSON.stringify(self.package, null, 2) );
760
751
 
761
- jetpack.write(`${self.firebaseProjectPath}/functions/package.json`, JSON.stringify(self.package, null, 2) );
762
- resolve();
752
+ resolve();
753
+ }
754
+
755
+ throw new Error('Please manually fix your outdated Node.js version')
763
756
  });
764
757
  };
765
758
 
766
759
  function fix_nvmrc(self) {
767
760
  return new Promise(function(resolve, reject) {
761
+ var v = self.packageJSON.engines.node;
768
762
 
769
- jetpack.write(`${self.firebaseProjectPath}/functions/.nvmrc`, `v${CLI_CONFIG.node}/*`);
770
- resolve();
771
- });
772
- };
763
+ jetpack.write(`${self.firebaseProjectPath}/functions/.nvmrc`, `v${v}/*`);
773
764
 
774
- async function fix_isFirebase(self) {
775
- log(chalk.red(`self is not a firebase project. Please use ${chalk.bold('firebase-init')} to set up.`));
776
- throw '';
777
- return;
765
+ log(chalk.red(`Please run ${chalk.bold(`nmv use ${v}`)} to use the correct version of Node.js`));
766
+
767
+ throw '';
768
+ });
778
769
  };
779
770
 
780
771
  async function fix_isFirebase(self) {
781
- log(chalk.red(`self is not a firebase project. Please use ${chalk.bold('firebase-init')} to set up.`));
772
+ log(chalk.red(`This is not a firebase project. Please use ${chalk.bold('firebase-init')} to set up.`));
782
773
  throw '';
783
- return;
784
774
  };
785
775
 
786
776
  function fix_projpackage(self) {
@@ -832,9 +822,7 @@ async function fix_bem(self) {
832
822
 
833
823
  return;
834
824
  };
835
- // async function fix_bea(self) {
836
- // return await installPkg('backend-assistant')
837
- // };
825
+
838
826
  // async function fix_ujp(self) {
839
827
  // return await installPkg('ultimate-jekyll-poster')
840
828
  // };
@@ -920,7 +908,7 @@ function fix_indexesSync(self) {
920
908
  } else {
921
909
  return reject();
922
910
  }
923
- })
911
+ })
924
912
  });
925
913
  };
926
914
 
@@ -941,7 +929,7 @@ function fix_setStoragePolicy(self) {
941
929
  .catch(e => {
942
930
  console.error(chalk.red(`There is no automatic fix. Please run: \n${chalk.bold('firebase deploy && npx bm setup')}`));
943
931
  return reject();
944
- });
932
+ });
945
933
  });
946
934
  };
947
935
 
@@ -1151,7 +1139,7 @@ async function execute(command, cwd) {
1151
1139
  // } else {
1152
1140
  // resolve(stdout);
1153
1141
  // }
1154
- // });
1142
+ // });
1155
1143
 
1156
1144
  });
1157
1145
  }
@@ -1173,7 +1161,7 @@ async function execute(command, cwd) {
1173
1161
  // // } else {
1174
1162
  // // resolve(stdout);
1175
1163
  // // }
1176
- // // });
1164
+ // // });
1177
1165
 
1178
1166
  // });
1179
1167
  // }
@@ -1340,7 +1328,7 @@ async function cmd_setStorageLifecycle(self) {
1340
1328
  .replace(/{bucket}/ig, `us.artifacts.${self.projectName}.appspot.com`)
1341
1329
  const command2 = `gsutil lifecycle set {config} gs://{bucket}`
1342
1330
  .replace(/{config}/ig, path.resolve(`${__dirname}/../../templates/storage-lifecycle-config-30-days.json`))
1343
- .replace(/{bucket}/ig, `bm-backup-firestore-${self.projectName}`)
1331
+ .replace(/{bucket}/ig, `bm-backup-firestore-${self.projectName}`)
1344
1332
 
1345
1333
  exec(command, function (error, stdout, stderr) {
1346
1334
  if (error) {
@@ -33,7 +33,7 @@ Module.prototype.main = function () {
33
33
  return reject(assistant.errorManager(`Parameter {email} is required.`, {code: 400, sentry: false, send: false, log: false}).error)
34
34
  }
35
35
 
36
- let emailPayload
36
+ let emailPayload
37
37
  try {
38
38
  emailPayload = merge({}, DEFAULT, require(path.join(__dirname, 'emails', `${payload.data.payload.id}.js`))(payload.data.payload, Manager.config));
39
39
  } catch (e) {
@@ -41,7 +41,7 @@ Module.prototype.main = function () {
41
41
  }
42
42
 
43
43
  const storage = Manager.storage({temporary: true});
44
- const ipPath = ['api:general:send-email', 'ips', assistant.request.ip];
44
+ const ipPath = ['api:general:send-email', 'ips', assistant.request.geolocation.ip];
45
45
  const emailPath = ['api:general:send-email', 'emails', payload.data.payload.email];
46
46
 
47
47
  const ipData = storage.get(ipPath).value() || {};
@@ -53,7 +53,7 @@ Module.prototype.main = function () {
53
53
 
54
54
  emailData.count = (emailData.count || 0) + 1;
55
55
  emailData.firstRequestTime = emailData.firstRequestTime ? emailData.firstRequestTime : new Date().toISOString();
56
- emailData.lastRequestTime = new Date().toISOString();
56
+ emailData.lastRequestTime = new Date().toISOString();
57
57
 
58
58
  storage.set(ipPath, ipData).write();
59
59
  storage.set(emailPath, emailData).write();
@@ -63,7 +63,7 @@ Module.prototype.main = function () {
63
63
  if (ipData.count >= emailPayload.spamFilter.ip || emailData.count >= emailPayload.spamFilter.email) {
64
64
  self.assistant.errorManager(`Spam filter triggered ip=${ipData.count}, email=${emailData.count}`, {code: 429, sentry: false, send: false, log: true})
65
65
  return resolve({data: {success: true}});
66
- }
66
+ }
67
67
 
68
68
  assistant.log('Email payload:', emailPayload, {environment: 'production'});
69
69
 
@@ -94,7 +94,7 @@ Module.prototype.main = function () {
94
94
  })
95
95
  .catch(e => {
96
96
  return reject(assistant.errorManager(`Error sending email: ${e}`, {code: 500, sentry: true, send: false, log: false}).error)
97
- })
97
+ })
98
98
  });
99
99
 
100
100
  };
@@ -85,8 +85,8 @@ Module.prototype.main = function () {
85
85
  uuid: uuid,
86
86
  signInToken: signInToken,
87
87
  timestamp: new Date().toISOString(),
88
- ip: assistant.request.ip,
89
- country: assistant.request.country,
88
+ ip: assistant.request.geolocation.ip,
89
+ country: assistant.request.geolocation.country,
90
90
  app: result,
91
91
  config: config,
92
92
  }
@@ -107,22 +107,8 @@ Module.prototype.signUp = function (payload) {
107
107
  // Merge the payload and the default user object
108
108
  const user = {
109
109
  activity: {
110
- geolocation: {
111
- // Main geolocation
112
- ip: assistant.request.ip,
113
- continent: assistant.request.continent,
114
- region: assistant.request.region,
115
- country: assistant.request.country,
116
- city: assistant.request.city,
117
- latitude: assistant.request.latitude,
118
- longitude: assistant.request.longitude,
119
-
120
- // Get User Agent data
121
- userAgent: assistant.request.userAgent,
122
- language: assistant.request.language,
123
- platform: assistant.request.platform,
124
- mobile: assistant.request.mobile,
125
- },
110
+ geolocation: assistant.request.geolocation,
111
+ client: assistant.request.client,
126
112
  },
127
113
  affiliate: {
128
114
  referrer: payload.affiliate.referrer,
@@ -71,6 +71,8 @@ Module.prototype.main = function () {
71
71
  geolocation: {
72
72
  ip: ipAddress,
73
73
  language: context.locale,
74
+ },
75
+ client: {
74
76
  userAgent: context.userAgent,
75
77
  },
76
78
  },
@@ -39,6 +39,8 @@ Module.prototype.main = function () {
39
39
  geolocation: {
40
40
  ip: context.ipAddress,
41
41
  language: context.locale,
42
+ },
43
+ client: {
42
44
  userAgent: context.userAgent,
43
45
  },
44
46
  },
@@ -20,10 +20,10 @@ function Analytics(Manager, options) {
20
20
  // Set properties
21
21
  self._assistant = options.assistant || Manager.Assistant();
22
22
  self._request = {
23
- ip: get(self._assistant, 'request.ip', '127.0.0.1'),
24
- country: get(self._assistant, 'request.country', ''),
23
+ ip: get(self._assistant, 'request.geolocation.ip', '127.0.0.1'),
24
+ country: get(self._assistant, 'request.geolocation.country', ''),
25
25
  referrer: get(self._assistant, 'request.referrer', ''),
26
- userAgent: get(self._assistant, 'request.userAgent', ''),
26
+ userAgent: get(self._assistant, 'request.client.userAgent', ''),
27
27
  name: get(self._assistant, 'meta.name', ''),
28
28
  }
29
29
 
@@ -187,10 +187,10 @@ ApiManager.prototype.getUser = async function (assistant) {
187
187
  // console.log('---authenticatedUser', authenticatedUser);
188
188
  const planId = get(authenticatedUser, 'plan.id', 'basic');
189
189
  let workingUID = !authenticatedUser.authenticated
190
- ? uuidv5(assistant.request.ip, '1b671a64-40d5-491e-99b0-da01ff1f3341')
190
+ ? uuidv5(assistant.request.geolocation.ip, '1b671a64-40d5-491e-99b0-da01ff1f3341')
191
191
  : authenticatedUser.auth.uid
192
- authenticatedUser.ip = assistant.request.ip;
193
- authenticatedUser.country = assistant.request.country;
192
+ authenticatedUser.ip = assistant.request.geolocation.ip;
193
+ authenticatedUser.country = assistant.request.geolocation.country;
194
194
  // console.log('---workingUID', workingUID);
195
195
  // console.log('----self.userList', self.userList);
196
196
  let existingUser = self.userList.find(user => user.auth.uid === workingUID);