90dc-core 1.16.2 → 1.16.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"FirebasePushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FirebasePushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@
12
12
  * - Built-in retry logic and error handling
13
13
  * - Message delivery tracking
14
14
  * - Type-safe API
15
- */ import * as admin from "firebase-admin";
15
+ */ import admin from "firebase-admin";
16
16
  import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
17
17
  import { CommonSchemas } from "../config/ConfigValidator.js";
18
18
  import { ExternalAPIError } from "../Errors/AppError.js";
@@ -55,22 +55,13 @@ export class FirebasePushNotificationClient {
55
55
  const logger = Log.getInstance().extend("firebase-push");
56
56
  const secretName = "projects/1033066542238/secrets/firebase_new_key/versions/latest";
57
57
  try {
58
- logger.info("Loading Firebase service account from Secret Manager", {
59
- secretName
60
- });
61
58
  // Check if FIREBASE_SERVICE_ACCOUNT_KEY env variable is set (for local development)
62
59
  const localKeyPath = process.env.FIREBASE_SERVICE_ACCOUNT_KEY;
63
60
  if (localKeyPath) {
64
- logger.info("Using local service account file", {
65
- path: localKeyPath
66
- });
67
61
  const fs = await import("node:fs/promises");
68
62
  const serviceAccountJson = await fs.readFile(localKeyPath, "utf-8");
69
63
  const serviceAccount = JSON.parse(serviceAccountJson);
70
- logger.info("Firebase service account loaded from file", {
71
- projectId: serviceAccount.project_id,
72
- clientEmail: serviceAccount.client_email
73
- });
64
+ logger.info("Firebase service account loaded from local file");
74
65
  return serviceAccount;
75
66
  }
76
67
  // Production: Load from Secret Manager
@@ -83,10 +74,7 @@ export class FirebasePushNotificationClient {
83
74
  }
84
75
  const serviceAccountJson = version.payload.data.toString();
85
76
  const serviceAccount = JSON.parse(serviceAccountJson);
86
- logger.info("Firebase service account loaded successfully from Secret Manager", {
87
- projectId: serviceAccount.project_id,
88
- clientEmail: serviceAccount.client_email
89
- });
77
+ logger.info("Firebase service account loaded from Secret Manager");
90
78
  return serviceAccount;
91
79
  } catch (error) {
92
80
  logger.error("Failed to load Firebase service account", {
@@ -103,7 +91,7 @@ export class FirebasePushNotificationClient {
103
91
  initializeApp() {
104
92
  try {
105
93
  // Check if app already exists
106
- if (admin.apps.length > 0) {
94
+ if (admin.apps && admin.apps.length > 0) {
107
95
  this.app = admin.app();
108
96
  this.logger.info("Using existing Firebase app");
109
97
  return;
@@ -111,20 +99,17 @@ export class FirebasePushNotificationClient {
111
99
  if (!this.serviceAccount) {
112
100
  throw new Error("Service account not loaded");
113
101
  }
114
- this.logger.info("Initializing Firebase Admin SDK", {
115
- projectId: this.serviceAccount.project_id,
116
- privateKeyLength: this.serviceAccount.private_key?.length,
117
- privateKeyStart: this.serviceAccount.private_key?.substring(0, 50)
118
- });
102
+ // Validate service account has all required fields
103
+ if (!this.serviceAccount.project_id || !this.serviceAccount.client_email || !this.serviceAccount.private_key) {
104
+ throw new Error("Invalid service account: missing required fields");
105
+ }
119
106
  this.app = admin.initializeApp({
120
107
  credential: admin.credential.cert(this.serviceAccount)
121
108
  });
122
109
  this.logger.info("Firebase Admin SDK initialized successfully");
123
110
  } catch (error) {
124
111
  this.logger.error("Failed to initialize Firebase Admin SDK", {
125
- error,
126
- errorMessage: error instanceof Error ? error.message : String(error),
127
- errorStack: error instanceof Error ? error.stack : undefined
112
+ error
128
113
  });
129
114
  throw new ExternalAPIError("Failed to initialize Firebase", `Error: ${String(error)}`);
130
115
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"sourcesContent":["/**\n * Firebase Push Notification Client\n *\n * Industry-standard implementation using Firebase Admin SDK for:\n * - iOS (APNs via FCM)\n * - Android (FCM)\n *\n * Features:\n * - Multicast messaging (up to 500 tokens per request)\n * - Topic-based messaging for efficient broadcasting\n * - Automatic token validation and cleanup\n * - Built-in retry logic and error handling\n * - Message delivery tracking\n * - Type-safe API\n */\n\nimport * as admin from \"firebase-admin\";\nimport { z } from \"zod\";\nimport { SecretManagerServiceClient } from \"@google-cloud/secret-manager\";\nimport { CommonSchemas } from \"../config/ConfigValidator.js\";\nimport { ExternalAPIError } from \"../Errors/AppError.js\";\nimport { Log } from \"../utils/Logger.js\";\nimport { DeviceTokens } from \"../dbmodels/user/DeviceTokens.js\";\nimport { PersistedUser } from \"../dbmodels/user/PersistedUser.js\";\nimport { NotificationModels } from \"../dbmodels/notifications/NotificationModels.js\";\nimport { TranslatedNotification } from \"../dbmodels/notifications/TranslatedNotification.js\";\n\ntype FirebaseConfig = z.infer<typeof CommonSchemas.pushNotification>;\n\ninterface ServiceAccount {\n type: string;\n project_id: string;\n private_key_id: string;\n private_key: string;\n client_email: string;\n client_id: string;\n auth_uri: string;\n token_uri: string;\n auth_provider_x509_cert_url: string;\n client_x509_cert_url: string;\n}\n\nexport interface NotificationPayload {\n title: string;\n body: string;\n imageUrl?: string;\n sound?: string;\n badge?: number;\n data?: Record<string, string>;\n redirectPath?: string;\n}\n\ninterface SendResult {\n successCount: number;\n failureCount: number;\n invalidTokens: string[];\n}\n\nexport class FirebasePushNotificationClient {\n private static instance: FirebasePushNotificationClient;\n private static initPromise: Promise<FirebasePushNotificationClient> | null = null;\n private logger = Log.getInstance().extend(\"firebase-push\");\n private app: admin.app.App | null = null;\n private serviceAccount: ServiceAccount | null = null;\n\n private constructor(_config: FirebaseConfig, serviceAccount: ServiceAccount) {\n // config parameter kept for backward compatibility but not used (FCM config comes from Secret Manager)\n this.serviceAccount = serviceAccount;\n this.initializeApp();\n }\n\n public static async getInstance(): Promise<FirebasePushNotificationClient> {\n if (FirebasePushNotificationClient.instance) {\n return FirebasePushNotificationClient.instance;\n }\n\n // If already initializing, wait for that to complete\n if (FirebasePushNotificationClient.initPromise) {\n return FirebasePushNotificationClient.initPromise;\n }\n\n // Start initialization\n FirebasePushNotificationClient.initPromise = (async () => {\n // Parse config (mostly for optional APNs fields, FCM comes from Secret Manager)\n const parsedConfig = CommonSchemas.pushNotification.parse(process.env);\n const serviceAccount = await FirebasePushNotificationClient.loadServiceAccount();\n FirebasePushNotificationClient.instance = new FirebasePushNotificationClient(parsedConfig, serviceAccount);\n FirebasePushNotificationClient.initPromise = null;\n return FirebasePushNotificationClient.instance;\n })();\n\n return FirebasePushNotificationClient.initPromise;\n }\n\n private static async loadServiceAccount(): Promise<ServiceAccount> {\n const logger = Log.getInstance().extend(\"firebase-push\");\n const secretName = \"projects/1033066542238/secrets/firebase_new_key/versions/latest\";\n\n try {\n logger.info(\"Loading Firebase service account from Secret Manager\", { secretName });\n\n // Check if FIREBASE_SERVICE_ACCOUNT_KEY env variable is set (for local development)\n const localKeyPath = process.env.FIREBASE_SERVICE_ACCOUNT_KEY;\n if (localKeyPath) {\n logger.info(\"Using local service account file\", { path: localKeyPath });\n const fs = await import(\"fs/promises\");\n const serviceAccountJson = await fs.readFile(localKeyPath, \"utf-8\");\n const serviceAccount = JSON.parse(serviceAccountJson) as ServiceAccount;\n\n logger.info(\"Firebase service account loaded from file\", {\n projectId: serviceAccount.project_id,\n clientEmail: serviceAccount.client_email,\n });\n\n return serviceAccount;\n }\n\n // Production: Load from Secret Manager\n const client = new SecretManagerServiceClient();\n const [version] = await client.accessSecretVersion({ name: secretName });\n\n if (!version.payload?.data) {\n throw new Error(\"Secret payload is empty\");\n }\n\n const serviceAccountJson = version.payload.data.toString();\n const serviceAccount = JSON.parse(serviceAccountJson) as ServiceAccount;\n\n logger.info(\"Firebase service account loaded successfully from Secret Manager\", {\n projectId: serviceAccount.project_id,\n clientEmail: serviceAccount.client_email,\n });\n\n return serviceAccount;\n } catch (error) {\n logger.error(\"Failed to load Firebase service account\", { error });\n throw new ExternalAPIError(\n \"Failed to load Firebase credentials\",\n `Error: ${String(error)}`\n );\n }\n }\n\n public static resetInstance(): void {\n FirebasePushNotificationClient.instance?.shutdown();\n FirebasePushNotificationClient.instance = undefined as any;\n FirebasePushNotificationClient.initPromise = null;\n }\n\n private initializeApp(): void {\n try {\n // Check if app already exists\n if (admin.apps.length > 0) {\n this.app = admin.app();\n this.logger.info(\"Using existing Firebase app\");\n return;\n }\n\n if (!this.serviceAccount) {\n throw new Error(\"Service account not loaded\");\n }\n\n this.logger.info(\"Initializing Firebase Admin SDK\", {\n projectId: this.serviceAccount.project_id,\n privateKeyLength: this.serviceAccount.private_key?.length,\n privateKeyStart: this.serviceAccount.private_key?.substring(0, 50),\n });\n\n this.app = admin.initializeApp({\n credential: admin.credential.cert(this.serviceAccount as admin.ServiceAccount),\n });\n\n this.logger.info(\"Firebase Admin SDK initialized successfully\");\n } catch (error) {\n this.logger.error(\"Failed to initialize Firebase Admin SDK\", {\n error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : undefined,\n });\n throw new ExternalAPIError(\n \"Failed to initialize Firebase\",\n `Error: ${String(error)}`\n );\n }\n }\n\n public shutdown(): void {\n if (this.app) {\n this.app.delete().catch((error) => {\n this.logger.error(\"Error shutting down Firebase app\", { error });\n });\n this.app = null;\n }\n }\n\n /**\n * Send notifications using multicast (up to 500 tokens at once)\n * Automatically handles token validation and cleanup\n */\n private async sendMulticast(\n tokens: string[],\n notification: NotificationPayload,\n platform?: \"ios\" | \"android\"\n ): Promise<SendResult> {\n if (tokens.length === 0) {\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const messaging = admin.messaging();\n const invalidTokens: string[] = [];\n let successCount = 0;\n let failureCount = 0;\n\n // Firebase allows max 500 tokens per multicast\n const BATCH_SIZE = 500;\n\n for (let i = 0; i < tokens.length; i += BATCH_SIZE) {\n const batch = tokens.slice(i, i + BATCH_SIZE);\n\n const message: admin.messaging.MulticastMessage = {\n tokens: batch,\n notification: {\n title: notification.title,\n body: notification.body,\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n data: notification.data || {},\n ...(platform === \"ios\" && {\n apns: {\n payload: {\n aps: {\n sound: notification.sound || \"default\",\n ...(notification.badge !== undefined && { badge: notification.badge }),\n contentAvailable: true,\n },\n },\n fcmOptions: {\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n },\n }),\n ...(platform === \"android\" && {\n android: {\n priority: \"high\",\n notification: {\n sound: notification.sound || \"default\",\n channelId: \"default\",\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n },\n }),\n };\n\n try {\n const response = await messaging.sendEachForMulticast(message);\n\n successCount += response.successCount;\n failureCount += response.failureCount;\n\n // Identify and collect invalid tokens\n response.responses.forEach((resp, idx) => {\n if (!resp.success && resp.error) {\n const errorCode = resp.error.code;\n\n // Token is invalid or unregistered\n if (\n errorCode === \"messaging/invalid-registration-token\" ||\n errorCode === \"messaging/registration-token-not-registered\"\n ) {\n invalidTokens.push(batch[idx]);\n }\n\n this.logger.warn(\"Message send failed\", {\n token: batch[idx],\n error: resp.error.message,\n code: errorCode,\n });\n }\n });\n\n this.logger.info(\"Multicast batch sent\", {\n batchSize: batch.length,\n successCount: response.successCount,\n failureCount: response.failureCount,\n });\n } catch (error) {\n this.logger.error(\"Multicast send error\", { error, batchSize: batch.length });\n failureCount += batch.length;\n }\n }\n\n // Clean up invalid tokens from database\n if (invalidTokens.length > 0) {\n await this.removeInvalidTokens(invalidTokens);\n }\n\n return { successCount, failureCount, invalidTokens };\n }\n\n /**\n * Remove invalid device tokens from database\n */\n private async removeInvalidTokens(tokens: string[]): Promise<void> {\n try {\n const deleted = await DeviceTokens.destroy({\n where: { deviceToken: tokens },\n });\n\n this.logger.info(\"Removed invalid tokens from database\", {\n count: deleted,\n tokens: tokens.length,\n });\n } catch (error) {\n this.logger.error(\"Failed to remove invalid tokens\", { error });\n }\n }\n\n /**\n * Send notification to specific device tokens\n */\n public async sendToTokens(\n tokens: string[],\n platform: \"ios\" | \"android\",\n notification: NotificationPayload\n ): Promise<SendResult> {\n if (tokens.length === 0) {\n this.logger.warn(\"No tokens provided, skipping notification\");\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n this.logger.info(\"Sending notification to tokens\", {\n tokenCount: tokens.length,\n platform,\n title: notification.title,\n });\n\n return await this.sendMulticast(tokens, notification, platform);\n }\n\n /**\n * Send notification to a single user (all their devices)\n */\n public async sendToUser(params: {\n userUuid: string;\n notification: NotificationPayload;\n }): Promise<SendResult> {\n this.logger.info(\"Sending notification to user\", { userUuid: params.userUuid });\n\n const deviceTokens = await DeviceTokens.findAll({\n where: { userUuid: params.userUuid },\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n this.logger.warn(\"No device tokens found for user\", {\n userUuid: params.userUuid,\n });\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n return {\n successCount: iosResult.successCount + androidResult.successCount,\n failureCount: iosResult.failureCount + androidResult.failureCount,\n invalidTokens: [...iosResult.invalidTokens, ...androidResult.invalidTokens],\n };\n }\n\n /**\n * Send notification to multiple users\n */\n public async sendToUsers(params: {\n userUuids: string[];\n notification: NotificationPayload;\n }): Promise<SendResult> {\n this.logger.info(\"Sending notification to multiple users\", {\n count: params.userUuids.length,\n });\n\n const deviceTokens = await DeviceTokens.findAll({\n where: { userUuid: params.userUuids },\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n this.logger.warn(\"No device tokens found for users\");\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n return {\n successCount: iosResult.successCount + androidResult.successCount,\n failureCount: iosResult.failureCount + androidResult.failureCount,\n invalidTokens: [...iosResult.invalidTokens, ...androidResult.invalidTokens],\n };\n }\n\n /**\n * Subscribe users to a topic for efficient group messaging\n */\n public async subscribeToTopic(\n tokens: string[],\n topic: string\n ): Promise<{ successCount: number; failureCount: number }> {\n if (tokens.length === 0) {\n return { successCount: 0, failureCount: 0 };\n }\n\n this.logger.info(\"Subscribing tokens to topic\", {\n tokenCount: tokens.length,\n topic,\n });\n\n try {\n const response = await admin.messaging().subscribeToTopic(tokens, topic);\n\n this.logger.info(\"Topic subscription complete\", {\n successCount: response.successCount,\n failureCount: response.failureCount,\n topic,\n });\n\n return {\n successCount: response.successCount,\n failureCount: response.failureCount,\n };\n } catch (error) {\n this.logger.error(\"Topic subscription error\", { error, topic });\n throw new ExternalAPIError(\n \"Failed to subscribe to topic\",\n `Topic: ${topic}, Error: ${String(error)}`\n );\n }\n }\n\n /**\n * Send notification to a topic (efficient for large groups)\n */\n public async sendToTopic(params: {\n topic: string;\n notification: NotificationPayload;\n }): Promise<{ messageId: string }> {\n this.logger.info(\"Sending notification to topic\", { topic: params.topic });\n\n const message: admin.messaging.Message = {\n topic: params.topic,\n notification: {\n title: params.notification.title,\n body: params.notification.body,\n ...(params.notification.imageUrl && {\n imageUrl: params.notification.imageUrl,\n }),\n },\n data: params.notification.data || {},\n apns: {\n payload: {\n aps: {\n sound: params.notification.sound || \"default\",\n ...(params.notification.badge !== undefined && { badge: params.notification.badge }),\n contentAvailable: true,\n },\n },\n },\n android: {\n priority: \"high\",\n notification: {\n sound: params.notification.sound || \"default\",\n channelId: \"default\",\n },\n },\n };\n\n try {\n const messageId = await admin.messaging().send(message);\n\n this.logger.info(\"Topic notification sent\", {\n topic: params.topic,\n messageId,\n });\n\n return { messageId };\n } catch (error) {\n this.logger.error(\"Topic send error\", { error, topic: params.topic });\n throw new ExternalAPIError(\n \"Failed to send topic notification\",\n `Topic: ${params.topic}, Error: ${String(error)}`\n );\n }\n }\n\n /**\n * Send notification using a template\n */\n public async sendTemplateToUser(params: {\n userUuid: string;\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<SendResult> {\n this.logger.info(\"Sending template notification to user\", {\n userUuid: params.userUuid,\n notificationType: params.notificationType,\n language: params.language,\n });\n\n const includeOptions: any = {\n model: TranslatedNotification,\n as: \"translations\",\n required: false,\n };\n\n if (params.language) {\n includeOptions.where = { language: params.language };\n }\n\n const template = await NotificationModels.findOne({\n where: { type: params.notificationType as any },\n include: [includeOptions],\n });\n\n if (!template) {\n throw new ExternalAPIError(\n \"Notification template not found\",\n `Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find(\n (t) => t.language === params.language\n );\n let text = translation?.text || template.text;\n\n if (params.data) {\n Object.entries(params.data).forEach(([key, value]) => {\n text = text.replace(new RegExp(`{{${key}}}`, \"g\"), value);\n });\n }\n\n return await this.sendToUser({\n userUuid: params.userUuid,\n notification: {\n title: params.notificationType,\n body: text,\n ...(params.data && { data: params.data }),\n },\n });\n }\n\n /**\n * Send templated notification to multiple users\n */\n public async sendTemplateToUsers(params: {\n userUuids: string[];\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<SendResult> {\n this.logger.info(\"Sending template notification to multiple users\", {\n count: params.userUuids.length,\n notificationType: params.notificationType,\n });\n\n const includeOptions: any = {\n model: TranslatedNotification,\n as: \"translations\",\n required: false,\n };\n\n if (params.language) {\n includeOptions.where = { language: params.language };\n }\n\n const template = await NotificationModels.findOne({\n where: { type: params.notificationType as any },\n include: [includeOptions],\n });\n\n if (!template) {\n throw new ExternalAPIError(\n \"Notification template not found\",\n `Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find(\n (t) => t.language === params.language\n );\n let text = translation?.text || template.text;\n\n if (params.data) {\n Object.entries(params.data).forEach(([key, value]) => {\n text = text.replace(new RegExp(`{{${key}}}`, \"g\"), value);\n });\n }\n\n return await this.sendToUsers({\n userUuids: params.userUuids,\n notification: {\n title: params.notificationType,\n body: text,\n ...(params.data && { data: params.data }),\n },\n });\n }\n\n /**\n * Send notification to all users (uses topic for efficiency)\n * Consider subscribing users to an \"all_users\" topic for better performance\n */\n public async sendToAllUsers(params: {\n notification: NotificationPayload;\n batchSize?: number;\n }): Promise<{ totalSent: number }> {\n const batchSize = params.batchSize || 1000;\n let offset = 0;\n let totalSent = 0;\n\n this.logger.info(\"Starting broadcast notification\", { batchSize });\n\n while (true) {\n const deviceTokens = await DeviceTokens.findAll({\n limit: batchSize,\n offset,\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n break;\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n totalSent += iosResult.successCount + androidResult.successCount;\n offset += batchSize;\n\n this.logger.info(\"Broadcast batch sent\", {\n batchSent: iosResult.successCount + androidResult.successCount,\n totalSent,\n });\n }\n\n this.logger.info(\"Broadcast notification complete\", { totalSent });\n return { totalSent };\n }\n\n /**\n * Send notification to a user group\n * Recommended: Use topics instead for better performance\n */\n public async sendToGroup(params: {\n notification: NotificationPayload;\n group: \"premium\" | \"free\" | \"trial\";\n batchSize?: number;\n }): Promise<{ totalSent: number }> {\n const batchSize = params.batchSize || 1000;\n let offset = 0;\n let totalSent = 0;\n\n this.logger.info(\"Starting group notification\", {\n group: params.group,\n batchSize,\n });\n\n while (true) {\n const users = await PersistedUser.findAll({\n where: { subscriptionType: params.group } as any,\n include: [\n {\n model: DeviceTokens,\n as: \"deviceTokens\",\n attributes: [\"deviceToken\", \"platform\"],\n },\n ],\n limit: batchSize,\n offset,\n });\n\n if (users.length === 0) {\n break;\n }\n\n const allTokens = users.flatMap((user) => (user as any).deviceTokens || []);\n const iosTokens = allTokens\n .filter((dt: any) => dt.platform === \"ios\")\n .map((dt: any) => dt.deviceToken);\n const androidTokens = allTokens\n .filter((dt: any) => dt.platform === \"android\")\n .map((dt: any) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n totalSent += iosResult.successCount + androidResult.successCount;\n offset += batchSize;\n\n this.logger.info(\"Group batch sent\", {\n batchSent: iosResult.successCount + androidResult.successCount,\n totalSent,\n });\n }\n\n this.logger.info(\"Group notification complete\", {\n totalSent,\n group: params.group,\n });\n return { totalSent };\n }\n}\n"],"names":["admin","SecretManagerServiceClient","CommonSchemas","ExternalAPIError","Log","DeviceTokens","PersistedUser","NotificationModels","TranslatedNotification","FirebasePushNotificationClient","instance","initPromise","logger","getInstance","extend","app","serviceAccount","_config","initializeApp","parsedConfig","pushNotification","parse","process","env","loadServiceAccount","secretName","info","localKeyPath","FIREBASE_SERVICE_ACCOUNT_KEY","path","fs","serviceAccountJson","readFile","JSON","projectId","project_id","clientEmail","client_email","client","version","accessSecretVersion","name","payload","data","Error","toString","error","String","resetInstance","shutdown","undefined","apps","length","privateKeyLength","private_key","privateKeyStart","substring","credential","cert","errorMessage","message","errorStack","stack","delete","catch","sendMulticast","tokens","notification","platform","successCount","failureCount","invalidTokens","messaging","BATCH_SIZE","i","batch","slice","title","body","imageUrl","apns","aps","sound","badge","contentAvailable","fcmOptions","android","priority","channelId","response","sendEachForMulticast","responses","forEach","resp","idx","success","errorCode","code","push","warn","token","batchSize","removeInvalidTokens","deleted","destroy","where","deviceToken","count","sendToTokens","tokenCount","sendToUser","params","userUuid","deviceTokens","findAll","attributes","iosTokens","filter","dt","map","androidTokens","iosResult","androidResult","Promise","all","resolve","sendToUsers","userUuids","subscribeToTopic","topic","sendToTopic","messageId","send","sendTemplateToUser","notificationType","language","includeOptions","model","as","required","template","findOne","type","include","translation","translations","find","t","text","Object","entries","key","value","replace","RegExp","sendTemplateToUsers","sendToAllUsers","offset","totalSent","limit","batchSent","sendToGroup","group","users","subscriptionType","allTokens","flatMap","user"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"sourcesContent":["/**\n * Firebase Push Notification Client\n *\n * Industry-standard implementation using Firebase Admin SDK for:\n * - iOS (APNs via FCM)\n * - Android (FCM)\n *\n * Features:\n * - Multicast messaging (up to 500 tokens per request)\n * - Topic-based messaging for efficient broadcasting\n * - Automatic token validation and cleanup\n * - Built-in retry logic and error handling\n * - Message delivery tracking\n * - Type-safe API\n */\n\nimport admin from \"firebase-admin\";\nimport { z } from \"zod\";\nimport { SecretManagerServiceClient } from \"@google-cloud/secret-manager\";\nimport { CommonSchemas } from \"../config/ConfigValidator.js\";\nimport { ExternalAPIError } from \"../Errors/AppError.js\";\nimport { Log } from \"../utils/Logger.js\";\nimport { DeviceTokens } from \"../dbmodels/user/DeviceTokens.js\";\nimport { PersistedUser } from \"../dbmodels/user/PersistedUser.js\";\nimport { NotificationModels } from \"../dbmodels/notifications/NotificationModels.js\";\nimport { TranslatedNotification } from \"../dbmodels/notifications/TranslatedNotification.js\";\n\ntype FirebaseConfig = z.infer<typeof CommonSchemas.pushNotification>;\n\ninterface ServiceAccount {\n type: string;\n project_id: string;\n private_key_id: string;\n private_key: string;\n client_email: string;\n client_id: string;\n auth_uri: string;\n token_uri: string;\n auth_provider_x509_cert_url: string;\n client_x509_cert_url: string;\n}\n\nexport interface NotificationPayload {\n title: string;\n body: string;\n imageUrl?: string;\n sound?: string;\n badge?: number;\n data?: Record<string, string>;\n redirectPath?: string;\n}\n\ninterface SendResult {\n successCount: number;\n failureCount: number;\n invalidTokens: string[];\n}\n\nexport class FirebasePushNotificationClient {\n private static instance: FirebasePushNotificationClient;\n private static initPromise: Promise<FirebasePushNotificationClient> | null = null;\n private logger = Log.getInstance().extend(\"firebase-push\");\n private app: admin.app.App | null = null;\n private serviceAccount: ServiceAccount | null = null;\n\n private constructor(_config: FirebaseConfig, serviceAccount: ServiceAccount) {\n // config parameter kept for backward compatibility but not used (FCM config comes from Secret Manager)\n this.serviceAccount = serviceAccount;\n this.initializeApp();\n }\n\n public static async getInstance(): Promise<FirebasePushNotificationClient> {\n if (FirebasePushNotificationClient.instance) {\n return FirebasePushNotificationClient.instance;\n }\n\n // If already initializing, wait for that to complete\n if (FirebasePushNotificationClient.initPromise) {\n return FirebasePushNotificationClient.initPromise;\n }\n\n // Start initialization\n FirebasePushNotificationClient.initPromise = (async () => {\n // Parse config (mostly for optional APNs fields, FCM comes from Secret Manager)\n const parsedConfig = CommonSchemas.pushNotification.parse(process.env);\n const serviceAccount = await FirebasePushNotificationClient.loadServiceAccount();\n FirebasePushNotificationClient.instance = new FirebasePushNotificationClient(parsedConfig, serviceAccount);\n FirebasePushNotificationClient.initPromise = null;\n return FirebasePushNotificationClient.instance;\n })();\n\n return FirebasePushNotificationClient.initPromise;\n }\n\n private static async loadServiceAccount(): Promise<ServiceAccount> {\n const logger = Log.getInstance().extend(\"firebase-push\");\n const secretName = \"projects/1033066542238/secrets/firebase_new_key/versions/latest\";\n\n try {\n // Check if FIREBASE_SERVICE_ACCOUNT_KEY env variable is set (for local development)\n const localKeyPath = process.env.FIREBASE_SERVICE_ACCOUNT_KEY;\n if (localKeyPath) {\n const fs = await import(\"fs/promises\");\n const serviceAccountJson = await fs.readFile(localKeyPath, \"utf-8\");\n const serviceAccount = JSON.parse(serviceAccountJson) as ServiceAccount;\n\n logger.info(\"Firebase service account loaded from local file\");\n return serviceAccount;\n }\n\n // Production: Load from Secret Manager\n const client = new SecretManagerServiceClient();\n const [version] = await client.accessSecretVersion({ name: secretName });\n\n if (!version.payload?.data) {\n throw new Error(\"Secret payload is empty\");\n }\n\n const serviceAccountJson = version.payload.data.toString();\n const serviceAccount = JSON.parse(serviceAccountJson) as ServiceAccount;\n\n logger.info(\"Firebase service account loaded from Secret Manager\");\n return serviceAccount;\n } catch (error) {\n logger.error(\"Failed to load Firebase service account\", { error });\n throw new ExternalAPIError(\n \"Failed to load Firebase credentials\",\n `Error: ${String(error)}`\n );\n }\n }\n\n public static resetInstance(): void {\n FirebasePushNotificationClient.instance?.shutdown();\n FirebasePushNotificationClient.instance = undefined as any;\n FirebasePushNotificationClient.initPromise = null;\n }\n\n private initializeApp(): void {\n try {\n // Check if app already exists\n if (admin.apps && admin.apps.length > 0) {\n this.app = admin.app();\n this.logger.info(\"Using existing Firebase app\");\n return;\n }\n\n if (!this.serviceAccount) {\n throw new Error(\"Service account not loaded\");\n }\n\n // Validate service account has all required fields\n if (!this.serviceAccount.project_id || !this.serviceAccount.client_email || !this.serviceAccount.private_key) {\n throw new Error(\"Invalid service account: missing required fields\");\n }\n\n this.app = admin.initializeApp({\n credential: admin.credential.cert(this.serviceAccount as admin.ServiceAccount),\n });\n\n this.logger.info(\"Firebase Admin SDK initialized successfully\");\n } catch (error) {\n this.logger.error(\"Failed to initialize Firebase Admin SDK\", { error });\n throw new ExternalAPIError(\n \"Failed to initialize Firebase\",\n `Error: ${String(error)}`\n );\n }\n }\n\n public shutdown(): void {\n if (this.app) {\n this.app.delete().catch((error) => {\n this.logger.error(\"Error shutting down Firebase app\", { error });\n });\n this.app = null;\n }\n }\n\n /**\n * Send notifications using multicast (up to 500 tokens at once)\n * Automatically handles token validation and cleanup\n */\n private async sendMulticast(\n tokens: string[],\n notification: NotificationPayload,\n platform?: \"ios\" | \"android\"\n ): Promise<SendResult> {\n if (tokens.length === 0) {\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const messaging = admin.messaging();\n const invalidTokens: string[] = [];\n let successCount = 0;\n let failureCount = 0;\n\n // Firebase allows max 500 tokens per multicast\n const BATCH_SIZE = 500;\n\n for (let i = 0; i < tokens.length; i += BATCH_SIZE) {\n const batch = tokens.slice(i, i + BATCH_SIZE);\n\n const message: admin.messaging.MulticastMessage = {\n tokens: batch,\n notification: {\n title: notification.title,\n body: notification.body,\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n data: notification.data || {},\n ...(platform === \"ios\" && {\n apns: {\n payload: {\n aps: {\n sound: notification.sound || \"default\",\n ...(notification.badge !== undefined && { badge: notification.badge }),\n contentAvailable: true,\n },\n },\n fcmOptions: {\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n },\n }),\n ...(platform === \"android\" && {\n android: {\n priority: \"high\",\n notification: {\n sound: notification.sound || \"default\",\n channelId: \"default\",\n ...(notification.imageUrl && { imageUrl: notification.imageUrl }),\n },\n },\n }),\n };\n\n try {\n const response = await messaging.sendEachForMulticast(message);\n\n successCount += response.successCount;\n failureCount += response.failureCount;\n\n // Identify and collect invalid tokens\n response.responses.forEach((resp, idx) => {\n if (!resp.success && resp.error) {\n const errorCode = resp.error.code;\n\n // Token is invalid or unregistered\n if (\n errorCode === \"messaging/invalid-registration-token\" ||\n errorCode === \"messaging/registration-token-not-registered\"\n ) {\n invalidTokens.push(batch[idx]);\n }\n\n this.logger.warn(\"Message send failed\", {\n token: batch[idx],\n error: resp.error.message,\n code: errorCode,\n });\n }\n });\n\n this.logger.info(\"Multicast batch sent\", {\n batchSize: batch.length,\n successCount: response.successCount,\n failureCount: response.failureCount,\n });\n } catch (error) {\n this.logger.error(\"Multicast send error\", { error, batchSize: batch.length });\n failureCount += batch.length;\n }\n }\n\n // Clean up invalid tokens from database\n if (invalidTokens.length > 0) {\n await this.removeInvalidTokens(invalidTokens);\n }\n\n return { successCount, failureCount, invalidTokens };\n }\n\n /**\n * Remove invalid device tokens from database\n */\n private async removeInvalidTokens(tokens: string[]): Promise<void> {\n try {\n const deleted = await DeviceTokens.destroy({\n where: { deviceToken: tokens },\n });\n\n this.logger.info(\"Removed invalid tokens from database\", {\n count: deleted,\n tokens: tokens.length,\n });\n } catch (error) {\n this.logger.error(\"Failed to remove invalid tokens\", { error });\n }\n }\n\n /**\n * Send notification to specific device tokens\n */\n public async sendToTokens(\n tokens: string[],\n platform: \"ios\" | \"android\",\n notification: NotificationPayload\n ): Promise<SendResult> {\n if (tokens.length === 0) {\n this.logger.warn(\"No tokens provided, skipping notification\");\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n this.logger.info(\"Sending notification to tokens\", {\n tokenCount: tokens.length,\n platform,\n title: notification.title,\n });\n\n return await this.sendMulticast(tokens, notification, platform);\n }\n\n /**\n * Send notification to a single user (all their devices)\n */\n public async sendToUser(params: {\n userUuid: string;\n notification: NotificationPayload;\n }): Promise<SendResult> {\n this.logger.info(\"Sending notification to user\", { userUuid: params.userUuid });\n\n const deviceTokens = await DeviceTokens.findAll({\n where: { userUuid: params.userUuid },\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n this.logger.warn(\"No device tokens found for user\", {\n userUuid: params.userUuid,\n });\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n return {\n successCount: iosResult.successCount + androidResult.successCount,\n failureCount: iosResult.failureCount + androidResult.failureCount,\n invalidTokens: [...iosResult.invalidTokens, ...androidResult.invalidTokens],\n };\n }\n\n /**\n * Send notification to multiple users\n */\n public async sendToUsers(params: {\n userUuids: string[];\n notification: NotificationPayload;\n }): Promise<SendResult> {\n this.logger.info(\"Sending notification to multiple users\", {\n count: params.userUuids.length,\n });\n\n const deviceTokens = await DeviceTokens.findAll({\n where: { userUuid: params.userUuids },\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n this.logger.warn(\"No device tokens found for users\");\n return { successCount: 0, failureCount: 0, invalidTokens: [] };\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n return {\n successCount: iosResult.successCount + androidResult.successCount,\n failureCount: iosResult.failureCount + androidResult.failureCount,\n invalidTokens: [...iosResult.invalidTokens, ...androidResult.invalidTokens],\n };\n }\n\n /**\n * Subscribe users to a topic for efficient group messaging\n */\n public async subscribeToTopic(\n tokens: string[],\n topic: string\n ): Promise<{ successCount: number; failureCount: number }> {\n if (tokens.length === 0) {\n return { successCount: 0, failureCount: 0 };\n }\n\n this.logger.info(\"Subscribing tokens to topic\", {\n tokenCount: tokens.length,\n topic,\n });\n\n try {\n const response = await admin.messaging().subscribeToTopic(tokens, topic);\n\n this.logger.info(\"Topic subscription complete\", {\n successCount: response.successCount,\n failureCount: response.failureCount,\n topic,\n });\n\n return {\n successCount: response.successCount,\n failureCount: response.failureCount,\n };\n } catch (error) {\n this.logger.error(\"Topic subscription error\", { error, topic });\n throw new ExternalAPIError(\n \"Failed to subscribe to topic\",\n `Topic: ${topic}, Error: ${String(error)}`\n );\n }\n }\n\n /**\n * Send notification to a topic (efficient for large groups)\n */\n public async sendToTopic(params: {\n topic: string;\n notification: NotificationPayload;\n }): Promise<{ messageId: string }> {\n this.logger.info(\"Sending notification to topic\", { topic: params.topic });\n\n const message: admin.messaging.Message = {\n topic: params.topic,\n notification: {\n title: params.notification.title,\n body: params.notification.body,\n ...(params.notification.imageUrl && {\n imageUrl: params.notification.imageUrl,\n }),\n },\n data: params.notification.data || {},\n apns: {\n payload: {\n aps: {\n sound: params.notification.sound || \"default\",\n ...(params.notification.badge !== undefined && { badge: params.notification.badge }),\n contentAvailable: true,\n },\n },\n },\n android: {\n priority: \"high\",\n notification: {\n sound: params.notification.sound || \"default\",\n channelId: \"default\",\n },\n },\n };\n\n try {\n const messageId = await admin.messaging().send(message);\n\n this.logger.info(\"Topic notification sent\", {\n topic: params.topic,\n messageId,\n });\n\n return { messageId };\n } catch (error) {\n this.logger.error(\"Topic send error\", { error, topic: params.topic });\n throw new ExternalAPIError(\n \"Failed to send topic notification\",\n `Topic: ${params.topic}, Error: ${String(error)}`\n );\n }\n }\n\n /**\n * Send notification using a template\n */\n public async sendTemplateToUser(params: {\n userUuid: string;\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<SendResult> {\n this.logger.info(\"Sending template notification to user\", {\n userUuid: params.userUuid,\n notificationType: params.notificationType,\n language: params.language,\n });\n\n const includeOptions: any = {\n model: TranslatedNotification,\n as: \"translations\",\n required: false,\n };\n\n if (params.language) {\n includeOptions.where = { language: params.language };\n }\n\n const template = await NotificationModels.findOne({\n where: { type: params.notificationType as any },\n include: [includeOptions],\n });\n\n if (!template) {\n throw new ExternalAPIError(\n \"Notification template not found\",\n `Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find(\n (t) => t.language === params.language\n );\n let text = translation?.text || template.text;\n\n if (params.data) {\n Object.entries(params.data).forEach(([key, value]) => {\n text = text.replace(new RegExp(`{{${key}}}`, \"g\"), value);\n });\n }\n\n return await this.sendToUser({\n userUuid: params.userUuid,\n notification: {\n title: params.notificationType,\n body: text,\n ...(params.data && { data: params.data }),\n },\n });\n }\n\n /**\n * Send templated notification to multiple users\n */\n public async sendTemplateToUsers(params: {\n userUuids: string[];\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<SendResult> {\n this.logger.info(\"Sending template notification to multiple users\", {\n count: params.userUuids.length,\n notificationType: params.notificationType,\n });\n\n const includeOptions: any = {\n model: TranslatedNotification,\n as: \"translations\",\n required: false,\n };\n\n if (params.language) {\n includeOptions.where = { language: params.language };\n }\n\n const template = await NotificationModels.findOne({\n where: { type: params.notificationType as any },\n include: [includeOptions],\n });\n\n if (!template) {\n throw new ExternalAPIError(\n \"Notification template not found\",\n `Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find(\n (t) => t.language === params.language\n );\n let text = translation?.text || template.text;\n\n if (params.data) {\n Object.entries(params.data).forEach(([key, value]) => {\n text = text.replace(new RegExp(`{{${key}}}`, \"g\"), value);\n });\n }\n\n return await this.sendToUsers({\n userUuids: params.userUuids,\n notification: {\n title: params.notificationType,\n body: text,\n ...(params.data && { data: params.data }),\n },\n });\n }\n\n /**\n * Send notification to all users (uses topic for efficiency)\n * Consider subscribing users to an \"all_users\" topic for better performance\n */\n public async sendToAllUsers(params: {\n notification: NotificationPayload;\n batchSize?: number;\n }): Promise<{ totalSent: number }> {\n const batchSize = params.batchSize || 1000;\n let offset = 0;\n let totalSent = 0;\n\n this.logger.info(\"Starting broadcast notification\", { batchSize });\n\n while (true) {\n const deviceTokens = await DeviceTokens.findAll({\n limit: batchSize,\n offset,\n attributes: [\"deviceToken\", \"platform\"],\n });\n\n if (deviceTokens.length === 0) {\n break;\n }\n\n const iosTokens = deviceTokens\n .filter((dt) => dt.platform === \"ios\")\n .map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens\n .filter((dt) => dt.platform === \"android\")\n .map((dt) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n totalSent += iosResult.successCount + androidResult.successCount;\n offset += batchSize;\n\n this.logger.info(\"Broadcast batch sent\", {\n batchSent: iosResult.successCount + androidResult.successCount,\n totalSent,\n });\n }\n\n this.logger.info(\"Broadcast notification complete\", { totalSent });\n return { totalSent };\n }\n\n /**\n * Send notification to a user group\n * Recommended: Use topics instead for better performance\n */\n public async sendToGroup(params: {\n notification: NotificationPayload;\n group: \"premium\" | \"free\" | \"trial\";\n batchSize?: number;\n }): Promise<{ totalSent: number }> {\n const batchSize = params.batchSize || 1000;\n let offset = 0;\n let totalSent = 0;\n\n this.logger.info(\"Starting group notification\", {\n group: params.group,\n batchSize,\n });\n\n while (true) {\n const users = await PersistedUser.findAll({\n where: { subscriptionType: params.group } as any,\n include: [\n {\n model: DeviceTokens,\n as: \"deviceTokens\",\n attributes: [\"deviceToken\", \"platform\"],\n },\n ],\n limit: batchSize,\n offset,\n });\n\n if (users.length === 0) {\n break;\n }\n\n const allTokens = users.flatMap((user) => (user as any).deviceTokens || []);\n const iosTokens = allTokens\n .filter((dt: any) => dt.platform === \"ios\")\n .map((dt: any) => dt.deviceToken);\n const androidTokens = allTokens\n .filter((dt: any) => dt.platform === \"android\")\n .map((dt: any) => dt.deviceToken);\n\n const [iosResult, androidResult] = await Promise.all([\n iosTokens.length > 0\n ? this.sendToTokens(iosTokens, \"ios\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n androidTokens.length > 0\n ? this.sendToTokens(androidTokens, \"android\", params.notification)\n : Promise.resolve({ successCount: 0, failureCount: 0, invalidTokens: [] }),\n ]);\n\n totalSent += iosResult.successCount + androidResult.successCount;\n offset += batchSize;\n\n this.logger.info(\"Group batch sent\", {\n batchSent: iosResult.successCount + androidResult.successCount,\n totalSent,\n });\n }\n\n this.logger.info(\"Group notification complete\", {\n totalSent,\n group: params.group,\n });\n return { totalSent };\n }\n}\n"],"names":["admin","SecretManagerServiceClient","CommonSchemas","ExternalAPIError","Log","DeviceTokens","PersistedUser","NotificationModels","TranslatedNotification","FirebasePushNotificationClient","instance","initPromise","logger","getInstance","extend","app","serviceAccount","_config","initializeApp","parsedConfig","pushNotification","parse","process","env","loadServiceAccount","secretName","localKeyPath","FIREBASE_SERVICE_ACCOUNT_KEY","fs","serviceAccountJson","readFile","JSON","info","client","version","accessSecretVersion","name","payload","data","Error","toString","error","String","resetInstance","shutdown","undefined","apps","length","project_id","client_email","private_key","credential","cert","delete","catch","sendMulticast","tokens","notification","platform","successCount","failureCount","invalidTokens","messaging","BATCH_SIZE","i","batch","slice","message","title","body","imageUrl","apns","aps","sound","badge","contentAvailable","fcmOptions","android","priority","channelId","response","sendEachForMulticast","responses","forEach","resp","idx","success","errorCode","code","push","warn","token","batchSize","removeInvalidTokens","deleted","destroy","where","deviceToken","count","sendToTokens","tokenCount","sendToUser","params","userUuid","deviceTokens","findAll","attributes","iosTokens","filter","dt","map","androidTokens","iosResult","androidResult","Promise","all","resolve","sendToUsers","userUuids","subscribeToTopic","topic","sendToTopic","messageId","send","sendTemplateToUser","notificationType","language","includeOptions","model","as","required","template","findOne","type","include","translation","translations","find","t","text","Object","entries","key","value","replace","RegExp","sendTemplateToUsers","sendToAllUsers","offset","totalSent","limit","batchSent","sendToGroup","group","users","subscriptionType","allTokens","flatMap","user"],"mappings":"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"}
@@ -2,5 +2,7 @@ import { Model } from "sequelize-typescript";
2
2
  export declare class UserCoach extends Model {
3
3
  userUuid: string;
4
4
  coachUuid: string;
5
+ assignedAt: Date;
6
+ unassignedAt: Date | null;
5
7
  }
6
8
  //# sourceMappingURL=UserCoach.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserCoach.d.ts","sourceRoot":"","sources":["../../../../src/lib/dbmodels/user/UserCoach.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,EAAoB,MAAM,sBAAsB,CAAC;AAG5F,qBAGa,SAAU,SAAQ,KAAK;IAIxB,QAAQ,EAAE,MAAM,CAAC;IAKjB,SAAS,EAAE,MAAM,CAAC;CAC7B"}
1
+ {"version":3,"file":"UserCoach.d.ts","sourceRoot":"","sources":["../../../../src/lib/dbmodels/user/UserCoach.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,EAAoB,MAAM,sBAAsB,CAAC;AAG5F,qBAGa,SAAU,SAAQ,KAAK;IAIxB,QAAQ,EAAE,MAAM,CAAC;IAKjB,SAAS,EAAE,MAAM,CAAC;IAOlB,UAAU,EAAE,IAAI,CAAC;IAMjB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC"}
@@ -18,6 +18,19 @@ _ts_decorate([
18
18
  ForeignKey(()=>PersistedUser),
19
19
  Column(DataType.UUID)
20
20
  ], UserCoach.prototype, "coachUuid", void 0);
21
+ _ts_decorate([
22
+ Column({
23
+ type: DataType.DATE,
24
+ allowNull: false,
25
+ defaultValue: DataType.NOW
26
+ })
27
+ ], UserCoach.prototype, "assignedAt", void 0);
28
+ _ts_decorate([
29
+ Column({
30
+ type: DataType.DATE,
31
+ allowNull: true
32
+ })
33
+ ], UserCoach.prototype, "unassignedAt", void 0);
21
34
  UserCoach = _ts_decorate([
22
35
  Table({
23
36
  tableName: 'UserCoaches'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/lib/dbmodels/user/UserCoach.ts"],"sourcesContent":["import {Column, DataType, ForeignKey, Model, PrimaryKey, Table} from \"sequelize-typescript\";\nimport {PersistedUser} from \"./PersistedUser.js\";\n\n@Table({\n tableName: 'UserCoaches',\n})\nexport class UserCoach extends Model {\n @PrimaryKey\n @ForeignKey(() => PersistedUser)\n @Column(DataType.UUID)\n declare userUuid: string;\n\n @PrimaryKey\n @ForeignKey(() => PersistedUser)\n @Column(DataType.UUID)\n declare coachUuid: string;\n}"],"names":["Column","DataType","ForeignKey","Model","PrimaryKey","Table","PersistedUser","UserCoach","UUID","tableName"],"mappings":";;;;;;AAAA,SAAQA,MAAM,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,EAAEC,KAAK,QAAO,uBAAuB;AAC5F,SAAQC,aAAa,QAAO,qBAAqB;AAKjD,OAAO,MAAMC,kBAAkBJ;AAU/B;;;mBARsBG;oBACDE;;;;mBAICF;oBACDE;;;;QAVjBC,WAAW"}
1
+ {"version":3,"sources":["../../../../src/lib/dbmodels/user/UserCoach.ts"],"sourcesContent":["import {Column, DataType, ForeignKey, Model, PrimaryKey, Table} from \"sequelize-typescript\";\nimport {PersistedUser} from \"./PersistedUser.js\";\n\n@Table({\n tableName: 'UserCoaches',\n})\nexport class UserCoach extends Model {\n @PrimaryKey\n @ForeignKey(() => PersistedUser)\n @Column(DataType.UUID)\n declare userUuid: string;\n\n @PrimaryKey\n @ForeignKey(() => PersistedUser)\n @Column(DataType.UUID)\n declare coachUuid: string;\n\n @Column({\n type: DataType.DATE,\n allowNull: false,\n defaultValue: DataType.NOW,\n })\n declare assignedAt: Date;\n\n @Column({\n type: DataType.DATE,\n allowNull: true,\n })\n declare unassignedAt: Date | null;\n}"],"names":["Column","DataType","ForeignKey","Model","PrimaryKey","Table","PersistedUser","UserCoach","UUID","type","DATE","allowNull","defaultValue","NOW","tableName"],"mappings":";;;;;;AAAA,SAAQA,MAAM,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,EAAEC,KAAK,QAAO,uBAAuB;AAC5F,SAAQC,aAAa,QAAO,qBAAqB;AAKjD,OAAO,MAAMC,kBAAkBJ;AAuB/B;;;mBArBsBG;oBACDE;;;;mBAICF;oBACDE;;;;QAIbC,MAAMR,SAASS,IAAI;QACnBC,WAAW;QACXC,cAAcX,SAASY,GAAG;;;;;QAK1BJ,MAAMR,SAASS,IAAI;QACnBC,WAAW;;;;;QAtBfG,WAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "90dc-core",
3
- "version": "1.16.2",
3
+ "version": "1.16.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",