@blackcode_sa/metaestetics-api 1.6.15 → 1.6.16

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.
@@ -142,6 +142,64 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
142
142
  return UserRole2;
143
143
  })(UserRole || {});
144
144
 
145
+ // src/admin/logger/index.ts
146
+ var firebaseFunctionsLogger;
147
+ try {
148
+ firebaseFunctionsLogger = require("firebase-functions/logger");
149
+ require("firebase-functions/logger/compat");
150
+ } catch (e) {
151
+ }
152
+ var Logger = class {
153
+ /**
154
+ * Log an error message
155
+ * @param message Message to log
156
+ * @param data Optional data to include
157
+ */
158
+ static error(message, data) {
159
+ if (firebaseFunctionsLogger) {
160
+ firebaseFunctionsLogger.error(message, data);
161
+ } else {
162
+ console.error(message, data !== void 0 ? data : "");
163
+ }
164
+ }
165
+ /**
166
+ * Log a warning message
167
+ * @param message Message to log
168
+ * @param data Optional data to include
169
+ */
170
+ static warn(message, data) {
171
+ if (firebaseFunctionsLogger) {
172
+ firebaseFunctionsLogger.warn(message, data);
173
+ } else {
174
+ console.warn(message, data !== void 0 ? data : "");
175
+ }
176
+ }
177
+ /**
178
+ * Log an info message
179
+ * @param message Message to log
180
+ * @param data Optional data to include
181
+ */
182
+ static info(message, data) {
183
+ if (firebaseFunctionsLogger) {
184
+ firebaseFunctionsLogger.info(message, data);
185
+ } else {
186
+ console.info(message, data !== void 0 ? data : "");
187
+ }
188
+ }
189
+ /**
190
+ * Log a debug message
191
+ * @param message Message to log
192
+ * @param data Optional data to include
193
+ */
194
+ static debug(message, data) {
195
+ if (firebaseFunctionsLogger) {
196
+ firebaseFunctionsLogger.debug(message, data);
197
+ } else {
198
+ console.debug(message, data !== void 0 ? data : "");
199
+ }
200
+ }
201
+ };
202
+
145
203
  // src/admin/notifications/notifications.admin.ts
