backend-manager 2.0.33 → 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.33",
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",
@@ -57,12 +57,12 @@
57
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/",
65
65
  "src/",
66
66
  "templates/"
67
67
  ]
68
- }
68
+ }
@@ -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
  }
@@ -119,6 +131,7 @@ Module.prototype.import = function (command, payload, user, response) {
119
131
 
120
132
  Module.prototype.resolveCommand = function (command) {
121
133
  const self = this;
134
+ const originalCommand = command;
122
135
 
123
136
  // Start
124
137
  if (false) {
@@ -160,25 +173,36 @@ Module.prototype.resolveCommand = function (command) {
160
173
  command = 'admin:payment-processor';
161
174
 
162
175
  // Special
163
- } else if (command === 'special:setup-electron-manager-client' || command === 'setup-electron-manager-client') {
164
- 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';
165
178
 
166
179
  // Test
167
- } else if (command === 'test:authenticate' || command === 'authenticate') {
168
- command = 'test:authenticate';
169
- } else if (command === 'test:create-test-accounts' || command === 'create-test-accounts') {
170
- command = 'test:create-test-accounts';
171
- } else if (command === 'test:webhook' || command === 'webhook') {
172
- 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';
173
186
 
174
187
  // End
175
188
  } else {
176
- 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'})
177
200
  }
178
201
 
179
202
  return {
180
203
  command: command,
181
- path: './' + path.join('./api/', `${command.replace(/\.\.\//g, '').replace(/\:/, '/')}.js`),
204
+ path: resolvedPath,
205
+ exists: !!pathExists,
182
206
  };
183
207
  }
184
208