90dc-core 1.16.0 → 1.16.1

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.
@@ -29,11 +29,13 @@ interface SendResult {
29
29
  }
30
30
  export declare class FirebasePushNotificationClient {
31
31
  private static instance;
32
- private config;
32
+ private static initPromise;
33
33
  private logger;
34
34
  private app;
35
+ private serviceAccount;
35
36
  private constructor();
36
- static getInstance(): FirebasePushNotificationClient;
37
+ static getInstance(): Promise<FirebasePushNotificationClient>;
38
+ private static loadServiceAccount;
37
39
  static resetInstance(): void;
38
40
  private initializeApp;
39
41
  shutdown(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"FirebasePushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAcH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,UAAU;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,qBAAa,8BAA8B;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,GAAG,CAA8B;IAEzC,OAAO;WAKO,WAAW,IAAI,8BAA8B;WAQ7C,aAAa,IAAI,IAAI;IAKnC,OAAO,CAAC,aAAa;IA+Bd,QAAQ,IAAI,IAAI;IASvB;;;OAGG;YACW,aAAa;IAoG3B;;OAEG;YACW,mBAAmB;IAejC;;OAEG;IACU,YAAY,CACvB,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,KAAK,GAAG,SAAS,EAC3B,YAAY,EAAE,mBAAmB,GAChC,OAAO,CAAC,UAAU,CAAC;IAetB;;OAEG;IACU,UAAU,CAAC,MAAM,EAAE;QAC9B,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,mBAAmB,CAAC;KACnC,GAAG,OAAO,CAAC,UAAU,CAAC;IAsCvB;;OAEG;IACU,WAAW,CAAC,MAAM,EAAE;QAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,YAAY,EAAE,mBAAmB,CAAC;KACnC,GAAG,OAAO,CAAC,UAAU,CAAC;IAsCvB;;OAEG;IACU,gBAAgB,CAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAgC1D;;OAEG;IACU,WAAW,CAAC,MAAM,EAAE;QAC/B,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,mBAAmB,CAAC;KACnC,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAiDlC;;OAEG;IACU,kBAAkB,CAAC,MAAM,EAAE;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,GAAG,OAAO,CAAC,UAAU,CAAC;IAkDvB;;OAEG;IACU,mBAAmB,CAAC,MAAM,EAAE;QACvC,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,GAAG,OAAO,CAAC,UAAU,CAAC;IAiDvB;;;OAGG;IACU,cAAc,CAAC,MAAM,EAAE;QAClC,YAAY,EAAE,mBAAmB,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA+ClC;;;OAGG;IACU,WAAW,CAAC,MAAM,EAAE;QAC/B,YAAY,EAAE,mBAAmB,CAAC;QAClC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;QACpC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CA4DnC"}
1
+ {"version":3,"file":"FirebasePushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/FirebasePushNotificationClient.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@
13
13
  * - Message delivery tracking
14
14
  * - Type-safe API
15
15
  */ import * as admin from "firebase-admin";
16
+ import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
16
17
  import { CommonSchemas } from "../config/ConfigValidator.js";
17
18
  import { ExternalAPIError } from "../Errors/AppError.js";
18
19
  import { Log } from "../utils/Logger.js";
@@ -22,23 +23,66 @@ import { NotificationModels } from "../dbmodels/notifications/NotificationModels
22
23
  import { TranslatedNotification } from "../dbmodels/notifications/TranslatedNotification.js";
23
24
  export class FirebasePushNotificationClient {
24
25
  static instance;
25
- config;
26
+ static initPromise = null;
26
27
  logger = Log.getInstance().extend("firebase-push");
27
28
  app = null;
28
- constructor(config){
29
- this.config = config;
29
+ serviceAccount = null;
30
+ constructor(_config, serviceAccount){
31
+ // config parameter kept for backward compatibility but not used (FCM config comes from Secret Manager)
32
+ this.serviceAccount = serviceAccount;
30
33
  this.initializeApp();
31
34
  }
32
- static getInstance() {
33
- if (!FirebasePushNotificationClient.instance) {
34
- const config = CommonSchemas.pushNotification.parse(process.env);
35
- FirebasePushNotificationClient.instance = new FirebasePushNotificationClient(config);
35
+ static async getInstance() {
36
+ if (FirebasePushNotificationClient.instance) {
37
+ return FirebasePushNotificationClient.instance;
38
+ }
39
+ // If already initializing, wait for that to complete
40
+ if (FirebasePushNotificationClient.initPromise) {
41
+ return FirebasePushNotificationClient.initPromise;
42
+ }
43
+ // Start initialization
44
+ FirebasePushNotificationClient.initPromise = (async ()=>{
45
+ // Parse config (mostly for optional APNs fields, FCM comes from Secret Manager)
46
+ const parsedConfig = CommonSchemas.pushNotification.parse(process.env);
47
+ const serviceAccount = await FirebasePushNotificationClient.loadServiceAccount();
48
+ FirebasePushNotificationClient.instance = new FirebasePushNotificationClient(parsedConfig, serviceAccount);
49
+ FirebasePushNotificationClient.initPromise = null;
50
+ return FirebasePushNotificationClient.instance;
51
+ })();
52
+ return FirebasePushNotificationClient.initPromise;
53
+ }
54
+ static async loadServiceAccount() {
55
+ const logger = Log.getInstance().extend("firebase-push");
56
+ const secretName = "projects/1033066542238/secrets/firebase_new_key/versions/latest";
57
+ try {
58
+ logger.info("Loading Firebase service account from Secret Manager", {
59
+ secretName
60
+ });
61
+ const client = new SecretManagerServiceClient();
62
+ const [version] = await client.accessSecretVersion({
63
+ name: secretName
64
+ });
65
+ if (!version.payload?.data) {
66
+ throw new Error("Secret payload is empty");
67
+ }
68
+ const serviceAccountJson = version.payload.data.toString();
69
+ const serviceAccount = JSON.parse(serviceAccountJson);
70
+ logger.info("Firebase service account loaded successfully", {
71
+ projectId: serviceAccount.project_id,
72
+ clientEmail: serviceAccount.client_email
73
+ });
74
+ return serviceAccount;
75
+ } catch (error) {
76
+ logger.error("Failed to load Firebase service account from Secret Manager", {
77
+ error
78
+ });
79
+ throw new ExternalAPIError("Failed to load Firebase credentials", `Error: ${String(error)}`);
36
80
  }
37
- return FirebasePushNotificationClient.instance;
38
81
  }
39
82
  static resetInstance() {
40
83
  FirebasePushNotificationClient.instance?.shutdown();
41
84
  FirebasePushNotificationClient.instance = undefined;
85
+ FirebasePushNotificationClient.initPromise = null;
42
86
  }
43
87
  initializeApp() {
44
88
  try {
@@ -48,15 +92,14 @@ export class FirebasePushNotificationClient {
48
92
  this.logger.info("Using existing Firebase app");
49
93
  return;
50
94
  }
95
+ if (!this.serviceAccount) {
96
+ throw new Error("Service account not loaded");
97
+ }
51
98
  this.logger.info("Initializing Firebase Admin SDK", {
52
- projectId: this.config.FCM_PROJECT_ID
99
+ projectId: this.serviceAccount.project_id
53
100
  });
54
101
  this.app = admin.initializeApp({
55
- credential: admin.credential.cert({
56
- projectId: this.config.FCM_PROJECT_ID,
57
- clientEmail: this.config.FCM_CLIENT_EMAIL,
58
- privateKey: this.config.FCM_PRIVATE_KEY
59
- })
102
+ credential: admin.credential.cert(this.serviceAccount)
60
103
  });
61
104
  this.logger.info("Firebase Admin SDK initialized successfully");
62
105
  } catch (error) {
@@ -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 { 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\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 config: FirebaseConfig;\n private logger = Log.getInstance().extend(\"firebase-push\");\n private app: admin.app.App | null = null;\n\n private constructor(config: FirebaseConfig) {\n this.config = config;\n this.initializeApp();\n }\n\n public static getInstance(): FirebasePushNotificationClient {\n if (!FirebasePushNotificationClient.instance) {\n const config = CommonSchemas.pushNotification.parse(process.env);\n FirebasePushNotificationClient.instance = new FirebasePushNotificationClient(config);\n }\n return FirebasePushNotificationClient.instance;\n }\n\n public static resetInstance(): void {\n FirebasePushNotificationClient.instance?.shutdown();\n FirebasePushNotificationClient.instance = undefined as any;\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 this.logger.info(\"Initializing Firebase Admin SDK\", {\n projectId: this.config.FCM_PROJECT_ID,\n });\n\n this.app = admin.initializeApp({\n credential: admin.credential.cert({\n projectId: this.config.FCM_PROJECT_ID,\n clientEmail: this.config.FCM_CLIENT_EMAIL,\n privateKey: this.config.FCM_PRIVATE_KEY,\n }),\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","CommonSchemas","ExternalAPIError","Log","DeviceTokens","PersistedUser","NotificationModels","TranslatedNotification","FirebasePushNotificationClient","instance","config","logger","getInstance","extend","app","initializeApp","pushNotification","parse","process","env","resetInstance","shutdown","undefined","apps","length","info","projectId","FCM_PROJECT_ID","credential","cert","clientEmail","FCM_CLIENT_EMAIL","privateKey","FCM_PRIVATE_KEY","error","String","delete","catch","sendMulticast","tokens","notification","platform","successCount","failureCount","invalidTokens","messaging","BATCH_SIZE","i","batch","slice","message","title","body","imageUrl","data","apns","payload","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 * 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 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\", {\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 from Secret Manager\", { 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 });\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","info","client","version","accessSecretVersion","name","payload","data","Error","serviceAccountJson","toString","JSON","projectId","project_id","clientEmail","client_email","error","String","resetInstance","shutdown","undefined","apps","length","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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/PushNotificationClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PushNotificationClient.d.ts","sourceRoot":"","sources":["../../../src/lib/clients/PushNotificationClient.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAyB;IAChD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,MAAM,CAA2C;IAEzD,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAa;IAEnC,OAAO;WAIO,WAAW,IAAI,sBAAsB;WAQrC,aAAa,IAAI,IAAI;IAK5B,QAAQ,IAAI,IAAI;IAOvB,OAAO,CAAC,cAAc;YA2BR,qBAAqB;YAmDrB,iBAAiB;YAsCjB,uBAAuB;IA8DxB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,GAAG,SAAS,EAAE,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAa7G,UAAU,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,mBAAmB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB1F,WAAW,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,mBAAmB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB3G,OAAO,CAAC,sBAAsB;IAajB,eAAe,CAAC,MAAM,EAAE;QACnC,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,mBAAmB,CAAC;QAClC,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,IAAI,CAAA;KAAE,CAAC;IAgCrB,gBAAgB,CAAC,MAAM,EAAE;QACpC,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,YAAY,EAAE,mBAAmB,CAAC;QAClC,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAiB9C,kBAAkB,CAAC,MAAM,EAAE;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDJ,mBAAmB,CAAC,MAAM,EAAE;QACvC,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CJ,cAAc,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,mBAAmB,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAyCjH,WAAW,CAAC,MAAM,EAAE;QAC/B,YAAY,EAAE,mBAAmB,CAAC;QAClC,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;QACpC,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAiDnC"}
@@ -36,6 +36,10 @@ export class PushNotificationClient {
36
36
  }
37
37
  getApnProvider() {
38
38
  if (!this.apnProvider) {
39
+ // Validate required APNs configuration
40
+ if (!this.config.APNS_PRIVATE_KEY || !this.config.APNS_KEY_ID || !this.config.APNS_TEAM_ID || !this.config.APNS_TOPIC) {
41
+ throw new ExternalAPIError("APNs configuration missing", "APNS_PRIVATE_KEY, APNS_KEY_ID, APNS_TEAM_ID, and APNS_TOPIC are required for APNs");
42
+ }
39
43
  this.logger.info("Initializing APNs provider", {
40
44
  production: this.config.APNS_PRODUCTION,
41
45
  topic: this.config.APNS_TOPIC
@@ -46,13 +50,16 @@ export class PushNotificationClient {
46
50
  keyId: this.config.APNS_KEY_ID,
47
51
  teamId: this.config.APNS_TEAM_ID
48
52
  },
49
- production: this.config.APNS_PRODUCTION
53
+ production: this.config.APNS_PRODUCTION ?? false
50
54
  });
51
55
  }
52
56
  return this.apnProvider;
53
57
  }
54
58
  async sendAppleNotification(deviceTokens, notification) {
55
59
  const provider = this.getApnProvider();
60
+ if (!this.config.APNS_TOPIC) {
61
+ throw new ExternalAPIError("APNs configuration missing", "APNS_TOPIC is required");
62
+ }
56
63
  const apnNotification = new apn.Notification();
57
64
  apnNotification.alert = {
58
65
  title: notification.title,
@@ -97,6 +104,10 @@ export class PushNotificationClient {
97
104
  if (this.fcmAccessToken && this.fcmTokenExpiry > now) {
98
105
  return this.fcmAccessToken;
99
106
  }
107
+ // Validate required FCM configuration
108
+ if (!this.config.FCM_CLIENT_EMAIL || !this.config.FCM_PRIVATE_KEY) {
109
+ throw new ExternalAPIError("FCM configuration missing", "FCM_CLIENT_EMAIL and FCM_PRIVATE_KEY are required for FCM");
110
+ }
100
111
  this.logger.info("Fetching new FCM access token");
101
112
  const jwtClient = new google.auth.JWT({
102
113
  email: this.config.FCM_CLIENT_EMAIL,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/clients/PushNotificationClient.ts"],"sourcesContent":["import apn from \"apn\";\nimport { google } from \"googleapis\";\nimport { z } from \"zod\";\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 PushNotificationConfig = z.infer<typeof CommonSchemas.pushNotification>;\n\nexport interface NotificationPayload {\n title: string;\n body: string;\n sound?: string;\n badge?: number;\n data?: Record<string, string>;\n redirectPath?: string;\n}\n\nexport class PushNotificationClient {\n private static instance: PushNotificationClient;\n private config: PushNotificationConfig;\n private logger = Log.getInstance().extend(\"push-client\");\n\n private apnProvider: apn.Provider | null = null;\n private fcmAccessToken: string | null = null;\n private fcmTokenExpiry: number = 0;\n\n private constructor(config: PushNotificationConfig) {\n this.config = config;\n }\n\n public static getInstance(): PushNotificationClient {\n if (!PushNotificationClient.instance) {\n const config = CommonSchemas.pushNotification.parse(process.env);\n PushNotificationClient.instance = new PushNotificationClient(config);\n }\n return PushNotificationClient.instance;\n }\n\n public static resetInstance(): void {\n PushNotificationClient.instance?.shutdown();\n PushNotificationClient.instance = undefined as any;\n }\n\n public shutdown(): void {\n if (this.apnProvider) {\n this.apnProvider.shutdown();\n this.apnProvider = null;\n }\n }\n\n private getApnProvider(): apn.Provider {\n if (!this.apnProvider) {\n this.logger.info(\"Initializing APNs provider\", {\n production: this.config.APNS_PRODUCTION,\n topic: this.config.APNS_TOPIC,\n });\n\n this.apnProvider = new apn.Provider({\n token: {\n key: this.config.APNS_PRIVATE_KEY,\n keyId: this.config.APNS_KEY_ID,\n teamId: this.config.APNS_TEAM_ID,\n },\n production: this.config.APNS_PRODUCTION,\n });\n }\n return this.apnProvider;\n }\n\n private async sendAppleNotification(deviceTokens: string[], notification: NotificationPayload): Promise<void> {\n const provider = this.getApnProvider();\n\n const apnNotification = new apn.Notification();\n apnNotification.alert = {\n title: notification.title,\n body: notification.body,\n };\n apnNotification.sound = notification.sound || \"default\";\n if (notification.badge !== undefined) {\n apnNotification.badge = notification.badge;\n }\n apnNotification.topic = this.config.APNS_TOPIC;\n apnNotification.payload = notification.data || {};\n\n if (notification.redirectPath) {\n apnNotification.urlArgs = [notification.redirectPath];\n }\n\n this.logger.info(\"Sending APNs notification\", {\n tokenCount: deviceTokens.length,\n title: notification.title,\n });\n\n try {\n const result = await provider.send(apnNotification, deviceTokens);\n\n if (result.failed.length > 0) {\n this.logger.warn(\"Some APNs notifications failed\", {\n failedCount: result.failed.length,\n failures: result.failed,\n });\n }\n\n this.logger.info(\"APNs notification sent\", {\n sent: result.sent.length,\n failed: result.failed.length,\n });\n } catch (error) {\n this.logger.error(\"APNs send error\", { error });\n throw new ExternalAPIError(\n \"Failed to send APNs notification\",\n `Service: APNs, Error: ${String(error)}`\n );\n }\n }\n\n private async getFcmAccessToken(): Promise<string> {\n const now = Date.now();\n\n if (this.fcmAccessToken && this.fcmTokenExpiry > now) {\n return this.fcmAccessToken;\n }\n\n this.logger.info(\"Fetching new FCM access token\");\n\n const jwtClient = new google.auth.JWT({\n email: this.config.FCM_CLIENT_EMAIL,\n key: this.config.FCM_PRIVATE_KEY,\n scopes: [\"https://www.googleapis.com/auth/firebase.messaging\"],\n });\n\n try {\n const tokens = await jwtClient.authorize();\n this.fcmAccessToken = tokens.access_token!;\n this.fcmTokenExpiry = now + 55 * 60 * 1000;\n\n return this.fcmAccessToken;\n } catch (error) {\n this.logger.error(\"FCM token fetch error\", { error });\n throw new ExternalAPIError(\n \"Failed to get FCM access token\",\n `Service: FCM, Error: ${String(error)}`\n );\n }\n }\n\n private async sendAndroidNotification(deviceTokens: string[], notification: NotificationPayload): Promise<void> {\n const accessToken = await this.getFcmAccessToken();\n\n this.logger.info(\"Sending FCM notification\", {\n tokenCount: deviceTokens.length,\n title: notification.title,\n });\n\n const messages = deviceTokens.map((token) => ({\n message: {\n token,\n notification: {\n title: notification.title,\n body: notification.body,\n },\n data: notification.data || {},\n android: {\n priority: \"high\" as const,\n notification: {\n sound: notification.sound || \"default\",\n channelId: \"default\",\n },\n },\n },\n }));\n\n const results = await Promise.allSettled(\n messages.map(async (msg) => {\n const response = await fetch(\n `https://fcm.googleapis.com/v1/projects/${this.config.FCM_PROJECT_ID}/messages:send`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(msg),\n }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`FCM error: ${error}`);\n }\n\n return response.json();\n })\n );\n\n const failed = results.filter((r) => r.status === \"rejected\").length;\n const succeeded = results.filter((r) => r.status === \"fulfilled\").length;\n\n if (failed > 0) {\n this.logger.warn(\"Some FCM notifications failed\", {\n succeeded,\n failed,\n });\n }\n\n this.logger.info(\"FCM notification sent\", { succeeded, failed });\n }\n\n public async sendToTokens(tokens: string[], platform: \"ios\" | \"android\", notification: NotificationPayload): Promise<void> {\n if (tokens.length === 0) {\n this.logger.warn(\"No tokens provided, skipping notification\");\n return;\n }\n\n if (platform === \"ios\") {\n await this.sendAppleNotification(tokens, notification);\n } else {\n await this.sendAndroidNotification(tokens, notification);\n }\n }\n\n public async sendToUser(params: { userUuid: string; notification: NotificationPayload }): Promise<void> {\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\", { userUuid: params.userUuid });\n return;\n }\n\n const iosTokens = deviceTokens.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n }\n\n public async sendToUsers(params: { userUuids: string[]; notification: NotificationPayload }): Promise<void> {\n this.logger.info(\"Sending notification to multiple users\", { count: params.userUuids.length });\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;\n }\n\n const iosTokens = deviceTokens.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n }\n\n private calculateScheduledTime(targetHour: number, targetMinute: number, userTimezone: string): Date {\n const now = new Date();\n const targetTime = new Date(now.toLocaleString(\"en-US\", { timeZone: userTimezone }));\n\n targetTime.setHours(targetHour, targetMinute, 0, 0);\n\n if (targetTime <= now) {\n targetTime.setDate(targetTime.getDate() + 1);\n }\n\n return targetTime;\n }\n\n public async scheduleForUser(params: {\n userUuid: string;\n notification: NotificationPayload;\n targetHour: number;\n targetMinute: number;\n }): Promise<{ scheduledAt: Date }> {\n this.logger.info(\"Scheduling notification for user\", {\n userUuid: params.userUuid,\n targetHour: params.targetHour,\n targetMinute: params.targetMinute,\n });\n\n const user = await PersistedUser.findByPk(params.userUuid, {\n attributes: [\"userUuid\", \"timeZone\"],\n });\n\n if (!user) {\n throw new ExternalAPIError(\n \"User not found\",\n `Service: PushNotificationClient, UserUuid: ${params.userUuid}`\n );\n }\n\n const scheduledAt = this.calculateScheduledTime(\n params.targetHour,\n params.targetMinute,\n (user as any).timeZone || \"UTC\"\n );\n\n this.logger.info(\"Notification scheduled\", {\n userUuid: params.userUuid,\n scheduledAt: scheduledAt.toISOString(),\n });\n\n return { scheduledAt };\n }\n\n public async scheduleForUsers(params: {\n userUuids: string[];\n notification: NotificationPayload;\n targetHour: number;\n targetMinute: number;\n }): Promise<Array<{ userUuid: string; scheduledAt: Date }>> {\n this.logger.info(\"Scheduling notifications for multiple users\", {\n count: params.userUuids.length,\n targetHour: params.targetHour,\n });\n\n const users = await PersistedUser.findAll({\n where: { userUuid: params.userUuids },\n attributes: [\"userUuid\", \"timeZone\"],\n });\n\n return users.map((user) => ({\n userUuid: user.userUuid,\n scheduledAt: this.calculateScheduledTime(params.targetHour, params.targetMinute, (user as any).timeZone || \"UTC\"),\n }));\n }\n\n public async sendTemplateToUser(params: {\n userUuid: string;\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<void> {\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 `Service: PushNotificationClient, Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find((t) => t.language === params.language);\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 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 public async sendTemplateToUsers(params: {\n userUuids: string[];\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<void> {\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 `Service: PushNotificationClient, Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find((t) => t.language === params.language);\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 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 public async sendToAllUsers(params: { notification: NotificationPayload; batchSize?: number }): 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\", {\n batchSize,\n });\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.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n\n totalSent += deviceTokens.length;\n offset += batchSize;\n\n this.logger.info(\"Broadcast batch sent\", {\n batchSent: deviceTokens.length,\n totalSent,\n });\n }\n\n this.logger.info(\"Broadcast notification complete\", { totalSent });\n return { totalSent };\n }\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.filter((dt: any) => dt.platform === \"ios\").map((dt: any) => dt.deviceToken);\n const androidTokens = allTokens.filter((dt: any) => dt.platform === \"android\").map((dt: any) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n\n totalSent += allTokens.length;\n offset += batchSize;\n\n this.logger.info(\"Group batch sent\", {\n batchSent: allTokens.length,\n totalSent,\n });\n }\n\n this.logger.info(\"Group notification complete\", { totalSent, group: params.group });\n return { totalSent };\n }\n}\n"],"names":["apn","google","CommonSchemas","ExternalAPIError","Log","DeviceTokens","PersistedUser","NotificationModels","TranslatedNotification","PushNotificationClient","instance","config","logger","getInstance","extend","apnProvider","fcmAccessToken","fcmTokenExpiry","pushNotification","parse","process","env","resetInstance","shutdown","undefined","getApnProvider","info","production","APNS_PRODUCTION","topic","APNS_TOPIC","Provider","token","key","APNS_PRIVATE_KEY","keyId","APNS_KEY_ID","teamId","APNS_TEAM_ID","sendAppleNotification","deviceTokens","notification","provider","apnNotification","Notification","alert","title","body","sound","badge","payload","data","redirectPath","urlArgs","tokenCount","length","result","send","failed","warn","failedCount","failures","sent","error","String","getFcmAccessToken","now","Date","jwtClient","auth","JWT","email","FCM_CLIENT_EMAIL","FCM_PRIVATE_KEY","scopes","tokens","authorize","access_token","sendAndroidNotification","accessToken","messages","map","message","android","priority","channelId","results","Promise","allSettled","msg","response","fetch","FCM_PROJECT_ID","method","headers","Authorization","JSON","stringify","ok","text","Error","json","filter","r","status","succeeded","sendToTokens","platform","sendToUser","params","userUuid","findAll","where","attributes","iosTokens","dt","deviceToken","androidTokens","all","resolve","sendToUsers","count","userUuids","calculateScheduledTime","targetHour","targetMinute","userTimezone","targetTime","toLocaleString","timeZone","setHours","setDate","getDate","scheduleForUser","user","findByPk","scheduledAt","toISOString","scheduleForUsers","users","sendTemplateToUser","notificationType","language","includeOptions","model","as","required","template","findOne","type","include","translation","translations","find","t","Object","entries","forEach","value","replace","RegExp","sendTemplateToUsers","sendToAllUsers","batchSize","offset","totalSent","limit","batchSent","sendToGroup","group","subscriptionType","allTokens","flatMap"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/lib/clients/PushNotificationClient.ts"],"sourcesContent":["import apn from \"apn\";\nimport { google } from \"googleapis\";\nimport { z } from \"zod\";\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 PushNotificationConfig = z.infer<typeof CommonSchemas.pushNotification>;\n\nexport interface NotificationPayload {\n title: string;\n body: string;\n sound?: string;\n badge?: number;\n data?: Record<string, string>;\n redirectPath?: string;\n}\n\nexport class PushNotificationClient {\n private static instance: PushNotificationClient;\n private config: PushNotificationConfig;\n private logger = Log.getInstance().extend(\"push-client\");\n\n private apnProvider: apn.Provider | null = null;\n private fcmAccessToken: string | null = null;\n private fcmTokenExpiry: number = 0;\n\n private constructor(config: PushNotificationConfig) {\n this.config = config;\n }\n\n public static getInstance(): PushNotificationClient {\n if (!PushNotificationClient.instance) {\n const config = CommonSchemas.pushNotification.parse(process.env);\n PushNotificationClient.instance = new PushNotificationClient(config);\n }\n return PushNotificationClient.instance;\n }\n\n public static resetInstance(): void {\n PushNotificationClient.instance?.shutdown();\n PushNotificationClient.instance = undefined as any;\n }\n\n public shutdown(): void {\n if (this.apnProvider) {\n this.apnProvider.shutdown();\n this.apnProvider = null;\n }\n }\n\n private getApnProvider(): apn.Provider {\n if (!this.apnProvider) {\n // Validate required APNs configuration\n if (!this.config.APNS_PRIVATE_KEY || !this.config.APNS_KEY_ID || !this.config.APNS_TEAM_ID || !this.config.APNS_TOPIC) {\n throw new ExternalAPIError(\n \"APNs configuration missing\",\n \"APNS_PRIVATE_KEY, APNS_KEY_ID, APNS_TEAM_ID, and APNS_TOPIC are required for APNs\"\n );\n }\n\n this.logger.info(\"Initializing APNs provider\", {\n production: this.config.APNS_PRODUCTION,\n topic: this.config.APNS_TOPIC,\n });\n\n this.apnProvider = new apn.Provider({\n token: {\n key: this.config.APNS_PRIVATE_KEY,\n keyId: this.config.APNS_KEY_ID,\n teamId: this.config.APNS_TEAM_ID,\n },\n production: this.config.APNS_PRODUCTION ?? false,\n });\n }\n return this.apnProvider;\n }\n\n private async sendAppleNotification(deviceTokens: string[], notification: NotificationPayload): Promise<void> {\n const provider = this.getApnProvider();\n\n if (!this.config.APNS_TOPIC) {\n throw new ExternalAPIError(\"APNs configuration missing\", \"APNS_TOPIC is required\");\n }\n\n const apnNotification = new apn.Notification();\n apnNotification.alert = {\n title: notification.title,\n body: notification.body,\n };\n apnNotification.sound = notification.sound || \"default\";\n if (notification.badge !== undefined) {\n apnNotification.badge = notification.badge;\n }\n apnNotification.topic = this.config.APNS_TOPIC;\n apnNotification.payload = notification.data || {};\n\n if (notification.redirectPath) {\n apnNotification.urlArgs = [notification.redirectPath];\n }\n\n this.logger.info(\"Sending APNs notification\", {\n tokenCount: deviceTokens.length,\n title: notification.title,\n });\n\n try {\n const result = await provider.send(apnNotification, deviceTokens);\n\n if (result.failed.length > 0) {\n this.logger.warn(\"Some APNs notifications failed\", {\n failedCount: result.failed.length,\n failures: result.failed,\n });\n }\n\n this.logger.info(\"APNs notification sent\", {\n sent: result.sent.length,\n failed: result.failed.length,\n });\n } catch (error) {\n this.logger.error(\"APNs send error\", { error });\n throw new ExternalAPIError(\n \"Failed to send APNs notification\",\n `Service: APNs, Error: ${String(error)}`\n );\n }\n }\n\n private async getFcmAccessToken(): Promise<string> {\n const now = Date.now();\n\n if (this.fcmAccessToken && this.fcmTokenExpiry > now) {\n return this.fcmAccessToken;\n }\n\n // Validate required FCM configuration\n if (!this.config.FCM_CLIENT_EMAIL || !this.config.FCM_PRIVATE_KEY) {\n throw new ExternalAPIError(\n \"FCM configuration missing\",\n \"FCM_CLIENT_EMAIL and FCM_PRIVATE_KEY are required for FCM\"\n );\n }\n\n this.logger.info(\"Fetching new FCM access token\");\n\n const jwtClient = new google.auth.JWT({\n email: this.config.FCM_CLIENT_EMAIL,\n key: this.config.FCM_PRIVATE_KEY,\n scopes: [\"https://www.googleapis.com/auth/firebase.messaging\"],\n });\n\n try {\n const tokens = await jwtClient.authorize();\n this.fcmAccessToken = tokens.access_token!;\n this.fcmTokenExpiry = now + 55 * 60 * 1000;\n\n return this.fcmAccessToken;\n } catch (error) {\n this.logger.error(\"FCM token fetch error\", { error });\n throw new ExternalAPIError(\n \"Failed to get FCM access token\",\n `Service: FCM, Error: ${String(error)}`\n );\n }\n }\n\n private async sendAndroidNotification(deviceTokens: string[], notification: NotificationPayload): Promise<void> {\n const accessToken = await this.getFcmAccessToken();\n\n this.logger.info(\"Sending FCM notification\", {\n tokenCount: deviceTokens.length,\n title: notification.title,\n });\n\n const messages = deviceTokens.map((token) => ({\n message: {\n token,\n notification: {\n title: notification.title,\n body: notification.body,\n },\n data: notification.data || {},\n android: {\n priority: \"high\" as const,\n notification: {\n sound: notification.sound || \"default\",\n channelId: \"default\",\n },\n },\n },\n }));\n\n const results = await Promise.allSettled(\n messages.map(async (msg) => {\n const response = await fetch(\n `https://fcm.googleapis.com/v1/projects/${this.config.FCM_PROJECT_ID}/messages:send`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(msg),\n }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`FCM error: ${error}`);\n }\n\n return response.json();\n })\n );\n\n const failed = results.filter((r) => r.status === \"rejected\").length;\n const succeeded = results.filter((r) => r.status === \"fulfilled\").length;\n\n if (failed > 0) {\n this.logger.warn(\"Some FCM notifications failed\", {\n succeeded,\n failed,\n });\n }\n\n this.logger.info(\"FCM notification sent\", { succeeded, failed });\n }\n\n public async sendToTokens(tokens: string[], platform: \"ios\" | \"android\", notification: NotificationPayload): Promise<void> {\n if (tokens.length === 0) {\n this.logger.warn(\"No tokens provided, skipping notification\");\n return;\n }\n\n if (platform === \"ios\") {\n await this.sendAppleNotification(tokens, notification);\n } else {\n await this.sendAndroidNotification(tokens, notification);\n }\n }\n\n public async sendToUser(params: { userUuid: string; notification: NotificationPayload }): Promise<void> {\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\", { userUuid: params.userUuid });\n return;\n }\n\n const iosTokens = deviceTokens.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n }\n\n public async sendToUsers(params: { userUuids: string[]; notification: NotificationPayload }): Promise<void> {\n this.logger.info(\"Sending notification to multiple users\", { count: params.userUuids.length });\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;\n }\n\n const iosTokens = deviceTokens.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n }\n\n private calculateScheduledTime(targetHour: number, targetMinute: number, userTimezone: string): Date {\n const now = new Date();\n const targetTime = new Date(now.toLocaleString(\"en-US\", { timeZone: userTimezone }));\n\n targetTime.setHours(targetHour, targetMinute, 0, 0);\n\n if (targetTime <= now) {\n targetTime.setDate(targetTime.getDate() + 1);\n }\n\n return targetTime;\n }\n\n public async scheduleForUser(params: {\n userUuid: string;\n notification: NotificationPayload;\n targetHour: number;\n targetMinute: number;\n }): Promise<{ scheduledAt: Date }> {\n this.logger.info(\"Scheduling notification for user\", {\n userUuid: params.userUuid,\n targetHour: params.targetHour,\n targetMinute: params.targetMinute,\n });\n\n const user = await PersistedUser.findByPk(params.userUuid, {\n attributes: [\"userUuid\", \"timeZone\"],\n });\n\n if (!user) {\n throw new ExternalAPIError(\n \"User not found\",\n `Service: PushNotificationClient, UserUuid: ${params.userUuid}`\n );\n }\n\n const scheduledAt = this.calculateScheduledTime(\n params.targetHour,\n params.targetMinute,\n (user as any).timeZone || \"UTC\"\n );\n\n this.logger.info(\"Notification scheduled\", {\n userUuid: params.userUuid,\n scheduledAt: scheduledAt.toISOString(),\n });\n\n return { scheduledAt };\n }\n\n public async scheduleForUsers(params: {\n userUuids: string[];\n notification: NotificationPayload;\n targetHour: number;\n targetMinute: number;\n }): Promise<Array<{ userUuid: string; scheduledAt: Date }>> {\n this.logger.info(\"Scheduling notifications for multiple users\", {\n count: params.userUuids.length,\n targetHour: params.targetHour,\n });\n\n const users = await PersistedUser.findAll({\n where: { userUuid: params.userUuids },\n attributes: [\"userUuid\", \"timeZone\"],\n });\n\n return users.map((user) => ({\n userUuid: user.userUuid,\n scheduledAt: this.calculateScheduledTime(params.targetHour, params.targetMinute, (user as any).timeZone || \"UTC\"),\n }));\n }\n\n public async sendTemplateToUser(params: {\n userUuid: string;\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<void> {\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 `Service: PushNotificationClient, Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find((t) => t.language === params.language);\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 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 public async sendTemplateToUsers(params: {\n userUuids: string[];\n notificationType: string;\n language?: string;\n data?: Record<string, string>;\n }): Promise<void> {\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 `Service: PushNotificationClient, Type: ${params.notificationType}`\n );\n }\n\n const translation = template.translations?.find((t) => t.language === params.language);\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 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 public async sendToAllUsers(params: { notification: NotificationPayload; batchSize?: number }): 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\", {\n batchSize,\n });\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.filter((dt) => dt.platform === \"ios\").map((dt) => dt.deviceToken);\n const androidTokens = deviceTokens.filter((dt) => dt.platform === \"android\").map((dt) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n\n totalSent += deviceTokens.length;\n offset += batchSize;\n\n this.logger.info(\"Broadcast batch sent\", {\n batchSent: deviceTokens.length,\n totalSent,\n });\n }\n\n this.logger.info(\"Broadcast notification complete\", { totalSent });\n return { totalSent };\n }\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.filter((dt: any) => dt.platform === \"ios\").map((dt: any) => dt.deviceToken);\n const androidTokens = allTokens.filter((dt: any) => dt.platform === \"android\").map((dt: any) => dt.deviceToken);\n\n await Promise.all([\n iosTokens.length > 0 ? this.sendToTokens(iosTokens, \"ios\", params.notification) : Promise.resolve(),\n androidTokens.length > 0 ? this.sendToTokens(androidTokens, \"android\", params.notification) : Promise.resolve(),\n ]);\n\n totalSent += allTokens.length;\n offset += batchSize;\n\n this.logger.info(\"Group batch sent\", {\n batchSent: allTokens.length,\n totalSent,\n });\n }\n\n this.logger.info(\"Group notification complete\", { totalSent, group: params.group });\n return { totalSent };\n }\n}\n"],"names":["apn","google","CommonSchemas","ExternalAPIError","Log","DeviceTokens","PersistedUser","NotificationModels","TranslatedNotification","PushNotificationClient","instance","config","logger","getInstance","extend","apnProvider","fcmAccessToken","fcmTokenExpiry","pushNotification","parse","process","env","resetInstance","shutdown","undefined","getApnProvider","APNS_PRIVATE_KEY","APNS_KEY_ID","APNS_TEAM_ID","APNS_TOPIC","info","production","APNS_PRODUCTION","topic","Provider","token","key","keyId","teamId","sendAppleNotification","deviceTokens","notification","provider","apnNotification","Notification","alert","title","body","sound","badge","payload","data","redirectPath","urlArgs","tokenCount","length","result","send","failed","warn","failedCount","failures","sent","error","String","getFcmAccessToken","now","Date","FCM_CLIENT_EMAIL","FCM_PRIVATE_KEY","jwtClient","auth","JWT","email","scopes","tokens","authorize","access_token","sendAndroidNotification","accessToken","messages","map","message","android","priority","channelId","results","Promise","allSettled","msg","response","fetch","FCM_PROJECT_ID","method","headers","Authorization","JSON","stringify","ok","text","Error","json","filter","r","status","succeeded","sendToTokens","platform","sendToUser","params","userUuid","findAll","where","attributes","iosTokens","dt","deviceToken","androidTokens","all","resolve","sendToUsers","count","userUuids","calculateScheduledTime","targetHour","targetMinute","userTimezone","targetTime","toLocaleString","timeZone","setHours","setDate","getDate","scheduleForUser","user","findByPk","scheduledAt","toISOString","scheduleForUsers","users","sendTemplateToUser","notificationType","language","includeOptions","model","as","required","template","findOne","type","include","translation","translations","find","t","Object","entries","forEach","value","replace","RegExp","sendTemplateToUsers","sendToAllUsers","batchSize","offset","totalSent","limit","batchSent","sendToGroup","group","subscriptionType","allTokens","flatMap"],"mappings":"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"}
@@ -91,19 +91,19 @@ export declare const CommonSchemas: {
91
91
  POSTMARK_TIMEOUT_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
92
92
  }, z.core.$strip>;
93
93
  pushNotification: z.ZodObject<{
94
- APNS_KEY_ID: z.ZodString;
95
- APNS_TEAM_ID: z.ZodString;
96
- APNS_TOPIC: z.ZodString;
97
- APNS_PRIVATE_KEY: z.ZodString;
98
- APNS_PRODUCTION: z.ZodDefault<z.ZodUnion<[z.ZodPipe<z.ZodEnum<{
94
+ APNS_KEY_ID: z.ZodOptional<z.ZodString>;
95
+ APNS_TEAM_ID: z.ZodOptional<z.ZodString>;
96
+ APNS_TOPIC: z.ZodOptional<z.ZodString>;
97
+ APNS_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
98
+ APNS_PRODUCTION: z.ZodOptional<z.ZodDefault<z.ZodUnion<[z.ZodPipe<z.ZodEnum<{
99
99
  0: "0";
100
100
  1: "1";
101
101
  true: "true";
102
102
  false: "false";
103
- }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>, z.ZodBoolean]>>;
104
- FCM_PROJECT_ID: z.ZodString;
105
- FCM_CLIENT_EMAIL: z.ZodString;
106
- FCM_PRIVATE_KEY: z.ZodString;
103
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>, z.ZodBoolean]>>>;
104
+ FCM_PROJECT_ID: z.ZodOptional<z.ZodString>;
105
+ FCM_CLIENT_EMAIL: z.ZodOptional<z.ZodString>;
106
+ FCM_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
107
107
  }, z.core.$strip>;
108
108
  };
109
109
  //# sourceMappingURL=ConfigValidator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigValidator.d.ts","sourceRoot":"","sources":["../../../src/lib/config/ConfigValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAoB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE3D,qBAAa,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAI;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,CAAC,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe;IAK5E,OAAO,CAAC,QAAQ;IAchB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAIxD,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAIrB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO;IAIjD,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAI/F,YAAY,IAAI,OAAO;IAIvB,aAAa,IAAI,OAAO;IAIxB,SAAS,IAAI,OAAO;IAIpB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;CAQlB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aACE,gBAAgB,EAAE,CAAC,CAAC,QAAQ,EAAE;gBAA/D,OAAO,EAAE,MAAM,EAAkB,gBAAgB,EAAE,CAAC,CAAC,QAAQ,EAAE;CAI5E;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAC5C,MAAM,EAAE,CAAC,EACT,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACzC,eAAe,CAAC,CAAC,CAAC,CAEpB;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EzB,CAAC"}
1
+ {"version":3,"file":"ConfigValidator.d.ts","sourceRoot":"","sources":["../../../src/lib/config/ConfigValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAoB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE3D,qBAAa,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAI;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,CAAC,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe;IAK5E,OAAO,CAAC,QAAQ;IAchB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAIxD,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAIrB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO;IAIjD,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAI/F,YAAY,IAAI,OAAO;IAIvB,aAAa,IAAI,OAAO;IAIxB,SAAS,IAAI,OAAO;IAIpB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;CAQlB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aACE,gBAAgB,EAAE,CAAC,CAAC,QAAQ,EAAE;gBAA/D,OAAO,EAAE,MAAM,EAAkB,gBAAgB,EAAE,CAAC,CAAC,QAAQ,EAAE;CAI5E;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAC5C,MAAM,EAAE,CAAC,EACT,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACzC,eAAe,CAAC,CAAC,CAAC,CAEpB;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4EzB,CAAC"}
@@ -140,21 +140,22 @@ export const CommonSchemas = {
140
140
  POSTMARK_TIMEOUT_MS: z.coerce.number().positive().default(10000)
141
141
  }),
142
142
  pushNotification: z.object({
143
- // APNs Configuration
144
- APNS_KEY_ID: z.string().min(1, "APNs Key ID is required"),
145
- APNS_TEAM_ID: z.string().min(1, "APNs Team ID is required"),
146
- APNS_TOPIC: z.string().min(1, "APNs Topic (Bundle ID) is required"),
147
- APNS_PRIVATE_KEY: z.string().min(1, "APNs Private Key is required"),
143
+ // APNs Configuration (DEPRECATED - use Firebase instead)
144
+ APNS_KEY_ID: z.string().optional(),
145
+ APNS_TEAM_ID: z.string().optional(),
146
+ APNS_TOPIC: z.string().optional(),
147
+ APNS_PRIVATE_KEY: z.string().optional(),
148
148
  APNS_PRODUCTION: z.enum([
149
149
  "true",
150
150
  "false",
151
151
  "1",
152
152
  "0"
153
- ]).transform((val)=>val === "true" || val === "1").or(z.boolean()).default(false),
154
- // FCM Configuration
155
- FCM_PROJECT_ID: z.string().min(1, "FCM Project ID is required"),
156
- FCM_CLIENT_EMAIL: z.string().email("Valid FCM client email is required"),
157
- FCM_PRIVATE_KEY: z.string().min(1, "FCM Private Key is required")
153
+ ]).transform((val)=>val === "true" || val === "1").or(z.boolean()).default(false).optional(),
154
+ // FCM Configuration (Firebase Admin SDK)
155
+ // Note: These are now loaded from GCP Secret Manager, not environment variables
156
+ FCM_PROJECT_ID: z.string().optional(),
157
+ FCM_CLIENT_EMAIL: z.string().optional(),
158
+ FCM_PRIVATE_KEY: z.string().optional()
158
159
  })
159
160
  };
160
161
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/config/ConfigValidator.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {Log} from \"../utils/Logger.js\";\n\nconst logger: Log = Log.getInstance().extend(\"config-validator\");\n\n\nexport const BaseConfigSchema = z.object({\n // Environment\n NODE_ENV: z.enum([\"development\", \"staging\", \"production\"]).default(\"development\"),\n PORT: z.coerce.number().min(1000).max(65535).default(3000),\n\n // Database\n DB_POOL_MAX: z.coerce.number().min(1).max(100).default(20),\n DB_POOL_MIN: z.coerce.number().min(0).max(50).default(5),\n\n // Redis\n REDIS_URL: z.url(\"REDIS_URL must be a valid URL\").optional(),\n\n // JWT Authentication\n ACCESS_TOKEN_SECRET: z.string().min(20, \"JWT_SECRET must be at least 32 characters for security\"),\n ACCESS_TOKEN_EXPIRY: z.string().default(\"1hr\"),\n REFRESH_TOKEN_SECRET: z.string().min(20, 'REFRESH_TOKEN_SECRET must be at least 32 characters for security'),\n REFRESH_TOKEN_EXPIRY: z.string().default(\"30d\"),\n\n // Logging\n LOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n});\n\nexport type BaseConfig = z.output<typeof BaseConfigSchema>;\n\nexport class ConfigValidator<T extends z.ZodType> {\n private readonly schema: T;\n private readonly config: z.output<T>;\n\n constructor(schema: T, env: Record<string, string | undefined> = process.env) {\n this.schema = schema;\n this.config = this.validate(env);\n }\n\n private validate(env: Record<string, string | undefined>): z.output<T> {\n try {\n const validated = this.schema.parse(env);\n this.logSuccess();\n return validated;\n } catch (error) {\n if (error instanceof z.ZodError) {\n this.logErrors(error);\n throw new ConfigurationError(\"Configuration validation failed\", error.issues);\n }\n throw error;\n }\n }\n\n get<K extends keyof z.output<T>>(key: K): z.output<T>[K] {\n return this.config[key];\n }\n\n getAll(): z.output<T> {\n return this.config;\n }\n\n has<K extends keyof z.output<T>>(key: K): boolean {\n return this.config[key] !== undefined;\n }\n\n getOrDefault<K extends keyof z.output<T>>(key: K, defaultValue: z.output<T>[K]): z.output<T>[K] {\n return this.config[key] ?? defaultValue;\n }\n\n isProduction(): boolean {\n return (this.config as any).NODE_ENV === \"production\";\n }\n\n isDevelopment(): boolean {\n return (this.config as any).NODE_ENV === \"development\";\n }\n\n isStaging(): boolean {\n return (this.config as any).NODE_ENV === \"staging\";\n }\n\n private logSuccess(): void {\n logger.info(\"✅ Configuration validated successfully\");\n }\n\n private logErrors(error: z.ZodError): void {\n logger.error(\"\\n❌ Configuration validation failed:\\n\");\n error.issues.forEach((issue) => {\n const path = issue.path.join(\".\");\n logger.error(` ❌ ${path}: ${issue.message}`);\n });\n logger.error(\"\\n💡 Fix these issues in your .env file and restart the service.\\n\");\n }\n}\n\nexport class ConfigurationError extends Error {\n constructor(message: string, public readonly validationErrors: z.ZodIssue[]) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\nexport function createConfig<T extends z.ZodType>(\n schema: T,\n env?: Record<string, string | undefined>\n): ConfigValidator<T> {\n return new ConfigValidator(schema, env);\n}\n\nexport const CommonSchemas = {\n database: z.object({\n DATABASE_URL: z.url(),\n DB_POOL_MAX: z.coerce.number().default(20),\n DB_POOL_MIN: z.coerce.number().default(5),\n DB_ACQUIRE_TIMEOUT: z.coerce.number().default(30000),\n DB_IDLE_TIMEOUT: z.coerce.number().default(10000),\n }),\n\n redis: z.object({\n REDIS_URL: z.url(),\n REDIS_MAX_RETRIES: z.coerce.number().default(3),\n REDIS_RETRY_DELAY: z.coerce.number().default(1000),\n }),\n\n jwt: z.object({\n JWT_SECRET: z.string().min(32),\n JWT_EXPIRY: z.string().default(\"1h\"),\n REFRESH_TOKEN_EXPIRY: z.string().default(\"7d\"),\n JWT_ISSUER: z.string().optional(),\n JWT_AUDIENCE: z.string().optional(),\n }),\n\n googleOAuth: z.object({\n GOOGLE_CLIENT_ID: z.string().min(1),\n GOOGLE_CLIENT_SECRET: z.string().min(1),\n GOOGLE_REDIRECT_URI: z.string().url().optional(),\n }),\n\n appleOAuth: z.object({\n APPLE_TEAM_ID: z.string().min(1),\n APPLE_CLIENT_ID: z.string().min(1),\n APPLE_KEY_ID: z.string().min(1),\n APPLE_PRIVATE_KEY_PATH: z.string().min(1),\n }),\n\n serviceUrls: z.object({\n AUTH_SERVICE_URL: z.url(),\n NOTIFICATION_SERVICE_URL: z.url(),\n PROGRAM_SERVICE_URL: z.url(),\n RESOURCE_SERVICE_URL: z.url(),\n IMAGE_SERVICE_URL: z.url(),\n }),\n\n featureFlags: z.object({\n ENABLE_DISTRIBUTED_TRACING: z.coerce.boolean().default(false),\n ENABLE_RATE_LIMITING: z.coerce.boolean().default(true),\n ENABLE_REQUEST_LOGGING: z.coerce.boolean().default(true),\n }),\n\n postmark: z.object({\n POSTMARK_API_KEY: z.string().min(1, \"Postmark API key is required\"),\n POSTMARK_FROM_EMAIL: z.string().email(\"Valid from email is required\"),\n POSTMARK_REPLY_TO_EMAIL: z.string().email().optional(),\n POSTMARK_TIMEOUT_MS: z.coerce.number().positive().default(10000),\n }),\n\n pushNotification: z.object({\n // APNs Configuration\n APNS_KEY_ID: z.string().min(1, \"APNs Key ID is required\"),\n APNS_TEAM_ID: z.string().min(1, \"APNs Team ID is required\"),\n APNS_TOPIC: z.string().min(1, \"APNs Topic (Bundle ID) is required\"),\n APNS_PRIVATE_KEY: z.string().min(1, \"APNs Private Key is required\"),\n APNS_PRODUCTION: z\n .enum([\"true\", \"false\", \"1\", \"0\"])\n .transform((val) => val === \"true\" || val === \"1\")\n .or(z.boolean())\n .default(false),\n\n // FCM Configuration\n FCM_PROJECT_ID: z.string().min(1, \"FCM Project ID is required\"),\n FCM_CLIENT_EMAIL: z.string().email(\"Valid FCM client email is required\"),\n FCM_PRIVATE_KEY: z.string().min(1, \"FCM Private Key is required\"),\n }),\n};"],"names":["z","Log","logger","getInstance","extend","BaseConfigSchema","object","NODE_ENV","enum","default","PORT","coerce","number","min","max","DB_POOL_MAX","DB_POOL_MIN","REDIS_URL","url","optional","ACCESS_TOKEN_SECRET","string","ACCESS_TOKEN_EXPIRY","REFRESH_TOKEN_SECRET","REFRESH_TOKEN_EXPIRY","LOG_LEVEL","ConfigValidator","schema","config","env","process","validate","validated","parse","logSuccess","error","ZodError","logErrors","ConfigurationError","issues","get","key","getAll","has","undefined","getOrDefault","defaultValue","isProduction","isDevelopment","isStaging","info","forEach","issue","path","join","message","Error","validationErrors","name","createConfig","CommonSchemas","database","DATABASE_URL","DB_ACQUIRE_TIMEOUT","DB_IDLE_TIMEOUT","redis","REDIS_MAX_RETRIES","REDIS_RETRY_DELAY","jwt","JWT_SECRET","JWT_EXPIRY","JWT_ISSUER","JWT_AUDIENCE","googleOAuth","GOOGLE_CLIENT_ID","GOOGLE_CLIENT_SECRET","GOOGLE_REDIRECT_URI","appleOAuth","APPLE_TEAM_ID","APPLE_CLIENT_ID","APPLE_KEY_ID","APPLE_PRIVATE_KEY_PATH","serviceUrls","AUTH_SERVICE_URL","NOTIFICATION_SERVICE_URL","PROGRAM_SERVICE_URL","RESOURCE_SERVICE_URL","IMAGE_SERVICE_URL","featureFlags","ENABLE_DISTRIBUTED_TRACING","boolean","ENABLE_RATE_LIMITING","ENABLE_REQUEST_LOGGING","postmark","POSTMARK_API_KEY","POSTMARK_FROM_EMAIL","email","POSTMARK_REPLY_TO_EMAIL","POSTMARK_TIMEOUT_MS","positive","pushNotification","APNS_KEY_ID","APNS_TEAM_ID","APNS_TOPIC","APNS_PRIVATE_KEY","APNS_PRODUCTION","transform","val","or","FCM_PROJECT_ID","FCM_CLIENT_EMAIL","FCM_PRIVATE_KEY"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/lib/config/ConfigValidator.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {Log} from \"../utils/Logger.js\";\n\nconst logger: Log = Log.getInstance().extend(\"config-validator\");\n\n\nexport const BaseConfigSchema = z.object({\n // Environment\n NODE_ENV: z.enum([\"development\", \"staging\", \"production\"]).default(\"development\"),\n PORT: z.coerce.number().min(1000).max(65535).default(3000),\n\n // Database\n DB_POOL_MAX: z.coerce.number().min(1).max(100).default(20),\n DB_POOL_MIN: z.coerce.number().min(0).max(50).default(5),\n\n // Redis\n REDIS_URL: z.url(\"REDIS_URL must be a valid URL\").optional(),\n\n // JWT Authentication\n ACCESS_TOKEN_SECRET: z.string().min(20, \"JWT_SECRET must be at least 32 characters for security\"),\n ACCESS_TOKEN_EXPIRY: z.string().default(\"1hr\"),\n REFRESH_TOKEN_SECRET: z.string().min(20, 'REFRESH_TOKEN_SECRET must be at least 32 characters for security'),\n REFRESH_TOKEN_EXPIRY: z.string().default(\"30d\"),\n\n // Logging\n LOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n});\n\nexport type BaseConfig = z.output<typeof BaseConfigSchema>;\n\nexport class ConfigValidator<T extends z.ZodType> {\n private readonly schema: T;\n private readonly config: z.output<T>;\n\n constructor(schema: T, env: Record<string, string | undefined> = process.env) {\n this.schema = schema;\n this.config = this.validate(env);\n }\n\n private validate(env: Record<string, string | undefined>): z.output<T> {\n try {\n const validated = this.schema.parse(env);\n this.logSuccess();\n return validated;\n } catch (error) {\n if (error instanceof z.ZodError) {\n this.logErrors(error);\n throw new ConfigurationError(\"Configuration validation failed\", error.issues);\n }\n throw error;\n }\n }\n\n get<K extends keyof z.output<T>>(key: K): z.output<T>[K] {\n return this.config[key];\n }\n\n getAll(): z.output<T> {\n return this.config;\n }\n\n has<K extends keyof z.output<T>>(key: K): boolean {\n return this.config[key] !== undefined;\n }\n\n getOrDefault<K extends keyof z.output<T>>(key: K, defaultValue: z.output<T>[K]): z.output<T>[K] {\n return this.config[key] ?? defaultValue;\n }\n\n isProduction(): boolean {\n return (this.config as any).NODE_ENV === \"production\";\n }\n\n isDevelopment(): boolean {\n return (this.config as any).NODE_ENV === \"development\";\n }\n\n isStaging(): boolean {\n return (this.config as any).NODE_ENV === \"staging\";\n }\n\n private logSuccess(): void {\n logger.info(\"✅ Configuration validated successfully\");\n }\n\n private logErrors(error: z.ZodError): void {\n logger.error(\"\\n❌ Configuration validation failed:\\n\");\n error.issues.forEach((issue) => {\n const path = issue.path.join(\".\");\n logger.error(` ❌ ${path}: ${issue.message}`);\n });\n logger.error(\"\\n💡 Fix these issues in your .env file and restart the service.\\n\");\n }\n}\n\nexport class ConfigurationError extends Error {\n constructor(message: string, public readonly validationErrors: z.ZodIssue[]) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\nexport function createConfig<T extends z.ZodType>(\n schema: T,\n env?: Record<string, string | undefined>\n): ConfigValidator<T> {\n return new ConfigValidator(schema, env);\n}\n\nexport const CommonSchemas = {\n database: z.object({\n DATABASE_URL: z.url(),\n DB_POOL_MAX: z.coerce.number().default(20),\n DB_POOL_MIN: z.coerce.number().default(5),\n DB_ACQUIRE_TIMEOUT: z.coerce.number().default(30000),\n DB_IDLE_TIMEOUT: z.coerce.number().default(10000),\n }),\n\n redis: z.object({\n REDIS_URL: z.url(),\n REDIS_MAX_RETRIES: z.coerce.number().default(3),\n REDIS_RETRY_DELAY: z.coerce.number().default(1000),\n }),\n\n jwt: z.object({\n JWT_SECRET: z.string().min(32),\n JWT_EXPIRY: z.string().default(\"1h\"),\n REFRESH_TOKEN_EXPIRY: z.string().default(\"7d\"),\n JWT_ISSUER: z.string().optional(),\n JWT_AUDIENCE: z.string().optional(),\n }),\n\n googleOAuth: z.object({\n GOOGLE_CLIENT_ID: z.string().min(1),\n GOOGLE_CLIENT_SECRET: z.string().min(1),\n GOOGLE_REDIRECT_URI: z.string().url().optional(),\n }),\n\n appleOAuth: z.object({\n APPLE_TEAM_ID: z.string().min(1),\n APPLE_CLIENT_ID: z.string().min(1),\n APPLE_KEY_ID: z.string().min(1),\n APPLE_PRIVATE_KEY_PATH: z.string().min(1),\n }),\n\n serviceUrls: z.object({\n AUTH_SERVICE_URL: z.url(),\n NOTIFICATION_SERVICE_URL: z.url(),\n PROGRAM_SERVICE_URL: z.url(),\n RESOURCE_SERVICE_URL: z.url(),\n IMAGE_SERVICE_URL: z.url(),\n }),\n\n featureFlags: z.object({\n ENABLE_DISTRIBUTED_TRACING: z.coerce.boolean().default(false),\n ENABLE_RATE_LIMITING: z.coerce.boolean().default(true),\n ENABLE_REQUEST_LOGGING: z.coerce.boolean().default(true),\n }),\n\n postmark: z.object({\n POSTMARK_API_KEY: z.string().min(1, \"Postmark API key is required\"),\n POSTMARK_FROM_EMAIL: z.string().email(\"Valid from email is required\"),\n POSTMARK_REPLY_TO_EMAIL: z.string().email().optional(),\n POSTMARK_TIMEOUT_MS: z.coerce.number().positive().default(10000),\n }),\n\n pushNotification: z.object({\n // APNs Configuration (DEPRECATED - use Firebase instead)\n APNS_KEY_ID: z.string().optional(),\n APNS_TEAM_ID: z.string().optional(),\n APNS_TOPIC: z.string().optional(),\n APNS_PRIVATE_KEY: z.string().optional(),\n APNS_PRODUCTION: z\n .enum([\"true\", \"false\", \"1\", \"0\"])\n .transform((val) => val === \"true\" || val === \"1\")\n .or(z.boolean())\n .default(false)\n .optional(),\n\n // FCM Configuration (Firebase Admin SDK)\n // Note: These are now loaded from GCP Secret Manager, not environment variables\n FCM_PROJECT_ID: z.string().optional(),\n FCM_CLIENT_EMAIL: z.string().optional(),\n FCM_PRIVATE_KEY: z.string().optional(),\n }),\n};"],"names":["z","Log","logger","getInstance","extend","BaseConfigSchema","object","NODE_ENV","enum","default","PORT","coerce","number","min","max","DB_POOL_MAX","DB_POOL_MIN","REDIS_URL","url","optional","ACCESS_TOKEN_SECRET","string","ACCESS_TOKEN_EXPIRY","REFRESH_TOKEN_SECRET","REFRESH_TOKEN_EXPIRY","LOG_LEVEL","ConfigValidator","schema","config","env","process","validate","validated","parse","logSuccess","error","ZodError","logErrors","ConfigurationError","issues","get","key","getAll","has","undefined","getOrDefault","defaultValue","isProduction","isDevelopment","isStaging","info","forEach","issue","path","join","message","Error","validationErrors","name","createConfig","CommonSchemas","database","DATABASE_URL","DB_ACQUIRE_TIMEOUT","DB_IDLE_TIMEOUT","redis","REDIS_MAX_RETRIES","REDIS_RETRY_DELAY","jwt","JWT_SECRET","JWT_EXPIRY","JWT_ISSUER","JWT_AUDIENCE","googleOAuth","GOOGLE_CLIENT_ID","GOOGLE_CLIENT_SECRET","GOOGLE_REDIRECT_URI","appleOAuth","APPLE_TEAM_ID","APPLE_CLIENT_ID","APPLE_KEY_ID","APPLE_PRIVATE_KEY_PATH","serviceUrls","AUTH_SERVICE_URL","NOTIFICATION_SERVICE_URL","PROGRAM_SERVICE_URL","RESOURCE_SERVICE_URL","IMAGE_SERVICE_URL","featureFlags","ENABLE_DISTRIBUTED_TRACING","boolean","ENABLE_RATE_LIMITING","ENABLE_REQUEST_LOGGING","postmark","POSTMARK_API_KEY","POSTMARK_FROM_EMAIL","email","POSTMARK_REPLY_TO_EMAIL","POSTMARK_TIMEOUT_MS","positive","pushNotification","APNS_KEY_ID","APNS_TEAM_ID","APNS_TOPIC","APNS_PRIVATE_KEY","APNS_PRODUCTION","transform","val","or","FCM_PROJECT_ID","FCM_CLIENT_EMAIL","FCM_PRIVATE_KEY"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "90dc-core",
3
- "version": "1.16.0",
3
+ "version": "1.16.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",