backend-manager 2.0.33 → 2.2.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.2.0",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -35,14 +35,15 @@
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",
44
44
  "json5": "^2.2.1",
45
45
  "lodash": "^4.17.21",
46
+ "lowdb": "^1.0.0",
46
47
  "mailchimp-api-v3": "^1.15.0",
47
48
  "mocha": "^9.2.2",
48
49
  "moment": "^2.29.3",
@@ -57,8 +58,8 @@
57
58
  "ultimate-jekyll-poster": "^0.0.11",
58
59
  "universal-analytics": "^0.5.3",
59
60
  "uuid": "^8.3.2",
60
- "wonderful-fetch": "^0.0.4",
61
- "yargs": "^17.4.1"
61
+ "wonderful-fetch": "^0.0.5",
62
+ "yargs": "^17.5.1"
62
63
  },
63
64
  "files": [
64
65
  "bin/",
@@ -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) {
@@ -0,0 +1,104 @@
1
+ const fetch = require('wonderful-fetch');
2
+
3
+ function Module() {
4
+
5
+ }
6
+
7
+ Module.prototype.main = function () {
8
+ const self = this;
9
+ const Manager = self.Manager;
10
+ const Api = self.Api;
11
+ const assistant = self.assistant;
12
+ const payload = self.payload;
13
+
14
+ return new Promise(async function(resolve, reject) {
15
+
16
+ const providers = [
17
+ { name: 'google.com', prefix: ['id_token'] },
18
+ { name: 'facebook.com', prefix: ['access_token'] },
19
+ { name: 'twitter.com', prefix: ['access_token', 'oauth_token_secret'] },
20
+ { name: 'github.com', prefix: ['access_token'] },
21
+ { name: 'microsoft.com', prefix: ['id_token'] },
22
+ // { name: 'microsoft.com', prefix: ['context', 'continueUri', 'sessionId'] },
23
+ { name: 'yahoo.com', prefix: ['id_token'] },
24
+ { name: 'apple.com', prefix: ['id_token'] },
25
+ ]
26
+ const promises = []
27
+
28
+ payload.data.payload.firebaseApiKey = payload.data.payload.firebaseApiKey || false;
29
+
30
+ if (!payload.data.payload.firebaseApiKey) {
31
+ return reject(assistant.errorManager(`The <firebaseApiKey> parameter is required.`, {code: 400, sentry: false, send: false, log: false}).error)
32
+ }
33
+
34
+ function request(provider) {
35
+ return new Promise(function(resolve, reject) {
36
+ let prefix = '';
37
+ provider.prefix
38
+ .forEach((item, i) => {
39
+ prefix += `${item}=LOL&`
40
+ });
41
+
42
+ fetch(`https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=${payload.data.payload.firebaseApiKey}`, {
43
+ method: 'post',
44
+ json: true,
45
+ body: {
46
+ postBody: `${prefix}providerId=${provider.name}`,
47
+ requestUri: 'http://localhost',
48
+ returnIdpCredential: true,
49
+ returnSecureToken: true
50
+ }
51
+ })
52
+ .then(response => {
53
+ payload.response.data[provider.name] = true;
54
+ })
55
+ .catch(e => {
56
+ try {
57
+ const errorJson = JSON.parse(e.message);
58
+ const errorArray = errorJson.error.errors || [];
59
+ let result = true;
60
+
61
+ errorArray
62
+ .forEach((error, i) => {
63
+ if (error.message.includes('OPERATION_NOT_ALLOWED') || error.message.includes('INVALID_CREDENTIAL_OR_PROVIDER_ID')) {
64
+ result = false;
65
+ }
66
+ });
67
+
68
+ assistant.log('Provider details', provider.name, result);
69
+
70
+ payload.response.data[provider.name] = result;
71
+ } catch (e) {
72
+ assistant.errorManager(`Error parsing error: ${e}`, {sentry: true, send: false, log: true})
73
+ payload.response.data[provider.name] = false;
74
+ }
75
+ })
76
+ .finally(r => {
77
+ return resolve();
78
+ })
79
+ });
80
+ }
81
+
82
+ providers
83
+ .forEach((provider, i) => {
84
+ payload.response.data[provider.name] = false;
85
+ promises.push(request(provider))
86
+ });
87
+
88
+ assistant.log('Checking providers...', {environment: 'production'});
89
+
90
+ await Promise.all(promises)
91
+ .then(response => {
92
+ // console.log('--payload.response.data', promises.length, payload.response.data);
93
+ return resolve({data: payload.response.data});
94
+ })
95
+ .catch(e => {
96
+ return reject(assistant.errorManager(`Failed to check providers: ${e}`, {code: 500, sentry: false, send: false, log: false}).error)
97
+ })
98
+
99
+ });
100
+
101
+ };
102
+
103
+
104
+ module.exports = Module;
@@ -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,117 @@ 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
+ click_action: Manager.config.brand.url,
122
+ icon: Manager.config.brand.brandmark,
87
123
  }
