backend-manager 2.2.4 → 2.3.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "2.2.4",
3
+ "version": "2.3.2",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -27,17 +27,17 @@
27
27
  },
28
28
  "homepage": "https://itwcreativeworks.com",
29
29
  "dependencies": {
30
- "@firebase/rules-unit-testing": "^2.0.2",
30
+ "@firebase/rules-unit-testing": "^2.0.3",
31
31
  "@google-cloud/storage": "^5.20.5",
32
32
  "@sendgrid/mail": "^7.7.0",
33
33
  "@sentry/node": "^6.19.7",
34
- "backend-assistant": "^0.0.64",
34
+ "backend-assistant": "^0.0.66",
35
35
  "busboy": "^1.6.0",
36
36
  "chalk": "^4.1.2",
37
37
  "cors": "^2.8.5",
38
38
  "dotenv": "^16.0.1",
39
39
  "firebase-admin": "^9.12.0",
40
- "firebase-functions": "^3.21.2",
40
+ "firebase-functions": "^3.22.0",
41
41
  "fs-jetpack": "^4.3.1",
42
42
  "hcaptcha": "^0.1.1",
43
43
  "inquirer": "^8.2.4",
@@ -48,9 +48,9 @@
48
48
  "mocha": "^9.2.2",
49
49
  "moment": "^2.29.3",
50
50
  "node-fetch": "^2.6.7",
51
- "node-powertools": "^0.0.13",
51
+ "node-powertools": "^0.0.14",
52
52
  "npm-api": "^1.0.1",
53
- "paypal-server-api": "^0.0.6",
53
+ "paypal-server-api": "^0.0.7",
54
54
  "pushid": "^1.0.0",
55
55
  "semver": "^7.3.7",
56
56
  "shortid": "^2.2.16",
package/src/cli/cli.js CHANGED
@@ -43,9 +43,9 @@ const fetch = require('node-fetch');
43
43
 
44
44
  let bem_giRegex = 'Set in .setup()'
45
45
  let bem_giRegexOuter = /# BEM>>>(.*\n?)# <<<BEM/sg;
46
- let bem_fsRulesRegex = /(\/\/\/---backend-manager---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
47
- let bem_fsRulesDefaultRegex = /(\/\/\/---default-rules---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
48
- let bem_fsRulesBackupRegex = /({{\s*?backend-manager\s*?}})/sgm;
46
+ let bem_allRulesRegex = /(\/\/\/---backend-manager---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
47
+ let bem_allRulesDefaultRegex = /(\/\/\/---default-rules---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
48
+ let bem_allRulesBackupRegex = /({{\s*?backend-manager\s*?}})/sgm;
49
49
  let MOCHA_PKG_SCRIPT = 'mocha ../test/ --recursive --timeout=10000';
50
50
  let NPM_CLEAN_SCRIPT = 'rm -fr node_modules && rm -fr package-lock.json && npm cache clean --force && npm install && npm rb';
51
51
  let NOFIX_TEXT = chalk.red(`There is no automatic fix for this check.`);
@@ -139,7 +139,7 @@ Main.prototype.process = async function (args) {
139
139
 
140
140
  if (self.options['rules:default'] || self.options['rules:getdefault']) {
141
141
  self.getRulesFile();
142
- console.log(self.default.firestoreRulesWhole.match(bem_fsRulesDefaultRegex)[0].replace(' ///', '///'));
142
+ console.log(self.default.firestoreRulesWhole.match(bem_allRulesDefaultRegex)[0].replace(' ///', '///'));
143
143
  return;
144
144
  }
145
145
 
@@ -205,9 +205,11 @@ module.exports = Main;
205
205
 
206
206
  Main.prototype.getRulesFile = function () {
207
207
  const self = this;
208
- self.default.firestoreRulesWhole = (jetpack.read(path.resolve(`${__dirname}/../../templates/firestore.rules`))).replace('=0.0.0-', `-${self.default.version}-`);
209
- self.default.firestoreRulesCore = self.default.firestoreRulesWhole.match(bem_fsRulesRegex)[0];
208
+ self.default.firestoreRulesWhole = (jetpack.read(path.resolve(`${__dirname}/../../templates/firestore.rules`))).replace('=0.0.0-', `=${self.default.version}-`);
209
+ self.default.firestoreRulesCore = self.default.firestoreRulesWhole.match(bem_allRulesRegex)[0];
210
210
 
211
+ self.default.databaseRulesWhole = (jetpack.read(path.resolve(`${__dirname}/../../templates/database.rules.json`))).replace('=0.0.0-', `=${self.default.version}-`);
212
+ self.default.databaseRulesCore = self.default.databaseRulesWhole.match(bem_allRulesRegex)[0];
211
213
  };
212
214
 
213
215
  Main.prototype.setup = async function () {
@@ -243,7 +245,7 @@ Main.prototype.setup = async function () {
243
245
 
244
246
  self.getRulesFile();
245
247
 
246
- self.default.firestoreRulesVersionRegex = new RegExp(`///---version-${self.default.version}---///`)
248
+ self.default.rulesVersionRegex = new RegExp(`///---version-${self.default.version}---///`)
247
249
  // bem_giRegex = new RegExp(jetpack.read(path.resolve(`${__dirname}/../../templates/gitignore.md`)).replace(/\./g, '\\.'), 'm' )
248
250
  bem_giRegex = new RegExp(jetpack.read(path.resolve(`${__dirname}/../../templates/gitignore.md`)), 'm' )
249
251
 
@@ -478,22 +480,26 @@ Main.prototype.setup = async function () {
478
480
 
479
481
  // Update actual files
480
482
  await self.test('update firestore rules file', function () {
481
- let exists = jetpack.exists(`${self.firebaseProjectPath}/firestore.rules`);
482
- let contents = jetpack.read(`${self.firebaseProjectPath}/firestore.rules`) || '';
483
- let containsCore = contents.match(bem_fsRulesRegex);
484
- let matchesVersion = contents.match(self.default.firestoreRulesVersionRegex);
483
+ const exists = jetpack.exists(`${self.firebaseProjectPath}/firestore.rules`);
484
+ const contents = jetpack.read(`${self.firebaseProjectPath}/firestore.rules`) || '';
485
+ const containsCore = contents.match(bem_allRulesRegex);
486
+ const matchesVersion = contents.match(self.default.rulesVersionRegex);
485
487
 
486
488
  return (!!exists && !!containsCore && !!matchesVersion);
487
489
  }, fix_firestoreRulesFile);
488
490
 
489
491
  await self.test('update firestore indexes file', function () {
490
- let exists = jetpack.exists(`${self.firebaseProjectPath}/firestore.indexes.json`);
492
+ const exists = jetpack.exists(`${self.firebaseProjectPath}/firestore.indexes.json`);
491
493
  return (!!exists);
492
494
  }, fix_firestoreIndexesFile);
493
495
 
494
496
  await self.test('update realtime rules file', function () {
495
- let exists = jetpack.exists(`${self.firebaseProjectPath}/database.rules.json`);
496
- return (!!exists);
497
+ const exists = jetpack.exists(`${self.firebaseProjectPath}/database.rules.json`);
498
+ const contents = jetpack.read(`${self.firebaseProjectPath}/database.rules.json`) || '';
499
+ const containsCore = contents.match(bem_allRulesRegex);
500
+ const matchesVersion = contents.match(self.default.rulesVersionRegex);
501
+
502
+ return (!!exists && !!containsCore && !!matchesVersion);
497
503
  }, fix_realtimeRulesFile);
498
504
 
499
505
  await self.test('update storage rules file', function () {
@@ -874,61 +880,94 @@ function fix_remoteconfigTemplate(self) {
874
880
 
875
881
  function fix_firestoreRulesFile(self) {
876
882
  return new Promise(function(resolve, reject) {
877
- let path = `${self.firebaseProjectPath}/firestore.rules`;
883
+ const name = 'firestore.rules'
884
+ let path = `${self.firebaseProjectPath}/${name}`;
878
885
  let exists = jetpack.exists(path);
879
886
  let contents = jetpack.read(path) || '';
880
887
 
881
888
  if (!exists || !contents) {
882
- log(chalk.yellow(`Writing new firestore.rules file...`));
889
+ log(chalk.yellow(`Writing new ${name} file...`));
883
890
  jetpack.write(path, self.default.firestoreRulesWhole)
884
891
  contents = jetpack.read(path) || '';
885
892
  }
886
893
 
887
- let hasTemplate = contents.match(bem_fsRulesRegex) || contents.match(bem_fsRulesBackupRegex);
894
+ let hasTemplate = contents.match(bem_allRulesRegex) || contents.match(bem_allRulesBackupRegex);
888
895
 
889
896
  if (!hasTemplate) {
890
- log(chalk.red(`Could not find rules template. Please edit firestore.rules file and add`), chalk.red(`{{backend-manager}}`), chalk.red(`to it.`));
897
+ log(chalk.red(`Could not find rules template. Please edit ${name} file and add`), chalk.red(`{{backend-manager}}`), chalk.red(`to it.`));
891
898
  reject()
892
899
  }
893
900
 
894
- let matchesVersion = contents.match(self.default.firestoreRulesVersionRegex);
901
+ let matchesVersion = contents.match(self.default.rulesVersionRegex);
895
902
  if (!matchesVersion) {
896
903
  // console.log('replace wih', self.default.firestoreRulesCore);
897
- contents = contents.replace(bem_fsRulesBackupRegex, self.default.firestoreRulesCore)
898
- contents = contents.replace(bem_fsRulesRegex, self.default.firestoreRulesCore)
904
+ contents = contents.replace(bem_allRulesBackupRegex, self.default.firestoreRulesCore)
905
+ contents = contents.replace(bem_allRulesRegex, self.default.firestoreRulesCore)
899
906
  jetpack.write(path, contents)
900
- log(chalk.yellow(`Writing core rules to firestore.rules file...`));
907
+ log(chalk.yellow(`Writing core rules to ${name} file...`));
901
908
  }
902
909
  resolve();
903
910
  });
904
911
  };
905
912
 
906
- function fix_firestoreIndexesFile(self) {
907
- return new Promise(async function(resolve, reject) {
908
- const name = 'firestore.indexes.json';
909
- let filePath = `${self.firebaseProjectPath}/${name}`;
910
- let exists = jetpack.exists(filePath);
913
+ function fix_realtimeRulesFile(self) {
914
+ return new Promise(function(resolve, reject) {
915
+ const name = 'database.rules.json'
916
+ let path = `${self.firebaseProjectPath}/${name}`;
917
+ let exists = jetpack.exists(path);
918
+ let contents = jetpack.read(path) || '';
911
919
 
912
- if (!exists) {
920
+ if (!exists || !contents) {
913
921
  log(chalk.yellow(`Writing new ${name} file...`));
914
- await cmd_indexesGet(self, name, false);
922
+ jetpack.write(path, self.default.databaseRulesWhole)
923
+ contents = jetpack.read(path) || '';
924
+ }
925
+
926
+ let hasTemplate = contents.match(bem_allRulesRegex) || contents.match(bem_allRulesBackupRegex);
927
+
928
+ if (!hasTemplate) {
929
+ log(chalk.red(`Could not find rules template. Please edit ${name} file and add`), chalk.red(`{{backend-manager}}`), chalk.red(`to it.`));
930
+ reject()
915
931
  }
916
932
 
933
+ let matchesVersion = contents.match(self.default.rulesVersionRegex);
934
+ if (!matchesVersion) {
935
+ // console.log('replace wih', self.default.databaseRulesCore);
936
+ contents = contents.replace(bem_allRulesBackupRegex, self.default.databaseRulesCore)
937
+ contents = contents.replace(bem_allRulesRegex, self.default.databaseRulesCore)
938
+ jetpack.write(path, contents)
939
+ log(chalk.yellow(`Writing core rules to ${name} file...`));
940
+ }
917
941
  resolve();
918
942
  });
919
943
  };
920
944
 
921
- function fix_realtimeRulesFile(self) {
922
- return new Promise(function(resolve, reject) {
923
- const name = 'database.rules.json';
945
+ // function fix_realtimeRulesFile(self) {
946
+ // return new Promise(function(resolve, reject) {
947
+ // const name = 'database.rules.json';
948
+ // let filePath = `${self.firebaseProjectPath}/${name}`;
949
+ // let exists = jetpack.exists(filePath);
950
+ // let contents = jetpack.read(filePath) || '';
951
+ //
952
+ // if (!exists) {
953
+ // log(chalk.yellow(`Writing new ${name} file...`));
954
+ // jetpack.write(filePath, jetpack.read(path.resolve(`${__dirname}/../../templates/${name}`)))
955
+ // contents = jetpack.read(filePath) || '';
956
+ // }
957
+ //
958
+ // resolve();
959
+ // });
960
+ // };
961
+
962
+ function fix_firestoreIndexesFile(self) {
963
+ return new Promise(async function(resolve, reject) {
964
+ const name = 'firestore.indexes.json';
924
965
  let filePath = `${self.firebaseProjectPath}/${name}`;
925
966
  let exists = jetpack.exists(filePath);
926
- let contents = jetpack.read(filePath) || '';
927
967
 
928
968
  if (!exists) {
929
969
  log(chalk.yellow(`Writing new ${name} file...`));
930
- jetpack.write(filePath, jetpack.read(path.resolve(`${__dirname}/../../templates/${name}`)))
931
- contents = jetpack.read(filePath) || '';
970
+ await cmd_indexesGet(self, name, false);
932
971
  }
933
972
 
934
973
  resolve();
@@ -1,3 +1,5 @@
1
+ let _;
2
+
1
3
  function Module() {
2
4
 
3
5
  }
@@ -11,6 +13,8 @@ Module.prototype.main = function () {
11
13
 
12
14
  return new Promise(async function(resolve, reject) {
13
15
 
16
+ _ = Manager.require('lodash')
17
+
14
18
  if (!payload.user.roles.admin) {
15
19
  return reject(assistant.errorManager(`Admin required.`, {code: 401, sentry: false, send: false, log: false}).error)
16
20
  } else {
@@ -21,15 +25,11 @@ Module.prototype.main = function () {
21
25
  let data = doc.data() || {};
22
26
  let error = null;
23
27
 
24
- await self.fixStats(data)
28
+ await self.updateStats(data)
25
29
  .catch(e => {
26
30
  error = e;
27
31
  })
28
32
 
29
- await self.updateStats()
30
- .catch(e => {
31
- error = e;
32
- })
33
33
 
34
34
  if (error) {
35
35
  return reject(assistant.errorManager(error, {code: 500, sentry: false, send: false, log: false}).error)
@@ -64,63 +64,64 @@ Module.prototype.fixStats = function (data) {
64
64
  return new Promise(async function(resolve, reject) {
65
65
  const stats = self.libraries.admin.firestore().doc(`meta/stats`);
66
66
 
67
- if (!data || !data.users || !data.users.total || !data.subscriptions || !data.subscriptions.total) {
68
- let usersTotal = 0;
69
- let subscriptionsTotal = 0;
70
- let error = null;
67
+
68
+
69
+ return resolve();
70
+ });
71
+ }
72
+
73
+ Module.prototype.updateStats = function (existingData) {
74
+ const self = this;
75
+
76
+ return new Promise(async function(resolve, reject) {
77
+ const stats = self.libraries.admin.firestore().doc(`meta/stats`);
78
+ const online = self.libraries.admin.database().ref(`gatherings/online`);
79
+
80
+ let error = null;
81
+ let update = {};
82
+
83
+ // Fix broken stats
84
+ if (!_.get(existingData, 'users.total', null)) {
71
85
  await self.getAllUsers()
72
86
  .then(r => {
73
- usersTotal = r.length
87
+ _.set(update, 'users.total', r.length)
74
88
  })
75
89
  .catch(e => {
76
90
  error = new Error(`Failed fixing stats: ${e}`);
77
- self.assistant.error(error, {environment: 'production'});
78
- })
79
- await self.getAllSubscriptions()
80
- .then(r => {
81
- subscriptionsTotal = r
82
- })
83
- .catch(e => {
84
- error = new Error(`Failed getting subscriptions: ${e}`);
85
- self.assistant.error(error, {environment: 'production'});
86
91
  })
92
+ }
87
93
 
88
- if (error) {
89
- return reject(error);
90
- }
91
- await stats
92
- .set({
93
- users: {
94
- total: usersTotal,
95
- },
96
- subscriptions: {
97
- total: subscriptionsTotal,
98
- },
99
- }, { merge: true })
100
- .catch(function (e) {
101
- return reject(e);
102
- })
94
+ if (error) {
95
+ return reject(error);
103
96
  }
104
97
 
105
- return resolve(data);
106
- });
107
- }
98
+ // Fetch new stats
99
+ await self.getAllNotifications()
100
+ .then(r => {
101
+ _.set(update, 'notifications.total', r)
102
+ })
103
+ .catch(e => {
104
+ error = new Error(`Failed getting notifications: ${e}`);
105
+ })
108
106
 
109
- Module.prototype.updateStats = function () {
110
- const self = this;
107
+ await self.getAllSubscriptions()
108
+ .then(r => {
109
+ _.set(update, 'subscriptions.total', r)
110
+ })
111
+ .catch(e => {
112
+ error = new Error(`Failed getting subscriptions: ${e}`);
113
+ })
111
114
 
112
- return new Promise(async function(resolve, reject) {
113
- const stats = self.libraries.admin.firestore().doc(`meta/stats`);
114
- let online = self.libraries.admin.database().ref(`gatherings/online`);
115
- let onlineCount = 0;
116
- let error = null;
115
+ if (error) {
116
+ return reject(error);
117
+ }
117
118
 
118
119
  await online
119
120
  .once('value')
120
121
  .then((snap) => {
121
122
  let data = snap.val() || {};
122
123
  let keys = Object.keys(data);
123
- onlineCount = keys.length;
124
+ _.set(update, 'users.online', keys.length)
124
125
  })
125
126
  .catch(e => {
126
127
  error = new Error(`Failed getting online users: ${e}`);
@@ -131,13 +132,9 @@ Module.prototype.updateStats = function () {
131
132
  }
132
133
 
133
134
  await stats
134
- .set({
135
- users: {
136
- online: onlineCount
137
- }
138
- }, { merge: true })
135
+ .set(update, { merge: true })
139
136
  .catch(function (e) {
140
- return reject(`Failed getting stats: ${e}`);
137
+ return reject(new Error(`Failed getting stats: ${e}`));
141
138
  })
142
139
 
143
140
  return resolve();
@@ -156,7 +153,7 @@ Module.prototype.getAllUsers = function () {
156
153
  });
157
154
  }
158
155
 
159
- Module.prototype.getAllSubscriptions = function () {
156
+ Module.prototype.getAllNotifications = function () {
160
157
  const self = this;
161
158
  return new Promise(async function(resolve, reject) {
162
159
  await self.libraries.admin.firestore().collection('notifications/subscriptions/all')
@@ -170,6 +167,33 @@ Module.prototype.getAllSubscriptions = function () {
170
167
  });
171
168
  }
172
169
 
170
+ Module.prototype.getAllSubscriptions = function () {
171
+ const self = this;
172
+ return new Promise(async function(resolve, reject) {
173
+ await self.libraries.admin.firestore().collection('users')
174
+ .where('plan.expires.timestampUNIX', '>=', new Date().getTime() / 1000)
175
+ .get()
176
+ .then(function(snapshot) {
177
+ let count = 0;
178
+
179
+ snapshot
180
+ .forEach((doc, i) => {
181
+ const data = doc.data();
182
+ const planId = _.get(data, 'plan.id', 'basic');
183
+ if (!['', 'basic', 'free'].includes(planId)) {
184
+ count++;
185
+ }
186
+ });
187
+
188
+ return resolve(count);
189
+ })
190
+ .catch(function(e) {
191
+ return reject(e)
192
+ });
193
+
194
+ });
195
+ }
196
+
173
197
  function getUsersBatch(self, nextPageToken) {
174
198
  return new Promise(async function(resolve, reject) {
175
199
  self.libraries.admin.auth().listUsers(1000, nextPageToken)
@@ -29,7 +29,7 @@ Module.prototype.main = function () {
29
29
  }
30
30
  }
31
31
 
32
- const postUrl = `blog/${payload.data.payload.url}`;
32
+ const postUrl = `${Manager.config.brand.url}/blog/${payload.data.payload.url}`;
33
33
 
34
34
  if (payload.data.payload.invoiceEmail && payload.data.payload.invoicePrice) {
35
35
  // Create invoice
@@ -1,31 +1,46 @@
1
1
  {
2
2
  "rules": {
3
+ ///---backend-manager---///
4
+ ///---version=0.0.0---///
5
+
6
+ // Gathering rules
3
7
  "gatherings": {
4
8
  ".read": false,
5
9
  ".write": false,
6
- "online": {
7
- ".read": "auth.uid != null && query.equalTo == auth.uid",
10
+ "$room": {
11
+ ".read": "
12
+ (auth.uid != null && query.equalTo == auth.uid)
13
+ ",
8
14
  ".write": false,
9
15
  ".indexOn": ["uid"],
10
- "$uid": {
16
+ "$id": {
11
17
  ".read": "
12
- // Allowed if user is signed in AND is the owner of the doc
18
+ // Allowed if user is authenticated AND is the owner of the doc
13
19
  (auth != null && auth.uid == data.child('uid').val())
20
+ // Allowed if uid is equal to the doc id [LEGACY FOR SOMIIBO]
21
+ || (auth != null && auth.uid == $id)
22
+ // Allowed if user is not authenticated AND is the doc has no owner
23
+ || (auth == null && (data.child('uid').val() == ''))
14
24
  ",
15
25
  ".write": "
16
- // Allowed if the user is signed in AND is the owner of the existing doc
26
+ // Allowed if the user is authenticated AND is the owner of the existing doc
17
27
  (auth != null && auth.uid == data.child('uid').val())
18
- // Allowed if the user is signed in AND is the owner of the new doc
28
+ // Allowed if the user is authenticated AND is the owner of the new doc
19
29
  || (auth != null && auth.uid == newData.child('uid').val())
20
- // Allowed if it's a delete
21
- || (!newData.exists())
22
- // Allowed if the existing doc has no owner
30
+ // Allowed if the user is authenticated AND is the owner of the existing doc
31
+ || (auth != null && auth.uid == data.child('uid').val())
32
+ // Allowed if uid is equal to the doc id [LEGACY FOR SOMIIBO]
33
+ || (auth != null && auth.uid == $id)
34
+ // Allowed if the existing doc has no owner
23
35
  || (data.child('uid').val() == '')
24
36
  // Allowed if the new doc has no owner
25
37
  || (newData.child('uid').val() == '')
38
+ // Allowed if it's a delete
39
+ || (!newData.exists())
26
40
  ",
27
41
  }
28
42
  }
29
- }
43
+ },
44
+ ///---------end---------///
30
45
  }
31
46
  }