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 +6 -5
- package/src/manager/functions/core/actions/api/admin/send-notification.js +35 -18
- package/src/manager/functions/core/actions/api/firebase/get-providers.js +104 -0
- package/src/manager/functions/core/actions/api/handler/create-post.js +101 -72
- package/src/manager/functions/core/actions/api/test/lab.js +55 -0
- package/src/manager/functions/core/actions/api.js +58 -32
- package/src/manager/index.js +75 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "2.0
|
|
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.
|
|
38
|
+
"dotenv": "^16.0.1",
|
|
39
39
|
"firebase-admin": "^9.12.0",
|
|
40
|
-
"firebase-functions": "^3.21.
|
|
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.
|
|
61
|
-
"yargs": "^17.
|
|
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
|
-
//
|
|
19
|
-
|
|
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:
|
|
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
|
-
|
|
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) {
|
|
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('
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
78
|
-
|
|
109
|
+
|
|
110
|
+
// Send notification
|
|
111
|
+
const sentNotification = fetch(`https://us-central1-${Manager.project.projectId}.cloudfunctions.net/bm_api`, {
|
|
79
112
|
method: 'POST',
|
|
80
|
-
|
|
113
|
+
json: true,
|
|
81
114
|
body: JSON.stringify({
|
|
82
115
|
authenticationToken: Manager.config.backend_manager.key,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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(
|
|
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 ${
|
|
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
|
-
|
|
169
|
-
} else if (command === 'test:create-test-accounts' || command === 'create-test-accounts') {
|
|
170
|
-
|
|
171
|
-
} else if (command === 'test:webhook' || command === 'webhook') {
|
|
172
|
-
|
|
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:
|
|
206
|
+
path: resolvedPath,
|
|
207
|
+
exists: !!pathExists,
|
|
182
208
|
};
|
|
183
209
|
}
|
|
184
210
|
|
package/src/manager/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
373
|
+
if (!jetpack.exists(dbPath)) {
|
|
374
|
+
jetpack.write(dbPath, {});
|
|
375
|
+
}
|
|
376
|
+
self.libraries.localDatabase = low(adapter);
|
|
354
377
|
} catch (e) {
|
|
355
|
-
console.error('
|
|
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
|
-
|
|
442
|
-
|
|
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
|
-
|
|
448
|
-
return new
|
|
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
|
-
|
|
454
|
-
return new
|
|
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
|
};
|