backend-manager 2.0.31 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "2.0.31",
3
+ "version": "2.1.0",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -35,9 +35,9 @@
35
35
  "busboy": "^1.6.0",
36
36
  "chalk": "^4.1.2",
37
37
  "cors": "^2.8.5",
38
- "dotenv": "^16.0.0",
38
+ "dotenv": "^16.0.1",
39
39
  "firebase-admin": "^9.12.0",
40
- "firebase-functions": "^3.21.0",
40
+ "firebase-functions": "^3.21.1",
41
41
  "fs-jetpack": "^4.3.1",
42
42
  "hcaptcha": "^0.1.1",
43
43
  "inquirer": "^8.2.4",
@@ -54,11 +54,11 @@
54
54
  "semver": "^7.3.7",
55
55
  "shortid": "^2.2.16",
56
56
  "uid-generator": "^2.0.0",
57
- "ultimate-jekyll-poster": "^0.0.10",
57
+ "ultimate-jekyll-poster": "^0.0.11",
58
58
  "universal-analytics": "^0.5.3",
59
59
  "uuid": "^8.3.2",
60
- "wonderful-fetch": "^0.0.4",
61
- "yargs": "^17.4.1"
60
+ "wonderful-fetch": "^0.0.5",
61
+ "yargs": "^17.5.0"
62
62
  },
63
63
  "files": [
64
64
  "bin/",
package/src/cli/cli.js CHANGED
@@ -582,6 +582,17 @@ Main.prototype.setup = async function () {
582
582
  console.log(chalk.yellow(`You should continue to run ${chalk.bold('npx bm setup')} until you pass all tests and fix all errors.`));
583
583
  }
584
584
 
585
+ // Notify parent that finished with test results
586
+ if (process.send) {
587
+ process.send({
588
+ sender: 'electron-manager',
589
+ command: 'setup:complete',
590
+ payload: {
591
+ passed: self.testCount === self.testTotal,
592
+ }
593
+ });
594
+ }
595
+
585
596
  return;
586
597
 
587
598
  };
