backend-manager 4.2.7 → 4.2.9
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": "4.2.
|
|
3
|
+
"version": "4.2.9",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"uid-generator": "^2.0.0",
|
|
80
80
|
"ultimate-jekyll-poster": "^1.0.2",
|
|
81
81
|
"uuid": "^9.0.1",
|
|
82
|
-
"wonderful-fetch": "^1.3.
|
|
82
|
+
"wonderful-fetch": "^1.3.1",
|
|
83
83
|
"wonderful-log": "^1.0.7",
|
|
84
84
|
"wonderful-version": "^1.2.0",
|
|
85
85
|
"yaml": "^2.6.1",
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Module
|
|
2
|
+
function Module() {
|
|
3
|
+
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// Main
|
|
7
|
+
Module.prototype.main = function () {
|
|
8
|
+
const self = this;
|
|
9
|
+
|
|
10
|
+
// Shortcuts
|
|
11
|
+
const Manager = self.Manager;
|
|
12
|
+
const Api = self.Api;
|
|
13
|
+
const assistant = self.assistant;
|
|
14
|
+
const payload = self.payload;
|
|
15
|
+
|
|
16
|
+
return new Promise(async function(resolve, reject) {
|
|
17
|
+
// Load libraries
|
|
18
|
+
const fetch = Manager.require('wonderful-fetch');
|
|
19
|
+
|
|
20
|
+
// Set up response obj
|
|
21
|
+
payload.response.data = {};
|
|
22
|
+
|
|
23
|
+
// Fix notification payload
|
|
24
|
+
const email = payload.data.payload;
|
|
25
|
+
|
|
26
|
+
// Log
|
|
27
|
+
assistant.log('Resolved email payload', email)
|
|
28
|
+
|
|
29
|
+
// Check if user is admin
|
|
30
|
+
if (!payload.user.roles.admin) {
|
|
31
|
+
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Attach BEM key since we are authorized
|
|
35
|
+
email.backendManagerKey = Manager?.config?.backend_manager?.key;
|
|
36
|
+
|
|
37
|
+
// Make request to ITW email service
|
|
38
|
+
await fetch('https://us-central1-itw-creative-works.cloudfunctions.net/sendEmail', {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
response: 'json',
|
|
41
|
+
body: email,
|
|
42
|
+
})
|
|
43
|
+
.then((r) => {
|
|
44
|
+
// Set response
|
|
45
|
+
payload.response.data = r;
|
|
46
|
+
|
|
47
|
+
// Resolve
|
|
48
|
+
return resolve({data: payload.response.data})
|
|
49
|
+
})
|
|
50
|
+
.catch((e) => {
|
|
51
|
+
return reject(assistant.errorify(`Failed to send email: ${e}`, {code: 400, sentry: true}));
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// HELPERS //
|
|
58
|
+
Module.prototype.processTokens = async function (note, options) {
|
|
59
|
+
const self = this;
|
|
60
|
+
|
|
61
|
+
// Shortcuts
|
|
62
|
+
const Manager = self.Manager;
|
|
63
|
+
const Api = self.Api;
|
|
64
|
+
const assistant = self.assistant;
|
|
65
|
+
const payload = self.payload;
|
|
66
|
+
|
|
67
|
+
// Set options
|
|
68
|
+
options = options || {};
|
|
69
|
+
options.tags = options.tags || false;
|
|
70
|
+
|
|
71
|
+
// Define collection path
|
|
72
|
+
const queryConditions = options.tags
|
|
73
|
+
? [{ field: 'tags', operator: 'array-contains-any', value: options.tags }]
|
|
74
|
+
: [];
|
|
75
|
+
|
|
76
|
+
// Batch processing logic
|
|
77
|
+
await Manager.Utilities().iterateCollection(
|
|
78
|
+
async (batch, index) => {
|
|
79
|
+
let batchTokens = [];
|
|
80
|
+
|
|
81
|
+
// Collect tokens from the current batch
|
|
82
|
+
for (const doc of batch.docs) {
|
|
83
|
+
const data = doc.data();
|
|
84
|
+
batchTokens.push(data.token);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Send the batch
|
|
88
|
+
try {
|
|
89
|
+
assistant.log(`Sending batch ${index} with ${batchTokens.length} tokens.`);
|
|
90
|
+
await self.sendBatch(batchTokens, note, index);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
assistant.error(`Error sending batch ${index}`, e);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
collection: PATH_NOTIFICATIONS,
|
|
97
|
+
where: queryConditions,
|
|
98
|
+
batchSize: BATCH_SIZE,
|
|
99
|
+
log: true,
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
.then(() => {
|
|
103
|
+
assistant.log('All batches processed successfully.');
|
|
104
|
+
})
|
|
105
|
+
.catch(e => {
|
|
106
|
+
assistant.errorify(`Error during token processing: ${e}`, { code: 500, log: true });
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Sending using SDK
|
|
111
|
+
// https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-multiple-devices
|
|
112
|
+
|
|
113
|
+
// Manually sending
|
|
114
|
+
// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send
|
|
115
|
+
// https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually
|
|
116
|
+
Module.prototype.sendBatch = async function (batch, note, id) {
|
|
117
|
+
const self = this;
|
|
118
|
+
|
|
119
|
+
// Shortcuts
|
|
120
|
+
const Manager = self.Manager;
|
|
121
|
+
const Api = self.Api;
|
|
122
|
+
const assistant = self.assistant;
|
|
123
|
+
const payload = self.payload;
|
|
124
|
+
|
|
125
|
+
// Libraries
|
|
126
|
+
const { admin } = self.libraries;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Log
|
|
130
|
+
assistant.log(`Sending batch ID: ${id}`);
|
|
131
|
+
console.log('🚩🚩🚩🚩🚩', 1, ); // FLAG
|
|
132
|
+
assistant.log(`batch`, batch);
|
|
133
|
+
assistant.log(`note`, note);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const response = await admin.messaging().sendMulticast({
|
|
137
|
+
tokens: batch,
|
|
138
|
+
notification: {
|
|
139
|
+
title: 'Your Notification Title',
|
|
140
|
+
body: 'This is the notification message.'
|
|
141
|
+
},
|
|
142
|
+
data: {
|
|
143
|
+
customDataKey: 'customDataValue' // Optional custom data payload
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
assistant.log(`🚩🚩🚩🚩🚩 222 response ${id}`, response); // FLAG
|
|
148
|
+
} catch (e) {
|
|
149
|
+
assistant.error(`🚩🚩🚩🚩🚩 222 error ${id}`, e);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const response = await admin.messaging().sendMulticast({
|
|
154
|
+
tokens: batch,
|
|
155
|
+
notification: note.notification,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
assistant.log(`🚩🚩🚩🚩🚩 333 response ${id}`, response); // FLAG
|
|
159
|
+
} catch (e) {
|
|
160
|
+
assistant.error(`🚩🚩🚩🚩🚩 333 error ${id}`, e);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const messages = batch.map(token => ({
|
|
165
|
+
token: token,
|
|
166
|
+
notification: {
|
|
167
|
+
title: 'Your Notification Title',
|
|
168
|
+
body: 'This is the notification message.'
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
const response = await admin.messaging().sendEach(messages);
|
|
173
|
+
|
|
174
|
+
assistant.log(`🚩🚩🚩🚩🚩 444 response ${id}`, response); // FLAG
|
|
175
|
+
} catch (e) {
|
|
176
|
+
assistant.error(`🚩🚩🚩🚩🚩 444 error ${id}`, e);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Send the batch
|
|
180
|
+
const response = await admin.messaging().sendToDevice(batch, note);
|
|
181
|
+
|
|
182
|
+
// Log
|
|
183
|
+
assistant.log(`Sent batch ID: ${id}, Success: ${response.successCount}, Failures: ${response.failureCount}`);
|
|
184
|
+
|
|
185
|
+
// Clean bad tokens
|
|
186
|
+
if (response.failureCount > 0) {
|
|
187
|
+
await self.cleanTokens(batch, response.results, id);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Update response
|
|
191
|
+
payload.response.data.sent += (batch.length - response.failureCount);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
throw assistant.errorify(`Error sending batch ${id}: ${e}`, { code: 500, log: true });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
Module.prototype.cleanTokens = async function (batch, results, id) {
|
|
198
|
+
const self = this;
|
|
199
|
+
|
|
200
|
+
// Shortcuts
|
|
201
|
+
const Manager = self.Manager;
|
|
202
|
+
const Api = self.Api;
|
|
203
|
+
const assistant = self.assistant;
|
|
204
|
+
const payload = self.payload;
|
|
205
|
+
|
|
206
|
+
// Log
|
|
207
|
+
assistant.log(`Cleaning ${results.length} tokens of batch ID: ${id}`);
|
|
208
|
+
|
|
209
|
+
// Filter out bad tokens
|
|
210
|
+
const cleanPromises = results
|
|
211
|
+
.map((item, index) => {
|
|
212
|
+
// Check if the token is bad
|
|
213
|
+
if (item.error && BAD_TOKEN_REASONS.includes(item.error.code)) {
|
|
214
|
+
const token = batch[index];
|
|
215
|
+
|
|
216
|
+
// Log
|
|
217
|
+
assistant.log(`Found bad token: ${token} (Reason: ${item.error.code})`);
|
|
218
|
+
|
|
219
|
+
// Delete the token
|
|
220
|
+
return self.deleteToken(token, item.error.code);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Return null for valid tokens
|
|
224
|
+
return null;
|
|
225
|
+
})
|
|
226
|
+
// Filter out nulls for valid tokens
|
|
227
|
+
.filter(Boolean);
|
|
228
|
+
|
|
229
|
+
// Clean bad tokens
|
|
230
|
+
try {
|
|
231
|
+
await Promise.all(cleanPromises);
|
|
232
|
+
assistant.log(`Completed cleaning tokens for batch ID: ${id}`);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
assistant.error(`Error cleaning tokens for batch ID: ${id}`, e);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
Module.prototype.deleteToken = async function (token, errorCode) {
|
|
239
|
+
const self = this;
|
|
240
|
+
|
|
241
|
+
// Shortcuts
|
|
242
|
+
const Manager = self.Manager;
|
|
243
|
+
const Api = self.Api;
|
|
244
|
+
const assistant = self.assistant;
|
|
245
|
+
const payload = self.payload;
|
|
246
|
+
|
|
247
|
+
// Libraries
|
|
248
|
+
const { admin } = self.libraries;
|
|
249
|
+
|
|
250
|
+
// Delete the token
|
|
251
|
+
try {
|
|
252
|
+
// Delete the token
|
|
253
|
+
await admin.firestore().doc(`${PATH_NOTIFICATIONS}/${token}`).delete();
|
|
254
|
+
|
|
255
|
+
// Log
|
|
256
|
+
assistant.log(`Deleted bad token: ${token} (Reason: ${errorCode})`);
|
|
257
|
+
|
|
258
|
+
// Update response
|
|
259
|
+
payload.response.data.deleted++;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
assistant.error(`Failed to delete bad token: ${token} (Reason: ${errorCode}). Error: ${error}`);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
module.exports = Module;
|
|
@@ -127,7 +127,12 @@ Module.prototype.processTokens = async function (note, options) {
|
|
|
127
127
|
});
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
// Sending using SDK
|
|
130
131
|
// https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-multiple-devices
|
|
132
|
+
|
|
133
|
+
// Manually sending
|
|
134
|
+
// https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send
|
|
135
|
+
// https://firebase.google.com/docs/cloud-messaging/migrate-v1#provide-credentials-manually
|
|
131
136
|
Module.prototype.sendBatch = async function (batch, note, id) {
|
|
132
137
|
const self = this;
|
|
133
138
|
|
|
@@ -147,8 +152,6 @@ Module.prototype.sendBatch = async function (batch, note, id) {
|
|
|
147
152
|
assistant.log(`batch`, batch);
|
|
148
153
|
assistant.log(`note`, note);
|
|
149
154
|
|
|
150
|
-
// Send the batch
|
|
151
|
-
// const response = await admin.messaging().sendToDevice(batch, note);
|
|
152
155
|
try {
|
|
153
156
|
const response = await admin.messaging().sendMulticast({
|
|
154
157
|
tokens: batch,
|
|
@@ -177,6 +180,25 @@ Module.prototype.sendBatch = async function (batch, note, id) {
|
|
|
177
180
|
assistant.error(`🚩🚩🚩🚩🚩 333 error ${id}`, e);
|
|
178
181
|
}
|
|
179
182
|
|
|
183
|
+
try {
|
|
184
|
+
const messages = batch.map(token => ({
|
|
185
|
+
token: token,
|
|
186
|
+
notification: {
|
|
187
|
+
title: 'Your Notification Title',
|
|
188
|
+
body: 'This is the notification message.'
|
|
189
|
+
},
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
const response = await admin.messaging().sendEach(messages);
|
|
193
|
+
|
|
194
|
+
assistant.log(`🚩🚩🚩🚩🚩 444 response ${id}`, response); // FLAG
|
|
195
|
+
} catch (e) {
|
|
196
|
+
assistant.error(`🚩🚩🚩🚩🚩 444 error ${id}`, e);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Send the batch
|
|
200
|
+
const response = await admin.messaging().sendToDevice(batch, note);
|
|
201
|
+
|
|
180
202
|
// Log
|
|
181
203
|
assistant.log(`Sent batch ID: ${id}, Success: ${response.successCount}, Failures: ${response.failureCount}`);
|
|
182
204
|
|
package/src/manager/index.js
CHANGED
|
@@ -152,6 +152,9 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
152
152
|
? self.assistant.meta.environment
|
|
153
153
|
: process.env.ENVIRONMENT;
|
|
154
154
|
|
|
155
|
+
// Set BEM env variables
|
|
156
|
+
process.env.BEM_FUNCTIONS_URL = self.project.functionsUrl;
|
|
157
|
+
|
|
155
158
|
// Use the working Firebase logger that they disabled for whatever reason
|
|
156
159
|
if (
|
|
157
160
|
process.env.GCLOUD_PROJECT
|