146
204
  var NotificationsAdmin = class {
147
205
  constructor(firestore12) {
@@ -171,7 +229,18 @@ var NotificationsAdmin = class {
171
229
  * Priprema Expo poruku za slanje
172
230
  */
173
231
  prepareExpoMessage(notification) {
174
- return notification.notificationTokens.filter((token) => import_expo_server_sdk.Expo.isExpoPushToken(token)).map((token) => ({
232
+ const validTokens = notification.notificationTokens.filter(
233
+ (token) => import_expo_server_sdk.Expo.isExpoPushToken(token)
234
+ );
235
+ Logger.info(
236
+ `[NotificationsAdmin] Preparing Expo messages for notification ${notification.id}`,
237
+ {
238
+ totalTokens: notification.notificationTokens.length,
239
+ validTokens: validTokens.length,
240
+ invalidTokensCount: notification.notificationTokens.length - validTokens.length
241
+ }
242
+ );
243
+ return validTokens.map((token) => ({
175
244
  to: token,
176
245
  sound: "default",
177
246
  title: notification.title,
@@ -203,45 +272,94 @@ var NotificationsAdmin = class {
203
272
  * Šalje notifikaciju kroz Expo servis sa boljim error handlingom
204
273
  */
205
274
  async sendPushNotification(notification) {
275
+ var _a;
206
276
  try {
277
+ Logger.info(
278
+ `[NotificationsAdmin] Processing notification ${notification.id} for sending`,
279
+ {
280
+ userId: notification.userId,
281
+ tokenCount: ((_a = notification.notificationTokens) == null ? void 0 : _a.length) || 0,
282
+ type: notification.notificationType
283
+ }
284
+ );
207
285
  const messages = this.prepareExpoMessage(notification);
208
286
  if (messages.length === 0) {
287
+ const errorMsg = "No valid notification tokens found";
288
+ Logger.error(
289
+ `[NotificationsAdmin] ${errorMsg} for notification ${notification.id}`
290
+ );
209
291
  await this.updateNotificationStatus(
210
292
  notification.id,
211
293
  "failed" /* FAILED */,
212
- "No valid notification tokens found"
294
+ errorMsg
213
295
  );
214
296
  return false;
215
297
  }
216
298
  const chunks = this.expo.chunkPushNotifications(messages);
299
+ Logger.info(
300
+ `[NotificationsAdmin] Sending ${messages.length} messages in ${chunks.length} chunks for notification ${notification.id}`
301
+ );
217
302
  const tickets = [];
218
- for (const chunk of chunks) {
303
+ for (let i = 0; i < chunks.length; i++) {
304
+ const chunk = chunks[i];
219
305
  try {
306
+ Logger.info(
307
+ `[NotificationsAdmin] Sending chunk ${i + 1}/${chunks.length} with ${chunk.length} messages`
308
+ );
220
309
  const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
310
+ Logger.info(
311
+ `[NotificationsAdmin] Received ${ticketChunk.length} tickets for chunk ${i + 1}`
312
+ );
221
313
  tickets.push(ticketChunk);
222
314
  } catch (error) {
223
- console.error(`Chunk sending error:`, error);
315
+ Logger.error(
316
+ `[NotificationsAdmin] Chunk ${i + 1} sending error:`,
317
+ error
318
+ );
224
319
  throw error;
225
320
  }
226
321
  }
227
322
  let hasErrors = false;
228
323
  const errors = [];
229
- tickets.flat().forEach((ticket, index) => {
324
+ const ticketsFlat = tickets.flat();
325
+ const ticketResults = {
326
+ total: ticketsFlat.length,
327
+ success: 0,
328
+ error: 0,
329
+ errorDetails: {}
330
+ };
331
+ ticketsFlat.forEach((ticket, index) => {
230
332
  if (ticket.status === "error") {
231
333
  hasErrors = true;
232
- errors.push(
233
- `Token ${notification.notificationTokens[index]}: ${ticket.message}`
234
- );
334
+ ticketResults.error++;
335
+ const errorMessage = ticket.message || "Unknown error";
336
+ ticketResults.errorDetails[errorMessage] = (ticketResults.errorDetails[errorMessage] || 0) + 1;
337
+ const tokenInfo = index < notification.notificationTokens.length ? `Token ${notification.notificationTokens[index]}` : `Token at index ${index}`;
338
+ errors.push(`${tokenInfo}: ${errorMessage}`);
339
+ } else {
340
+ ticketResults.success++;
235
341
  }
236
342
  });
343
+ Logger.info(
344
+ `[NotificationsAdmin] Ticket results for notification ${notification.id}`,
345
+ ticketResults
346
+ );
237
347
  if (hasErrors) {
348
+ const errorSummary = errors.join("; ");
349
+ Logger.warn(
350
+ `[NotificationsAdmin] Partial success or errors in notification ${notification.id}`,
351
+ { errorCount: errors.length, errorSummary }
352
+ );
238
353
  await this.updateNotificationStatus(
239
354
  notification.id,
240
- "partialSuccess" /* PARTIAL_SUCCESS */,
241
- errors.join("; ")
355
+ ticketResults.success > 0 ? "partialSuccess" /* PARTIAL_SUCCESS */ : "failed" /* FAILED */,
356
+ errorSummary
242
357
  );
243
- return false;
358
+ return ticketResults.success > 0;
244
359
  }
360
+ Logger.info(
361
+ `[NotificationsAdmin] Successfully sent notification ${notification.id} to all recipients`
362
+ );
245
363
  await this.updateNotificationStatus(
246
364
  notification.id,
247
365
  "sent" /* SENT */
@@ -249,7 +367,10 @@ var NotificationsAdmin = class {
249
367
  return true;
250
368
  } catch (error) {
251
369
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
252
- console.error("Gre\u0161ka pri slanju notifikacije:", error);
370
+ Logger.error(
371
+ `[NotificationsAdmin] Critical error sending notification ${notification.id}:`,
372
+ error
373
+ );
253
374
  await this.updateNotificationStatus(
254
375
  notification.id,
255
376
  "failed" /* FAILED */,
@@ -263,13 +384,28 @@ var NotificationsAdmin = class {
263
384
  */
264
385
  async processPendingNotifications(batchSize = 100) {
265
386
  const now = admin.firestore.Timestamp.now();
387
+ Logger.info(
388
+ `[NotificationsAdmin] Starting to process pending notifications with batch size ${batchSize}`
389
+ );
266
390
  const pendingNotifications = await this.db.collection("notifications").where("status", "==", "pending" /* PENDING */).where("notificationTime", "<=", now).limit(batchSize).get();
391
+ Logger.info(
392
+ `[NotificationsAdmin] Found ${pendingNotifications.size} pending notifications to process`
393
+ );
394
+ if (pendingNotifications.empty) {
395
+ Logger.info(
396
+ "[NotificationsAdmin] No pending notifications found to process"
397
+ );
398
+ return;
399
+ }
267
400
  const results = await Promise.allSettled(
268
401
  pendingNotifications.docs.map(async (doc) => {
269
402
  const notification = {
270
403
  id: doc.id,
271
404
  ...doc.data()
272
405
  };
406
+ Logger.info(
407
+ `[NotificationsAdmin] Processing notification ${notification.id} of type ${notification.notificationType}`
408
+ );
273
409
  return this.sendPushNotification(notification);
274
410
  })
275
411
  );
@@ -279,8 +415,8 @@ var NotificationsAdmin = class {
279
415
  const failed = results.filter(
280
416
  (r) => r.status === "rejected" || r.status === "fulfilled" && !r.value
281
417
  ).length;
282
- console.log(
283
- `Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
418
+ Logger.info(
419
+ `[NotificationsAdmin] Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
284
420
  );
285
421
  }
286
422
  /**
@@ -289,6 +425,10 @@ var NotificationsAdmin = class {
289
425
  async cleanupOldNotifications(daysOld = 30, batchSize = 500) {
290
426
  const cutoffDate = /* @__PURE__ */ new Date();
291
427
  cutoffDate.setDate(cutoffDate.getDate() - daysOld);
428
+ Logger.info(
429
+ `[NotificationsAdmin] Starting cleanup of notifications older than ${daysOld} days`
430
+ );
431
+ let totalDeleted = 0;
292
432
  while (true) {
293
433
  const oldNotifications = await this.db.collection("notifications").where(
294
434
  "createdAt",
@@ -296,6 +436,9 @@ var NotificationsAdmin = class {
296
436
  admin.firestore.Timestamp.fromDate(cutoffDate)
297
437
  ).limit(batchSize).get();
298
438
  if (oldNotifications.empty) {
439
+ Logger.info(
440
+ `[NotificationsAdmin] No more old notifications to delete. Total deleted: ${totalDeleted}`
441
+ );
299
442
  break;
300
443
  }
301
444
  const batch = this.db.batch();
@@ -303,8 +446,9 @@ var NotificationsAdmin = class {
303
446
  batch.delete(doc.ref);
304
447
  });
305
448
  await batch.commit();
306
- console.log(
307
- `Deleted batch of ${oldNotifications.size} old notifications`
449
+ totalDeleted += oldNotifications.size;
450
+ Logger.info(
451
+ `[NotificationsAdmin] Deleted batch of ${oldNotifications.size} old notifications. Running total: ${totalDeleted}`
308
452
  );
309
453
  }
310
454
  }
@@ -2436,66 +2580,6 @@ var PatientRequirementsAdminService = class {
2436
2580
 
2437
2581
  // src/admin/calendar/calendar.admin.service.ts
2438
2582
  var admin8 = __toESM(require("firebase-admin"));
2439
-
2440
- // src/admin/logger/index.ts
2441
- var firebaseFunctionsLogger;
2442
- try {
2443
- firebaseFunctionsLogger = require("firebase-functions/logger");
2444
- require("firebase-functions/logger/compat");
2445
- } catch (e) {
2446
- }
2447
- var Logger = class {
2448
- /**
2449
- * Log an error message
2450
- * @param message Message to log
2451
- * @param data Optional data to include
2452
- */
2453
- static error(message, data) {
2454
- if (firebaseFunctionsLogger) {
2455
- firebaseFunctionsLogger.error(message, data);
2456
- } else {
2457
- console.error(message, data !== void 0 ? data : "");
2458
- }
2459
- }
2460
- /**
2461
- * Log a warning message
2462
- * @param message Message to log
2463
- * @param data Optional data to include
2464
- */
2465
- static warn(message, data) {
2466
- if (firebaseFunctionsLogger) {
2467
- firebaseFunctionsLogger.warn(message, data);
2468
- } else {
2469
- console.warn(message, data !== void 0 ? data : "");
2470
- }
2471
- }
2472
- /**
2473
- * Log an info message
2474
- * @param message Message to log
2475
- * @param data Optional data to include
2476
- */
2477
- static info(message, data) {
2478
- if (firebaseFunctionsLogger) {
2479
- firebaseFunctionsLogger.info(message, data);
2480
- } else {
2481
- console.info(message, data !== void 0 ? data : "");
2482
- }
2483
- }
2484
- /**
2485
- * Log a debug message
2486
- * @param message Message to log
2487
- * @param data Optional data to include
2488
- */
2489
- static debug(message, data) {
2490
- if (firebaseFunctionsLogger) {
2491
- firebaseFunctionsLogger.debug(message, data);
2492
- } else {
2493
- console.debug(message, data !== void 0 ? data : "");
2494
- }
2495
- }
2496
- };
2497
-
2498
- // src/admin/calendar/calendar.admin.service.ts
2499
2583
  var CalendarAdminService = class {
2500
2584
  constructor(firestore12) {
2501
2585
  this.db = firestore12 || admin8.firestore();
@@ -87,6 +87,64 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
87
87
  return UserRole2;
88
88
  })(UserRole || {});
89
89
 
90
+ // src/admin/logger/index.ts
91
+ var firebaseFunctionsLogger;
92
+ try {
93
+ firebaseFunctionsLogger = __require("firebase-functions/logger");
94
+ __require("firebase-functions/logger/compat");
95
+ } catch (e) {
96
+ }
97
+ var Logger = class {
98
+ /**
99
+ * Log an error message
100
+ * @param message Message to log
101
+ * @param data Optional data to include
102
+ */
103
+ static error(message, data) {
104
+ if (firebaseFunctionsLogger) {
105
+ firebaseFunctionsLogger.error(message, data);
106
+ } else {
107
+ console.error(message, data !== void 0 ? data : "");
108
+ }
109
+ }
110
+ /**
111
+ * Log a warning message
112
+ * @param message Message to log
113
+ * @param data Optional data to include
114
+ */
115
+ static warn(message, data) {
116
+ if (firebaseFunctionsLogger) {
117
+ firebaseFunctionsLogger.warn(message, data);
118
+ } else {
119
+ console.warn(message, data !== void 0 ? data : "");
120
+ }
121
+ }
122
+ /**
123
+ * Log an info message
124
+ * @param message Message to log
125
+ * @param data Optional data to include
126
+ */
127
+ static info(message, data) {
128
+ if (firebaseFunctionsLogger) {
129
+ firebaseFunctionsLogger.info(message, data);
130
+ } else {
131
+ console.info(message, data !== void 0 ? data : "");
132
+ }
133
+ }
134
+ /**
135
+ * Log a debug message
136
+ * @param message Message to log
137
+ * @param data Optional data to include
138
+ */
139
+ static debug(message, data) {
140
+ if (firebaseFunctionsLogger) {
141
+ firebaseFunctionsLogger.debug(message, data);
142
+ } else {
143
+ console.debug(message, data !== void 0 ? data : "");
144
+ }
145
+ }
146
+ };
147
+
90
148
  // src/admin/notifications/notifications.admin.ts
91
149
  var NotificationsAdmin = class {
92
150
  constructor(firestore12) {
@@ -116,7 +174,18 @@ var NotificationsAdmin = class {
116
174
  * Priprema Expo poruku za slanje
117
175
  */
118
176
  prepareExpoMessage(notification) {
119
- return notification.notificationTokens.filter((token) => Expo.isExpoPushToken(token)).map((token) => ({
177
+ const validTokens = notification.notificationTokens.filter(
178
+ (token) => Expo.isExpoPushToken(token)
179
+ );
180
+ Logger.info(
181
+ `[NotificationsAdmin] Preparing Expo messages for notification ${notification.id}`,
182
+ {
183
+ totalTokens: notification.notificationTokens.length,
184
+ validTokens: validTokens.length,
185
+ invalidTokensCount: notification.notificationTokens.length - validTokens.length
186
+ }
187
+ );
188
+ return validTokens.map((token) => ({
120
189
  to: token,
121
190
  sound: "default",
122
191
  title: notification.title,
@@ -148,45 +217,94 @@ var NotificationsAdmin = class {
148
217
  * Šalje notifikaciju kroz Expo servis sa boljim error handlingom
149
218
  */
150
219
  async sendPushNotification(notification) {
220
+ var _a;
151
221
  try {
222
+ Logger.info(
223
+ `[NotificationsAdmin] Processing notification ${notification.id} for sending`,
224
+ {
225
+ userId: notification.userId,
226
+ tokenCount: ((_a = notification.notificationTokens) == null ? void 0 : _a.length) || 0,
227
+ type: notification.notificationType
228
+ }
229
+ );
152
230
  const messages = this.prepareExpoMessage(notification);
153
231
  if (messages.length === 0) {
232
+ const errorMsg = "No valid notification tokens found";
233
+ Logger.error(
234
+ `[NotificationsAdmin] ${errorMsg} for notification ${notification.id}`
235
+ );
154
236
  await this.updateNotificationStatus(
155
237
  notification.id,
156
238
  "failed" /* FAILED */,
157
- "No valid notification tokens found"
239
+ errorMsg
158
240
  );
159
241
  return false;
160
242
  }
161
243
  const chunks = this.expo.chunkPushNotifications(messages);
244
+ Logger.info(
245
+ `[NotificationsAdmin] Sending ${messages.length} messages in ${chunks.length} chunks for notification ${notification.id}`
246
+ );
162
247
  const tickets = [];
163
- for (const chunk of chunks) {
248
+ for (let i = 0; i < chunks.length; i++) {
249
+ const chunk = chunks[i];
164
250
  try {
251
+ Logger.info(
252
+ `[NotificationsAdmin] Sending chunk ${i + 1}/${chunks.length} with ${chunk.length} messages`
253
+ );
165
254
  const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
255
+ Logger.info(
256
+ `[NotificationsAdmin] Received ${ticketChunk.length} tickets for chunk ${i + 1}`
257
+ );
166
258
  tickets.push(ticketChunk);
167
259
  } catch (error) {
168
- console.error(`Chunk sending error:`, error);
260
+ Logger.error(
261
+ `[NotificationsAdmin] Chunk ${i + 1} sending error:`,
262
+ error
263
+ );
169
264
  throw error;
170
265
  }
171
266
  }
172
267
  let hasErrors = false;
173
268
  const errors = [];
174
- tickets.flat().forEach((ticket, index) => {
269
+ const ticketsFlat = tickets.flat();
270
+ const ticketResults = {
271
+ total: ticketsFlat.length,
272
+ success: 0,
273
+ error: 0,
274
+ errorDetails: {}
275
+ };
276
+ ticketsFlat.forEach((ticket, index) => {
175
277
  if (ticket.status === "error") {
176
278
  hasErrors = true;
177
- errors.push(
178
- `Token ${notification.notificationTokens[index]}: ${ticket.message}`
179
- );
279
+ ticketResults.error++;
280
+ const errorMessage = ticket.message || "Unknown error";
281
+ ticketResults.errorDetails[errorMessage] = (ticketResults.errorDetails[errorMessage] || 0) + 1;
282
+ const tokenInfo = index < notification.notificationTokens.length ? `Token ${notification.notificationTokens[index]}` : `Token at index ${index}`;
283
+ errors.push(`${tokenInfo}: ${errorMessage}`);
284
+ } else {
285
+ ticketResults.success++;
180
286
  }
181
287
  });
288
+ Logger.info(
289
+ `[NotificationsAdmin] Ticket results for notification ${notification.id}`,
290
+ ticketResults
291
+ );
182
292
  if (hasErrors) {
293
+ const errorSummary = errors.join("; ");
294
+ Logger.warn(
295
+ `[NotificationsAdmin] Partial success or errors in notification ${notification.id}`,
296
+ { errorCount: errors.length, errorSummary }
297
+ );
183
298
  await this.updateNotificationStatus(
184
299
  notification.id,
185
- "partialSuccess" /* PARTIAL_SUCCESS */,
186
- errors.join("; ")
300
+ ticketResults.success > 0 ? "partialSuccess" /* PARTIAL_SUCCESS */ : "failed" /* FAILED */,
301
+ errorSummary
187
302
  );
188
- return false;
303
+ return ticketResults.success > 0;
189
304
  }
305
+ Logger.info(
306
+ `[NotificationsAdmin] Successfully sent notification ${notification.id} to all recipients`
307
+ );
190
308
  await this.updateNotificationStatus(
191
309
  notification.id,
192
310
  "sent" /* SENT */
@@ -194,7 +312,10 @@ var NotificationsAdmin = class {
194
312
  return true;
195
313
  } catch (error) {
196
314
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
197
- console.error("Gre\u0161ka pri slanju notifikacije:", error);
315
+ Logger.error(
316
+ `[NotificationsAdmin] Critical error sending notification ${notification.id}:`,
317
+ error
318
+ );
198
319
  await this.updateNotificationStatus(
199
320
  notification.id,
200
321
  "failed" /* FAILED */,
@@ -208,13 +329,28 @@ var NotificationsAdmin = class {
208
329
  */
209
330
  async processPendingNotifications(batchSize = 100) {
210
331
  const now = admin.firestore.Timestamp.now();
332
+ Logger.info(
333
+ `[NotificationsAdmin] Starting to process pending notifications with batch size ${batchSize}`
334
+ );
211
335
  const pendingNotifications = await this.db.collection("notifications").where("status", "==", "pending" /* PENDING */).where("notificationTime", "<=", now).limit(batchSize).get();
336
+ Logger.info(
337
+ `[NotificationsAdmin] Found ${pendingNotifications.size} pending notifications to process`
338
+ );
339
+ if (pendingNotifications.empty) {
340
+ Logger.info(
341
+ "[NotificationsAdmin] No pending notifications found to process"
342
+ );
343
+ return;
344
+ }
212
345
  const results = await Promise.allSettled(
213
346
  pendingNotifications.docs.map(async (doc) => {
214
347
  const notification = {
215
348
  id: doc.id,
216
349
  ...doc.data()
217
350
  };
351
+ Logger.info(
352
+ `[NotificationsAdmin] Processing notification ${notification.id} of type ${notification.notificationType}`
353
+ );
218
354
  return this.sendPushNotification(notification);
219
355
  })
220
356
  );
@@ -224,8 +360,8 @@ var NotificationsAdmin = class {
224
360
  const failed = results.filter(
225
361
  (r) => r.status === "rejected" || r.status === "fulfilled" && !r.value
226
362
  ).length;
227
- console.log(
228
- `Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
363
+ Logger.info(
364
+ `[NotificationsAdmin] Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
229
365
  );
230
366
  }
231
367
  /**
@@ -234,6 +370,10 @@ var NotificationsAdmin = class {
234
370
  async cleanupOldNotifications(daysOld = 30, batchSize = 500) {
235
371
  const cutoffDate = /* @__PURE__ */ new Date();
236
372
  cutoffDate.setDate(cutoffDate.getDate() - daysOld);
373
+ Logger.info(
374
+ `[NotificationsAdmin] Starting cleanup of notifications older than ${daysOld} days`
375
+ );
376
+ let totalDeleted = 0;
237
377
  while (true) {
238
378
  const oldNotifications = await this.db.collection("notifications").where(
239
379
  "createdAt",
@@ -241,6 +381,9 @@ var NotificationsAdmin = class {
241
381
  admin.firestore.Timestamp.fromDate(cutoffDate)
242
382
  ).limit(batchSize).get();
243
383
  if (oldNotifications.empty) {
384
+ Logger.info(
385
+ `[NotificationsAdmin] No more old notifications to delete. Total deleted: ${totalDeleted}`
386
+ );
244
387
  break;
245
388
  }
246
389
  const batch = this.db.batch();
@@ -248,8 +391,9 @@ var NotificationsAdmin = class {
248
391
  batch.delete(doc.ref);
249
392
  });
250
393
  await batch.commit();
251
- console.log(
252
- `Deleted batch of ${oldNotifications.size} old notifications`
394
+ totalDeleted += oldNotifications.size;
395
+ Logger.info(
396
+ `[NotificationsAdmin] Deleted batch of ${oldNotifications.size} old notifications. Running total: ${totalDeleted}`
253
397
  );
254
398
  }
255
399
  }
@@ -2381,66 +2525,6 @@ var PatientRequirementsAdminService = class {
2381
2525
 
2382
2526
  // src/admin/calendar/calendar.admin.service.ts
2383
2527
  import * as admin8 from "firebase-admin";
2384
-
2385
- // src/admin/logger/index.ts
2386
- var firebaseFunctionsLogger;
2387
- try {
2388
- firebaseFunctionsLogger = __require("firebase-functions/logger");
2389
- __require("firebase-functions/logger/compat");
2390
- } catch (e) {
2391
- }
2392
- var Logger = class {
2393
- /**
2394
- * Log an error message
2395
- * @param message Message to log
2396
- * @param data Optional data to include
2397
- */
2398
- static error(message, data) {
2399
- if (firebaseFunctionsLogger) {
2400
- firebaseFunctionsLogger.error(message, data);
2401
- } else {
2402
- console.error(message, data !== void 0 ? data : "");
2403
- }
2404
- }
2405
- /**
2406
- * Log a warning message
2407
- * @param message Message to log
2408
- * @param data Optional data to include
2409
- */
2410
- static warn(message, data) {
2411
- if (firebaseFunctionsLogger) {
2412
- firebaseFunctionsLogger.warn(message, data);
2413
- } else {
2414
- console.warn(message, data !== void 0 ? data : "");
2415
- }
2416
- }
2417
- /**
2418
- * Log an info message
2419
- * @param message Message to log
2420
- * @param data Optional data to include
2421
- */
2422
- static info(message, data) {
2423
- if (firebaseFunctionsLogger) {
2424
- firebaseFunctionsLogger.info(message, data);
2425
- } else {
2426
- console.info(message, data !== void 0 ? data : "");
2427
- }
2428
- }
2429
- /**
2430
- * Log a debug message
2431
- * @param message Message to log
2432
- * @param data Optional data to include
2433
- */
2434
- static debug(message, data) {
2435
- if (firebaseFunctionsLogger) {
2436
- firebaseFunctionsLogger.debug(message, data);
2437
- } else {
2438
- console.debug(message, data !== void 0 ? data : "");
2439
- }
2440
- }
2441
- };
2442
-
2443
- // src/admin/calendar/calendar.admin.service.ts
2444
2528
  var CalendarAdminService = class {
2445
2529
  constructor(firestore12) {
2446
2530
  this.db = firestore12 || admin8.firestore();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blackcode_sa/metaestetics-api",
3
3
  "private": false,
4
- "version": "1.6.15",
4
+ "version": "1.6.16",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -9,6 +9,7 @@ import { Appointment, PaymentStatus } from "../../types/appointment";
9
9
  import { UserRole } from "../../types";
10
10
  import { Timestamp as FirebaseClientTimestamp } from "@firebase/firestore";
11
11
  import { TimestampUtils } from "../../utils/TimestampUtils";
12
+ import { Logger } from "../logger";
12
13
 
13
14
  export class NotificationsAdmin {
14
15
  private expo: Expo;
@@ -46,19 +47,31 @@ export class NotificationsAdmin {
46
47
  * Priprema Expo poruku za slanje
47
48
  */
48
49
  private prepareExpoMessage(notification: Notification): ExpoPushMessage[] {
49
- return notification.notificationTokens
50
- .filter((token) => Expo.isExpoPushToken(token))
51
- .map((token) => ({
52
- to: token,
53
- sound: "default",
54
- title: notification.title,
55
- body: notification.body,
56
- data: {
57
- notificationId: notification.id,
58
- notificationType: notification.notificationType,
59
- userId: notification.userId,
60
- },
61
- }));
50
+ const validTokens = notification.notificationTokens.filter((token) =>
51
+ Expo.isExpoPushToken(token)
52
+ );
53
+
54
+ Logger.info(
55
+ `[NotificationsAdmin] Preparing Expo messages for notification ${notification.id}`,
56
+ {
57
+ totalTokens: notification.notificationTokens.length,
58
+ validTokens: validTokens.length,
59
+ invalidTokensCount:
60
+ notification.notificationTokens.length - validTokens.length,
61
+ }
62
+ );
63
+
64
+ return validTokens.map((token) => ({
65
+ to: token,
66
+ sound: "default",
67
+ title: notification.title,
68
+ body: notification.body,
69
+ data: {
70
+ notificationId: notification.id,
71
+ notificationType: notification.notificationType,
72
+ userId: notification.userId,
73
+ },
74
+ }));
62
75
  }
63
76
 
64
77
  /**
@@ -93,27 +106,58 @@ export class NotificationsAdmin {
93
106
  */
94
107
  async sendPushNotification(notification: Notification): Promise<boolean> {
95
108
  try {
109
+ Logger.info(
110
+ `[NotificationsAdmin] Processing notification ${notification.id} for sending`,
111
+ {
112
+ userId: notification.userId,
113
+ tokenCount: notification.notificationTokens?.length || 0,
114
+ type: notification.notificationType,
115
+ }
116
+ );
117
+
96
118
  const messages = this.prepareExpoMessage(notification);
97
119
 
98
120
  if (messages.length === 0) {
121
+ const errorMsg = "No valid notification tokens found";
122
+ Logger.error(
123
+ `[NotificationsAdmin] ${errorMsg} for notification ${notification.id}`
124
+ );
99
125
  await this.updateNotificationStatus(
100
126
  notification.id!,
101
127
  NotificationStatus.FAILED,
102
- "No valid notification tokens found"
128
+ errorMsg
103
129
  );
104
130
  return false;
105
131
  }
106
132
 
107
133
  const chunks = this.expo.chunkPushNotifications(messages);
134
+ Logger.info(
135
+ `[NotificationsAdmin] Sending ${messages.length} messages in ${chunks.length} chunks for notification ${notification.id}`
136
+ );
137
+
108
138
  const tickets: ExpoPushTicket[][] = [];
109
139
 
110
140
  // Šaljemo sve chunks
111
- for (const chunk of chunks) {
141
+ for (let i = 0; i < chunks.length; i++) {
142
+ const chunk = chunks[i];
112
143
  try {
144
+ Logger.info(
145
+ `[NotificationsAdmin] Sending chunk ${i + 1}/${
146
+ chunks.length
147
+ } with ${chunk.length} messages`
148
+ );
113
149
  const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
150
+ Logger.info(
151
+ `[NotificationsAdmin] Received ${
152
+ ticketChunk.length
153
+ } tickets for chunk ${i + 1}`
154
+ );
114
155
  tickets.push(ticketChunk);
115
156
  } catch (error) {
116
- console.error(`Chunk sending error:`, error);
157
+ Logger.error(
158
+ `[NotificationsAdmin] Chunk ${i + 1} sending error:`,
159
+ error
160
+ );
117
161
  throw error;
118
162
  }
119
163
  }
@@ -121,25 +165,62 @@ export class NotificationsAdmin {
121
165
  // Proveravamo rezultate
122
166
  let hasErrors = false;
123
167
  const errors: string[] = [];
168
+ const ticketsFlat = tickets.flat();
124
169
 
125
- tickets.flat().forEach((ticket, index) => {
170
+ // Log detailed ticket information
171
+ const ticketResults = {
172
+ total: ticketsFlat.length,
173
+ success: 0,
174
+ error: 0,
175
+ errorDetails: {} as Record<string, number>,
176
+ };
177
+
178
+ ticketsFlat.forEach((ticket, index) => {
126
179
  if (ticket.status === "error") {
127
180
  hasErrors = true;
128
- errors.push(
129
- `Token ${notification.notificationTokens[index]}: ${ticket.message}`
130
- );
181
+ ticketResults.error++;
182
+
183
+ // Count each error type
184
+ const errorMessage = ticket.message || "Unknown error";
185
+ ticketResults.errorDetails[errorMessage] =
186
+ (ticketResults.errorDetails[errorMessage] || 0) + 1;
187
+
188
+ const tokenInfo =
189
+ index < notification.notificationTokens.length
190
+ ? `Token ${notification.notificationTokens[index]}`
191
+ : `Token at index ${index}`;
192
+
193
+ errors.push(`${tokenInfo}: ${errorMessage}`);
194
+ } else {
195
+ ticketResults.success++;
131
196
  }
132
197
  });
133
198
 
199
+ Logger.info(
200
+ `[NotificationsAdmin] Ticket results for notification ${notification.id}`,
201
+ ticketResults
202
+ );
203
+
134
204
  if (hasErrors) {
205
+ const errorSummary = errors.join("; ");
206
+ Logger.warn(
207
+ `[NotificationsAdmin] Partial success or errors in notification ${notification.id}`,
208
+ { errorCount: errors.length, errorSummary }
209
+ );
210
+
135
211
  await this.updateNotificationStatus(
136
212
  notification.id!,
137
- NotificationStatus.PARTIAL_SUCCESS,
138
- errors.join("; ")
213
+ ticketResults.success > 0
214
+ ? NotificationStatus.PARTIAL_SUCCESS
215
+ : NotificationStatus.FAILED,
216
+ errorSummary
139
217
  );
140
- return false;
218
+ return ticketResults.success > 0;
141
219
  }
142
220
 
221
+ Logger.info(
222
+ `[NotificationsAdmin] Successfully sent notification ${notification.id} to all recipients`
223
+ );
143
224
  await this.updateNotificationStatus(
144
225
  notification.id!,
145
226
  NotificationStatus.SENT
@@ -148,7 +229,10 @@ export class NotificationsAdmin {
148
229
  } catch (error) {
149
230
  const errorMessage =
150
231
  error instanceof Error ? error.message : "Unknown error";
151
- console.error("Greška pri slanju notifikacije:", error);
232
+ Logger.error(
233
+ `[NotificationsAdmin] Critical error sending notification ${notification.id}:`,
234
+ error
235
+ );
152
236
  await this.updateNotificationStatus(
153
237
  notification.id!,
154
238
  NotificationStatus.FAILED,
@@ -164,6 +248,10 @@ export class NotificationsAdmin {
164
248
  async processPendingNotifications(batchSize: number = 100): Promise<void> {
165
249
  const now = admin.firestore.Timestamp.now();
166
250
 
251
+ Logger.info(
252
+ `[NotificationsAdmin] Starting to process pending notifications with batch size ${batchSize}`
253
+ );
254
+
167
255
  const pendingNotifications = await this.db
168
256
  .collection("notifications")
169
257
  .where("status", "==", NotificationStatus.PENDING)
@@ -171,12 +259,27 @@ export class NotificationsAdmin {
171
259
  .limit(batchSize)
172
260
  .get();
173
261
 
262
+ Logger.info(
263
+ `[NotificationsAdmin] Found ${pendingNotifications.size} pending notifications to process`
264
+ );
265
+
266
+ if (pendingNotifications.empty) {
267
+ Logger.info(
268
+ "[NotificationsAdmin] No pending notifications found to process"
269
+ );
270
+ return;
271
+ }
272
+
174
273
  const results = await Promise.allSettled(
175
274
  pendingNotifications.docs.map(async (doc) => {
176
275
  const notification = {
177
276
  id: doc.id,
178
277
  ...doc.data(),
179
278
  } as Notification;
279
+
280
+ Logger.info(
281
+ `[NotificationsAdmin] Processing notification ${notification.id} of type ${notification.notificationType}`
282
+ );
180
283
  return this.sendPushNotification(notification);
181
284
  })
182
285
  );
@@ -189,8 +292,8 @@ export class NotificationsAdmin {
189
292
  (r) => r.status === "rejected" || (r.status === "fulfilled" && !r.value)
190
293
  ).length;
191
294
 
192
- console.log(
193
- `Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
295
+ Logger.info(
296
+ `[NotificationsAdmin] Processed ${results.length} notifications: ${successful} successful, ${failed} failed`
194
297
  );
195
298
  }
196
299
 
@@ -204,6 +307,11 @@ export class NotificationsAdmin {
204
307
  const cutoffDate = new Date();
205
308
  cutoffDate.setDate(cutoffDate.getDate() - daysOld);
206
309
 
310
+ Logger.info(
311
+ `[NotificationsAdmin] Starting cleanup of notifications older than ${daysOld} days`
312
+ );
313
+
314
+ let totalDeleted = 0;
207
315
  while (true) {
208
316
  const oldNotifications = await this.db
209
317
  .collection("notifications")
@@ -216,6 +324,9 @@ export class NotificationsAdmin {
216
324
  .get();
217
325
 
218
326
  if (oldNotifications.empty) {
327
+ Logger.info(
328
+ `[NotificationsAdmin] No more old notifications to delete. Total deleted: ${totalDeleted}`
329
+ );
219
330
  break;
220
331
  }
221
332
 
@@ -225,8 +336,9 @@ export class NotificationsAdmin {
225
336
  });
226
337
 
227
338
  await batch.commit();
228
- console.log(
229
- `Deleted batch of ${oldNotifications.size} old notifications`
339
+ totalDeleted += oldNotifications.size;
340
+ Logger.info(
341
+ `[NotificationsAdmin] Deleted batch of ${oldNotifications.size} old notifications. Running total: ${totalDeleted}`
230
342
  );
231
343
  }
232
344
  }