88
124
  }),
89
125
  })
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
- });
126
+ .then(response => response)
127
+ .catch(e => e);
128
+
129
+
130
+ return resolve({data: payload.response.data})
131
+
103
132
 
104
133
  });
105
134
 
@@ -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,41 @@ 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
+ // console.log('---result', result);
61
+ // console.log('---self.payload.response.data', self.payload.response.data);
62
+ self.payload.response.status = result.status || self.payload.response.status || 200;
63
+ self.payload.response.data = result.data || self.payload.response.data || {};
64
+ })
65
+ .catch(e => {
66
+ self.payload.response.status = e.code || 500;
67
+ self.payload.response.error = e || new Error('Unknown error occured');
68
+ })
69
+ } catch (e) {
70
+ self.payload.response.status = 500;
71
+ self.payload.response.error = e || new Error('Unknown error occured');
72
+ }
73
+ })
74
+ .catch(e => {
75
+ self.payload.response.status = 400;
76
+ self.payload.response.error = new Error(`Failed to import: ${e}`);
77
+ })
78
+ }
65
79
 
66
80
  if (self.payload.response.status === 200) {
67
81
  return res.status(self.payload.response.status).json(self.payload.response.data);
68
82
  } else {
69
- console.error(`Error executing ${self.payload.data.command} => ${resolved.command} @ ${resolved.path}`, self.payload.response.error)
83
+ console.error(`Error executing ${resolved.command} @ ${resolved.path}`, self.payload.response.error)
70
84
  // return res.status(self.payload.response.status).send(self.payload.response.error.message);
71
85
  return res.status(self.payload.response.status).send(`${self.payload.response.error}`);
72
86
  }