@@ -15,8 +15,32 @@ Module.prototype.main = function () {
15
15
 
16
16
  return new Promise(async function(resolve, reject) {
17
17
 
18
- // console.log('----self.Manager.libraries', self.Manager.libraries);
19
- // console.log('----self.Manager.libraries.sentry 1', self.Manager.libraries.sentry);
18
+ // Set up response obj
19
+ payload.response.data = {
20
+ subscribers: 0,
21
+ batches: 0,
22
+ sent: 0,
23
+ deleted: 0,
24
+ }
25
+
26
+ // Fix notification payload
27
+ self._notificationPayload = {
28
+ notification: self.payload.data.payload.notification || {},
29
+ };
30
+ self._notificationPayload.notification.title = self.payload.data.payload.title || self.payload.data.payload.notification.title || 'Notification';
31
+ self._notificationPayload.notification.click_action = self.payload.data.payload.click_action || self.payload.data.payload.notification.click_action || 'https://itwcreativeworks.com';
32
+ self._notificationPayload.notification.body = self.payload.data.payload.body || self.payload.data.payload.notification.body || 'Check this out';
33
+ self._notificationPayload.notification.icon = self.payload.data.payload.icon || self.payload.data.payload.notification.icon || self.Manager.config.brand.brandmark || 'https://cdn.itwcreativeworks.com/assets/itw-creative-works/images/socials/itw-creative-works-brandmark-black-1024x1024.png';
34
+
35
+ try {
36
+ self._notificationPayload.notification.click_action = new URL(self._notificationPayload.notification.click_action);
37
+ self._notificationPayload.notification.click_action.searchParams.set('cb', new Date().getTime())
38
+ self._notificationPayload.notification.click_action = self._notificationPayload.notification.click_action.toString()
39
+ } catch (e) {
40
+ assistant.errorManager(`Failed to add cb to URL: ${e}`, {code: 500, sentry: false, send: false, log: true})
41
+ }
42
+
43
+ assistant.log('Resolved notification payload', self._notificationPayload, {environment: 'production'})
20
44
 
21
45
  if (!payload.user.roles.admin) {
22
46
  return reject(assistant.errorManager(`Admin required.`, {code: 401, sentry: false, send: false, log: false}).error)
@@ -28,7 +52,7 @@ Module.prototype.main = function () {
28
52
 
29
53
  await self.getTokens({tags: false})
30
54
  .then(r => {
31
- return resolve({data: r})
55
+ return resolve({data: payload.response.data})
32
56
  })
33
57
  .catch(e => {
34
58
  return reject(assistant.errorManager(`Failed to send notification: ${e}`, {code: 400, sentry: true, send: false, log: false}).error)
@@ -54,6 +78,7 @@ Module.prototype.getTokens = function (options) {
54
78
  .get()
55
79
  .then(function(querySnapshot) {
56
80
  self.assistant.log(`Queried ${querySnapshot.size} tokens.`);
81
+ self.payload.response.data.subscribers = querySnapshot.size;
57
82
  // self.result.subscriptionsStart = querySnapshot.size;
58
83
  let batchCurrentSize = 0;
59
84
  let batchSizeMax = 1000;
@@ -74,6 +99,7 @@ Module.prototype.getTokens = function (options) {
74
99
  batchPromises.push(self.sendBatch(batchCurrent, batchId));
75
100
  batchCurrent = [];
76
101
  batchCurrentSize = 0;
102
+ self.payload.response.data.batches++;
77
103
  }
78
104
  batchLoops++;
79
105
  });
@@ -103,20 +129,7 @@ Module.prototype.sendBatch = function (batch, id) {
103
129
 
104
130
  // self.assistant.log('payload', payload);
105
131
 
106
- let payload = {};
107
- payload.notification = {};
108
- payload.notification.title = self.payload.data.payload.title;
109
- payload.notification.clickAction = self.payload.data.payload.click_action || self.payload.data.payload.clickAction;
110
- payload.notification.click_action = self.payload.data.payload.click_action || self.payload.data.payload.clickAction;
111
- payload.notification.body = self.payload.data.payload.body;
112
- payload.notification.icon = self.payload.data.payload.icon || self.Manager.config.brand.brandmark;
113
-
114
- // payload.data = {};
115
- // payload.data.clickAction = self.payload.data.payload.click_action || self.payload.data.payload.clickAction;
116
- // payload.data.click_action = self.payload.data.payload.click_action || self.payload.data.payload.clickAction;
117
-
118
-
119
- await self.libraries.admin.messaging().sendToDevice(batch, payload)
132
+ await self.libraries.admin.messaging().sendToDevice(batch, self._notificationPayload)
120
133
  .then(async function (response) {
121
134
  // self.result.batches.list.push('#' + id + ' | ' + '✅ ' + response.successCount + ' | ' + '❌ ' + response.failureCount);
122
135
  self.assistant.log('Sent batch #' + id);
@@ -126,6 +139,7 @@ Module.prototype.sendBatch = function (batch, id) {
126
139
  if (response.failureCount > 0) {
127
140
  await self.cleanTokens(batch, response.results, id);
128
141
  }
142
+ self.payload.response.data.sent += (batch.length - response.failureCount);
129
143
  resolve();
130
144
  })
131
145
  .catch(function (e) {
@@ -145,7 +159,9 @@ Module.prototype.cleanTokens = function (batch, results, id) {
145
159
  self.assistant.log(`Cleaning tokens of batch ID: ${id}`);
146
160
 
147
161
  results.forEach(function (item, index) {
148
- if (!item.error) { return false; }
162
+ if (!item.error) {
163
+ return false;
164
+ }
149
165
  let curCode = item.error.code;
150
166
  let token = batch[index];
151
167
  self.assistant.log(`Found bad token: ${index} = ${curCode}`);
@@ -168,6 +184,7 @@ Module.prototype.deleteToken = function (token, errorCode) {
168
184
  .delete()
169
185
  .then(function() {
170
186
  self.assistant.log(`Deleting bad token: ${token} for reason ${errorCode}`);
187
+ self.payload.response.data.deleted++;
171
188
  resolve();
172
189
  })
173
190
  .catch(function(error) {
@@ -1,4 +1,4 @@
1
- const fetch = require('node-fetch');
1
+ const fetch = require('wonderful-fetch');
2
2
  const _ = require('lodash')
3
3
 
4
4
  function Module() {
@@ -18,88 +18,116 @@ Module.prototype.main = function () {
18
18
  return reject(assistant.errorManager(`Admin required.`, {code: 401, sentry: false, send: false, log: false}).error)
19
19
  }
20
20
 
21
- const createdInvoice = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/wrapper', {
22
- method: 'POST',
23
- headers: { 'Content-Type': 'application/json' },
24
- body: JSON.stringify({
25
- authenticationToken: Manager.config.backend_manager.key,
26
- method: 'post',
27
- service: 'paypal',
28
- command: 'v2/invoicing/invoices',
29
- body: {
30
- detail: {
31
- currency_code: 'USD',
32
- note: `Post to ${Manager.config.brand.name}`,
33
- },
34
- primary_recipients: [
35
- {
36
- billing_info: {
37
- email_address: payload.data.payload.invoiceEmail,
38
- },
39
- }
40
- ],
41
- items: [
42
- {
43
- name: `Guest post`,
44
- description: `blog/${payload.data.payload.url}`,
45
- quantity: '1',
46
- unit_amount: {
47
- currency_code: 'USD',
48
- value: `${payload.data.payload.invoicePrice}`
49
- },
50
- // discount: {
51
- // percent: '5'
52
- // },
53
- unit_of_measure: 'QUANTITY'
21
+ payload.response.data = {
22
+ invoice: {
23
+ success: false,
24
+ data: {},
25
+ },
26
+ notification: {
27
+ success: false,
28
+ data: {},
29
+ }
30
+ }
31
+
32
+ const postUrl = `blog/${payload.data.payload.url}`;
33
+
34
+ if (payload.data.payload.invoiceEmail && payload.data.payload.invoicePrice) {
35
+ // Create invoice
36
+ const createdInvoice = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/wrapper', {
37
+ method: 'POST',
38
+ json: true,
39
+ body: JSON.stringify({
40
+ authenticationToken: Manager.config.backend_manager.key,
41
+ method: 'post',
42
+ service: 'paypal',
43
+ command: 'v2/invoicing/invoices',
44
+ body: {
45
+ detail: {
46
+ currency_code: 'USD',
47
+ note: `Post to ${Manager.config.brand.name}`,
54
48
  },
55
- ],
56
- }
57
- }),
58
- })
59
- .then(function (res) {
60
- return res.text()
61
- .then(function (data) {
62
- if (res.ok) {
63
- return JSON.parse(data)
64
- } else {
65
- throw new Error(data || res.statusText || 'Unknown error.')
49
+ primary_recipients: [
50
+ {
51
+ billing_info: {
52
+ email_address: payload.data.payload.invoiceEmail,
53
+ },
54
+ }
55
+ ],
56
+ items: [
57
+ {
58
+ name: `Guest post`,
59
+ description: postUrl,
60
+ quantity: '1',
61
+ unit_amount: {
62
+ currency_code: 'USD',
63
+ value: `${payload.data.payload.invoicePrice}`
64
+ },
65
+ // discount: {
66
+ // percent: '5'
67
+ // },
68
+ unit_of_measure: 'QUANTITY'
69
+ },
70
+ ],
66
71
  }
67
- })
68
- })
69
- .catch(function (e) {
70
- return e;
71
- });
72
+ }),
73
+ })
74
+ .then(response => response)
75
+ .catch(e => e);
76
+
77
+ if (createdInvoice instanceof Error) {
78
+ return reject(assistant.errorManager(createdInvoice, {code: 400, sentry: false, send: false, log: false}).error)
79
+ }
80
+
81
+ // Send invoice
82
+ const createdInvoiceId = _.get(createdInvoice, 'href', '').split('/').pop();
83
+ const sentInvoice = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/wrapper', {
84
+ method: 'POST',
85
+ json: true,
86
+ body: JSON.stringify({
87
+ authenticationToken: Manager.config.backend_manager.key,
88
+ service: 'paypal',
89
+ command: `v2/invoicing/invoices/${createdInvoiceId}/send`,
90
+ method: 'post',
91
+ body: {
92
+ }
93
+ }),
94
+ })
95
+ .then(response => response)
96
+ .catch(e => e);
97
+
98
+ if (sentInvoice instanceof Error) {
99
+ return reject(assistant.errorManager(sentInvoice, {code: 500, sentry: false, send: false, log: false}).error)
100
+ }
101
+
102
+ payload.response.data.invoice = {
103
+ success: true,
104
+ data: sentInvoice,
105
+ }
72
106
 
73
- if (createdInvoice instanceof Error) {
74
- return reject(assistant.errorManager(createdInvoice, {code: 400, sentry: false, send: false, log: false}).error)
75
107
  }
76
108
 
77
- const createdInvoiceId = _.get(createdInvoice, 'href', '').split('/').pop();
78
- const sentInvoice = await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/wrapper', {
109
+
110
+ // Send notification
111
+ const sentNotification = fetch(`https://us-central1-${Manager.project.projectId}.cloudfunctions.net/bm_api`, {
79
112
  method: 'POST',
80
- headers: { 'Content-Type': 'application/json' },
113
+ json: true,
81
114
  body: JSON.stringify({
82
115
  authenticationToken: Manager.config.backend_manager.key,
83
- service: 'paypal',
84
- command: `v2/invoicing/invoices/${createdInvoiceId}/send`,
85
- method: 'post',
86
- body: {
116
+ command: `admin:send-notification`,
117
+ payload: {
118
+ title: payload.data.payload.title,
119
+ body: `"${payload.data.payload.title}" was just published on our blog. It's a great read and we think you'll enjoy the content!`,
120
+ click_action: `${Manager.config.brand.url}/${postUrl}`,
121
+ icon: Manager.config.brand.brandmark,
87
122
  }
88
123
  }),
89
124
  })
90
- .then(function (res) {
91
- return res.text()
92
- .then(function (data) {
93
- if (res.ok) {
94
- return resolve({data: JSON.parse(data)})
95
- } else {
96
- throw new Error(data || res.statusText || 'Unknown error.')
97
- }
98
- })
99
- })
100
- .catch(function (e) {
101
- return reject(assistant.errorManager(e, {code: 500, sentry: false, send: false, log: false}).error)
102
- });
125
+ .then(response => response)
126
+ .catch(e => e);
127
+
128
+
129
+ return resolve({data: payload.response.data})
130
+
103
131
 
104
132
  });
105
133
 
@@ -0,0 +1,55 @@
1
+ const fetch = require('node-fetch');
2
+ const _ = require('lodash')
3
+
4
+ function Module() {
5
+
6
+ }
7
+
8
+ Module.prototype.main = function () {
9
+ const self = this;
10
+ const Manager = self.Manager;
11
+ const Api = self.Api;
12
+ const assistant = self.assistant;
13
+ const payload = self.payload;
14
+
15
+ return new Promise(async function(resolve, reject) {
16
+
17
+ if (!payload.user.roles.admin) {
18
+ self.assistant.log('User is not admin')
19
+ }
20
+
21
+ const url = 'https://itwcreativeworks.com';
22
+ const title = 'https://itwcreativeworks.com';
23
+ const icon = 'https://cdn.itwcreativeworks.com/assets/itw-creative-works/images/socials/itw-creative-works-brandmark-black-1024x1024.png?cb=1651834176';
24
+
25
+
26
+ await Api.import('admin:send-notification', {
27
+ title: 'New blog post!',
28
+ click_action: url,
29
+ body: `"${title}" was just published on our blog. It's a great read and we think you'll enjoy the content!`,
30
+ icon: icon,
31
+ })
32
+ .then(library => {
33
+
34
+ console.log('----');
35
+
36
+ library.main()
37
+ .then(result => {
38
+ return resolve({data: {success: true}});
39
+ })
40
+ .catch(e => {
41
+ console.error('Error', e);
42
+ })
43
+
44
+ })
45
+
46
+ // if (error) {
47
+ //
48
+ // }
49
+
50
+ });
51
+
52
+ };
53
+
54
+
55
+ module.exports = Module;
@@ -1,5 +1,6 @@
1
1
  const path = require('path');
2
2
  const _ = require('lodash');
3
+ const jetpack = require('fs-jetpack');
3
4
 
4
5
  function Module() {
5
6
 
@@ -22,6 +23,11 @@ Module.prototype.init = function (Manager, data) {
22
23
  user: {},
23
24
  };
24
25
 
26
+ // Fix the two required
27
+ const resolved = self.resolveCommand(self.assistant.request.data.command);
28
+ self.assistant.request.data.command = resolved.command;
29
+ self.assistant.request.data.payload = self.assistant.request.data.payload || {};
30
+
25
31
  return self;
26
32
  }
27
33
 
@@ -40,33 +46,39 @@ Module.prototype.main = function() {
40
46
 
41
47
  self.assistant.log(`Executing: ${resolved.command}`, self.payload, JSON.stringify(self.payload), {environment: 'production'})
42
48
 
43
- await self.import(resolved.command)
44
- .then(async lib => {
45
- try {
46
- // Call main function
47
- await lib.main()
48
- .then(result => {
49
- self.payload.response.status = result.status || 200;
50
- self.payload.response.data = result.data || {};
51
- })
52
- .catch(e => {
53
- self.payload.response.status = e.code || 500;
54
- self.payload.response.error = e || new Error('Unknown error occured');
55
- })
56
- } catch (e) {
57
- self.payload.response.status = 500;
58
- self.payload.response.error = e || new Error('Unknown error occured');
59
- }
60
- })
61
- .catch(e => {
49
+ if (!resolved.exists) {
62
50
  self.payload.response.status = 400;
63
- self.payload.response.error = new Error(`Failed to import: ${e}`);
64
- })
51
+ self.payload.response.error = new Error(`${self.payload.data.command} is not a valid command`);
52
+ } else {
53
+ await self.import(resolved.command)
54
+ .then(async lib => {
55
+ try {
56
+ // Call main function
57
+ await lib.main()
58
+ .then(result => {
59
+ result = result || {};
60
+ self.payload.response.status = result.status || self.payload.response.status || 200;
61
+ self.payload.response.data = result.data || self.payload.response.data || {};
62
+ })
63
+ .catch(e => {
64
+ self.payload.response.status = e.code || 500;
65
+ self.payload.response.error = e || new Error('Unknown error occured');
66
+ })
67
+ } catch (e) {
68
+ self.payload.response.status = 500;
69
+ self.payload.response.error = e || new Error('Unknown error occured');
70
+ }
71
+ })
72
+ .catch(e => {
73
+ self.payload.response.status = 400;
74
+ self.payload.response.error = new Error(`Failed to import: ${e}`);
75
+ })
76
+ }
65
77
 
66
78
  if (self.payload.response.status === 200) {
67
79
  return res.status(self.payload.response.status).json(self.payload.response.data);
68
80
  } else {
69
- console.error(`Error executing ${self.payload.data.command} => ${resolved.command} @ ${resolved.path}`, self.payload.response.error)
81
+ console.error(`Error executing ${resolved.command} @ ${resolved.path}`, self.payload.response.error)
70
82
  // return res.status(self.payload.response.status).send(self.payload.response.error.message);
71
83
  return res.status(self.payload.response.status).send(`${self.payload.response.error}`);
72
84
  }
@@ -89,12 +101,19 @@ Module.prototype.import = function (command, payload, user, response) {
89
101
  lib.assistant = self.assistant;
90
102
  lib.payload = _.cloneDeep({
91
103
  data: {
104
+ // command: '?',
92
105
  payload: payload ? payload : self.payload.data.payload,
93
106
  },
94
107
  user: user ? user : self.payload.user,
95
108
  response: response ? response : self.payload.response,
96
109
  });
97
110
 
111
+ if (self.payload.data.backendManagerKey) {
112
+ lib.payload.data.backendManagerKey = self.payload.data.backendManagerKey;
113
+ } else if (self.payload.data.authenticationToken) {
114
+ lib.payload.data.authenticationToken = self.payload.data.authenticationToken;
115
+ }
116
+
98
117
  // lib.payload = {};
99
118
  //
100
119
  // // Set payload and user if it's provided
@@ -112,6 +131,7 @@ Module.prototype.import = function (command, payload, user, response) {
112
131
 
113
132
  Module.prototype.resolveCommand = function (command) {
114
133
  const self = this;
134
+ const originalCommand = command;
115
135
 
116
136
  // Start
117
137
  if (false) {
@@ -153,25 +173,36 @@ Module.prototype.resolveCommand = function (command) {
153
173
  command = 'admin:payment-processor';
154
174
 
155
175
  // Special
156
- } else if (command === 'special:setup-electron-manager-client' || command === 'setup-electron-manager-client') {
157
- command = 'special:setup-electron-manager-client';
176
+ // } else if (command === 'special:setup-electron-manager-client' || command === 'setup-electron-manager-client') {
177
+ // command = 'special:setup-electron-manager-client';
158
178
 
159
179
  // Test
160
- } else if (command === 'test:authenticate' || command === 'authenticate') {
161
- command = 'test:authenticate';
162
- } else if (command === 'test:create-test-accounts' || command === 'create-test-accounts') {
163
- command = 'test:create-test-accounts';
164
- } else if (command === 'test:webhook' || command === 'webhook') {
165
- command = 'test:webhook';
180
+ // } else if (command === 'test:authenticate' || command === 'authenticate') {
181
+ // command = 'test:authenticate';
182
+ // } else if (command === 'test:create-test-accounts' || command === 'create-test-accounts') {
183
+ // command = 'test:create-test-accounts';
184
+ // } else if (command === 'test:webhook' || command === 'webhook') {
185
+ // command = 'test:webhook';
166
186
 
167
187
  // End
168
188
  } else {
169
- command = 'error:error';
189
+ // command = 'error:error';
190
+ }
191
+
192
+ command = command || '';
193
+
194
+ const resolvedPath = './' + path.join('./api/', `${command.replace(/\.\.\//g, '').replace(/\:/, '/')}.js`);
195
+ const pathExists = jetpack.exists(path.join(__dirname, resolvedPath));
196
+
197
+ // if (!command || command === 'error:error') {
198
+ if (!pathExists) {
199
+ self.assistant.log(`This command does not exist: ${originalCommand} => ${command} @ ${pathExists}`, {environment: 'production'})
170
200
  }
171
201
 
172
202
  return {
173
203
  command: command,
174
- path: './' + path.join('./api/', `${command.replace(/\.\.\//g, '').replace(/\:/, '/')}.js`),
204
+ path: resolvedPath,
205
+ exists: !!pathExists,
175
206
  };
176
207
  }
177
208