UNPKG

notification-services

Version:

Use email, sms and custom notification services for node.js application easily

227 lines (226 loc) 7.31 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FCMv1 = exports.fcmSendMulticast = exports.fcmSendToCondition = exports.fcmSendToTopic = exports.fcmSendToToken = exports.fcmSend = exports.fcmConfig = void 0; const axios_1 = __importDefault(require("axios")); const google_auth_library_1 = require("google-auth-library"); // Cache for auth tokens const authTokenCache = {}; class FCMv1 { constructor(config) { this.googleAuth = null; this.projectId = config.projectId; this.serviceAccountKey = config.serviceAccountKey; } /** * Initialize Google Auth client */ async initializeAuth() { if (this.googleAuth) { return this.googleAuth; } let keyFile; let credentials; if (typeof this.serviceAccountKey === 'string') { // Path to service account key file keyFile = this.serviceAccountKey; } else { // Service account key object credentials = this.serviceAccountKey; } this.googleAuth = new google_auth_library_1.GoogleAuth({ keyFile, credentials, scopes: ['https://www.googleapis.com/auth/firebase.messaging'], }); return this.googleAuth; } /** * Get OAuth2 access token with caching */ async getAccessToken() { const cacheKey = `fcm_${this.projectId}`; const cached = authTokenCache[cacheKey]; if (cached && Date.now() < cached.expiresAt) { return cached.token; } try { const auth = await this.initializeAuth(); const client = await auth.getClient(); const tokenResponse = await client.getAccessToken(); if (!tokenResponse.token) { throw new Error('Failed to obtain access token'); } // Cache token for 50 minutes (tokens are valid for 1 hour) authTokenCache[cacheKey] = { token: tokenResponse.token, expiresAt: Date.now() + (50 * 60 * 1000), }; return tokenResponse.token; } catch (error) { throw new Error(`Failed to get access token: ${error.message}`); } } /** * Send FCM message */ async send(options) { const { message, validateOnly = false } = options; // Validate message if (!message.token && !message.topic && !message.condition) { throw new Error('Message must have either token, topic, or condition'); } try { const accessToken = await this.getAccessToken(); const url = `https://fcm.googleapis.com/v1/projects/${this.projectId}/messages:send`; const payload = { message, validate_only: validateOnly, }; const headers = { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }; const response = await axios_1.default.post(url, payload, { headers }); return response.data; } catch (error) { if (error.response && error.response.data) { return error.response.data; } throw new Error(`FCM send failed: ${error.message}`); } } /** * Send message to single token */ async sendToToken(token, messageOptions) { const message = { ...messageOptions, token, }; return this.send({ message }); } /** * Send message to topic */ async sendToTopic(topic, messageOptions) { const message = { ...messageOptions, topic, }; return this.send({ message }); } /** * Send message to condition */ async sendToCondition(condition, messageOptions) { const message = { ...messageOptions, condition, }; return this.send({ message }); } /** * Send to multiple tokens (sends individual messages) */ async sendMulticast(tokens, messageOptions) { const promises = tokens.map(token => this.sendToToken(token, messageOptions)); const responses = await Promise.allSettled(promises); const results = []; let successCount = 0; let failureCount = 0; responses.forEach((result) => { if (result.status === 'fulfilled') { results.push(result.value); if (!result.value.error) { successCount++; } else { failureCount++; } } else { results.push({ error: { code: 500, message: result.reason.message || 'Unknown error', status: 'INTERNAL', }, }); failureCount++; } }); return { responses: results, successCount, failureCount, }; } } exports.FCMv1 = FCMv1; // Convenience functions for backward compatibility and ease of use let fcmInstance = null; /** * Configure FCM v1 client */ const fcmConfig = (config) => { fcmInstance = new FCMv1(config); return fcmInstance; }; exports.fcmConfig = fcmConfig; /** * Send FCM message using the configured instance */ const fcmSend = async (options) => { if (!fcmInstance) { throw new Error('FCM not configured. Please call fcmConfig() first.'); } return fcmInstance.send(options); }; exports.fcmSend = fcmSend; /** * Send to single token using the configured instance */ const fcmSendToToken = async (token, messageOptions) => { if (!fcmInstance) { throw new Error('FCM not configured. Please call fcmConfig() first.'); } return fcmInstance.sendToToken(token, messageOptions); }; exports.fcmSendToToken = fcmSendToToken; /** * Send to topic using the configured instance */ const fcmSendToTopic = async (topic, messageOptions) => { if (!fcmInstance) { throw new Error('FCM not configured. Please call fcmConfig() first.'); } return fcmInstance.sendToTopic(topic, messageOptions); }; exports.fcmSendToTopic = fcmSendToTopic; /** * Send to condition using the configured instance */ const fcmSendToCondition = async (condition, messageOptions) => { if (!fcmInstance) { throw new Error('FCM not configured. Please call fcmConfig() first.'); } return fcmInstance.sendToCondition(condition, messageOptions); }; exports.fcmSendToCondition = fcmSendToCondition; /** * Send to multiple tokens using the configured instance */ const fcmSendMulticast = async (tokens, messageOptions) => { if (!fcmInstance) { throw new Error('FCM not configured. Please call fcmConfig() first.'); } return fcmInstance.sendMulticast(tokens, messageOptions); }; exports.fcmSendMulticast = fcmSendMulticast; exports.default = FCMv1;