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.7",
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.0",
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
 
@@ -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