@@ -119,6 +133,7 @@ Module.prototype.import = function (command, payload, user, response) {
119
133
 
120
134
  Module.prototype.resolveCommand = function (command) {
121
135
  const self = this;
136
+ const originalCommand = command;
122
137
 
123
138
  // Start
124
139
  if (false) {
@@ -160,25 +175,36 @@ Module.prototype.resolveCommand = function (command) {
160
175
  command = 'admin:payment-processor';
161
176
 
162
177
  // Special
163
- } else if (command === 'special:setup-electron-manager-client' || command === 'setup-electron-manager-client') {
164
- command = 'special:setup-electron-manager-client';
178
+ // } else if (command === 'special:setup-electron-manager-client' || command === 'setup-electron-manager-client') {
179
+ // command = 'special:setup-electron-manager-client';
165
180
 
166
181
  // 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';
182
+ // } else if (command === 'test:authenticate' || command === 'authenticate') {
183
+ // command = 'test:authenticate';
184
+ // } else if (command === 'test:create-test-accounts' || command === 'create-test-accounts') {
185
+ // command = 'test:create-test-accounts';
186
+ // } else if (command === 'test:webhook' || command === 'webhook') {
187
+ // command = 'test:webhook';
173
188
 
174
189
  // End
175
190
  } else {
176
- command = 'error:error';
191
+ // command = 'error:error';
192
+ }
193
+
194
+ command = command || '';
195
+
196
+ const resolvedPath = './' + path.join('./api/', `${command.replace(/\.\.\//g, '').replace(/\:/, '/')}.js`);
197
+ const pathExists = jetpack.exists(path.join(__dirname, resolvedPath));
198
+
199
+ // if (!command || command === 'error:error') {
200
+ if (!pathExists) {
201
+ self.assistant.log(`This command does not exist: ${originalCommand} => ${command} @ ${pathExists}`, {environment: 'production'})
177
202
  }
178
203
 
179
204
  return {
180
205
  command: command,
181
- path: './' + path.join('./api/', `${command.replace(/\.\.\//g, '').replace(/\:/, '/')}.js`),
206
+ path: resolvedPath,
207
+ exists: !!pathExists,
182
208
  };
183
209
  }
184
210
 
@@ -29,6 +29,7 @@ Manager.prototype.init = function (exporter, options) {
29
29
  options = options || {};
30
30
  options.initialize = typeof options.initialize === 'undefined' ? true : options.initialize;
31
31
  options.setupFunctions = typeof options.setupFunctions === 'undefined' ? true : options.setupFunctions;
32
+ options.setupLocalDatabase = typeof options.setupLocalDatabase === 'undefined' ? false : options.setupLocalDatabase;
32
33
  options.sentry = typeof options.sentry === 'undefined' ? true : options.sentry;
33
34
  options.reportErrorsInDev = typeof options.reportErrorsInDev === 'undefined' ? false : options.reportErrorsInDev;
34
35
  options.firebaseConfig = options.firebaseConfig;
@@ -40,11 +41,15 @@ Manager.prototype.init = function (exporter, options) {
40
41
 
41
42
  // Load libraries
42
43
  self.libraries = {
44
+ // Third-party
43
45
  functions: require('firebase-functions'),
44
46
  admin: require('firebase-admin'),
45
47
  cors: require('cors')({ origin: true }),
46
- Assistant: require('backend-assistant'),
47
48
  sentry: null,
49
+
50
+ // First-party
51
+ Assistant: require('backend-assistant'),
52
+ localDatabase: null,
48
53
  User: null,
49
54
  Analytics: null,
50
55
  };
@@ -76,23 +81,7 @@ Manager.prototype.init = function (exporter, options) {
76
81
  require('firebase-functions/lib/logger/compat');
77
82
  }
78
83
 
79
- // Setup options features
80
- if (self.options.initialize) {
81
- // console.log('Initializing:', self.project);
82
- try {
83
- // console.log('----self.project.databaseURL', self.project.databaseURL);
84
- self.libraries.initializedAdmin = self.libraries.admin.initializeApp({
85
- credential: self.libraries.admin.credential.cert(
86
- require(path.resolve(self.cwd, options.serviceAccountPath))
87
- ),
88
- databaseURL: self.project.databaseURL,
89
- }, options.uniqueAppName);
90
- } catch (e) {
91
- console.error('Failed to call .initializeApp()', e);
92
- }
93
- // admin.firestore().settings({/* your settings... */ timestampsInSnapshots: true})
94
- }
95
-
84
+ // Setup sentry
96
85
  if (self.options.sentry) {
97
86
  const sentryRelease = `${get(self.config, 'app.id') || self.project.projectId}@${self.package.version}`;
98
87
  const sentryDSN = get(self.config, 'sentry.dsn', '');
@@ -116,6 +105,23 @@ Manager.prototype.init = function (exporter, options) {
116
105
  });
117
106
  }
118
107
 
108
+ // Setup options features
109
+ if (self.options.initialize) {
110
+ // console.log('Initializing:', self.project);
111
+ try {
112
+ // console.log('----self.project.databaseURL', self.project.databaseURL);
113
+ self.libraries.initializedAdmin = self.libraries.admin.initializeApp({
114
+ credential: self.libraries.admin.credential.cert(
115
+ require(path.resolve(self.cwd, options.serviceAccountPath))
116
+ ),
117
+ databaseURL: self.project.databaseURL,
118
+ }, options.uniqueAppName);
119
+ } catch (e) {
120
+ console.error('Failed to call .initializeApp()', e);
121
+ }
122
+ // admin.firestore().settings({/* your settings... */ timestampsInSnapshots: true})
123
+ }
124
+
119
125
  // Main functions
120
126
  if (options.setupFunctions) {
121
127
  exporter.bm_api =
@@ -348,13 +354,30 @@ Manager.prototype.init = function (exporter, options) {
348
354
  }
349
355
 
350
356
  // Set dotenv
351
- // if (self.assistant.meta.environment === 'development') {
357
+ try {
358
+ require('dotenv').config();
359
+ } catch (e) {
360
+ console.error('Failed to set up environment variables from .env file');
361
+ }
362
+
363
+ // Setup LocalDatabase
364
+ if (options.setupLocalDatabase) {
365
+ const low = require('lowdb');
366
+ const FileSync = require('lowdb/adapters/FileSync');
367
+ // const dbPath = path.resolve(process.cwd(), './.data/db.json');
368
+ const dbPath = './.data/db.json';
369
+ const adapter = new FileSync(dbPath);
370
+ const jetpack = require('fs-jetpack');
371
+
352
372
  try {
353
- require('dotenv').config();
373
+ if (!jetpack.exists(dbPath)) {
374
+ jetpack.write(dbPath, {});
375
+ }
376
+ self.libraries.localDatabase = low(adapter);
354
377
  } catch (e) {
355
- console.error('Failed to set up environment variables from .env file');
378
+ console.error('Could not load .data', e);
356
379
  }
357
- // }
380
+ }
358
381
 
359
382
  return self;
360
383
  };
@@ -438,22 +461,46 @@ Manager.prototype.Assistant = function(ref, options) {
438
461
  };
439
462
 
440
463
  Manager.prototype.User = function () {
441
- this.libraries.User = this.libraries.User || require('./helpers/user.js');
442
- return new this.libraries.User(...arguments);
464
+ const self = this;
465
+ self.libraries.User = self.libraries.User || require('./helpers/user.js');
466
+ return new self.libraries.User(...arguments);
443
467
  };
444
468
 
445
469
  Manager.prototype.Analytics = function () {
446
470
  const self = this;
447
- this.libraries.Analytics = this.libraries.Analytics || require('./helpers/analytics.js');
448
- return new this.libraries.Analytics(self, ...arguments);
471
+ self.libraries.Analytics = self.libraries.Analytics || require('./helpers/analytics.js');
472
+ return new self.libraries.Analytics(self, ...arguments);
449
473
  };
450
474
 
451
475
  Manager.prototype.ApiManager = function () {
452
476
  const self = this;
453
- this.libraries.ApiManager = this.libraries.ApiManager || require('./helpers/api-manager.js');
454
- return new this.libraries.ApiManager(self, ...arguments);
477
+ self.libraries.ApiManager = self.libraries.ApiManager || require('./helpers/api-manager.js');
478
+ return new self.libraries.ApiManager(self, ...arguments);
455
479
  };
456
480
 
481
+ // Manager.prototype.LocalDatabase = function () {
482
+ // const self = this;
483
+ // if (!self.libraries.LocalDatabase) {
484
+ // const low = require('lowdb');
485
+ // const FileSync = require('lowdb/adapters/FileSync');
486
+ // // const dbPath = path.resolve(process.cwd(), './.data/db.json');
487
+ // const dbPath = './.data/db.json';
488
+ // const adapter = new FileSync(dbPath);
489
+ // const jetpack = require('fs-jetpack');
490
+ //
491
+ // try {
492
+ // if (!jetpack.exists(dbPath)) {
493
+ // jetpack.write(dbPath, {});
494
+ // }
495
+ // self.localDatabase = low(adapter);
496
+ // } catch (e) {
497
+ // console.error('Could not load .data', e);
498
+ // }
499
+ // }
500
+ // self.libraries.LocalDatabase = self.libraries.LocalDatabase || require('./helpers/api-manager.js');
501
+ // return new self.libraries.LocalDatabase(self, ...arguments);
502
+ // };
503
+
457
504
  Manager.prototype.require = function (p) {
458
505
  return require(p);
459
506
  };