Viewing File: /home/ubuntu/efiexchange-node-base/node_modules/fcm-node/lib/fcm.js
var https = require('https');
var HttpsProxyAgent = require('https-proxy-agent');
var retry = require('retry');
var firebaseadmin = require("firebase-admin");
const TopicRequest = require('../lib/topic_request');
const TopicOptions = require('../lib/topic_options');
const TopicData = require('../lib/topic_data');
function FCM(accountKey, proxy_url=null, name=null) {
var admin = null
if(!proxy_url) {
proxy_url = process.env.http_proxy || null;
}
if(!accountKey) {
throw Error('You must provide the APIKEY for your firebase application.');
}
else if(typeof accountKey == 'string') { //API KEY PASSED string, legacy use
this.serverKey = accountKey;
this.fcmOptions = {
host: 'fcm.googleapis.com',
port: 443,
path: '/fcm/send',
method: 'POST',
headers: {}
};
this.send = function (payload, CB) {
var self = this;
if (!CB) {
throw Error('you must provide a callback function(err,result)'); //just in case
}
else {
var operation = retry.operation();
var mpayload = JSON.stringify(payload);
var mFcmOptions = Object.assign({}, self.fcmOptions); //copying the fcmOptions object to avoid problems in parallel calls
if(proxy_url) {
// HTTP/HTTPS proxy to connect to
var proxy = proxy_url;
var agent = new HttpsProxyAgent(proxy);
mFcmOptions.agent = agent;
}
operation.attempt(function (currentAttempt) {
var headers = {
'Host': mFcmOptions.host,
'Authorization': 'key=' + self.serverKey,
'Content-Type': 'application/json'
//'Content-Length': mpayload.length //removed this line for chunk-encoded transfer compatibility (UTF-8 and all non-ANSI codification)
};
mFcmOptions.headers = headers;
if (self.keepAlive) headers.Connection = 'keep-alive';
var request = https.request(mFcmOptions, function (res) {
var data = '';
if (res.statusCode == 503) {
// If the server is temporary unavailable, the FCM spec requires that we implement exponential backoff
// and respect any Retry-After header
if (res.headers['retry-after']) {
var retrySeconds = res.headers['retry-after'] * 1; // force number
if (isNaN(retrySeconds)) {
// The Retry-After header is a HTTP-date, try to parse it
retrySeconds = new Date(res.headers['retry-after']).getTime() - new Date().getTime();
}
if (!isNaN(retrySeconds) && retrySeconds > 0) {
operation._timeouts['minTimeout'] = retrySeconds;
}
}
if (!operation.retry('TemporaryUnavailable')) {
CB(operation.mainError(), null);
}
// Ignore all subsequent events for this request
return;
}
function respond() {
var error = null, id = null;
//Handle the various responses
if (data.indexOf('\"multicast_id\":') > -1)//multicast_id success
{
var anyFail = ((JSON.parse(data)).failure > 0);
if (anyFail) {
error = data.substring(0).trim();
}
var anySuccess = ((JSON.parse(data)).success > 0);
if (anySuccess) {
id = data.substring(0).trim();
}
} else if (data.indexOf('\"message_id\":') > -1) { //topic messages success
id = data;
} else if (data.indexOf('\"error\":') > -1) { //topic messages error
error = data;
} else if (data.indexOf('TopicsMessageRateExceeded') > -1) {
error = 'TopicsMessageRateExceededError'
} else if (data.indexOf('Unauthorized') > -1) {
error = 'NotAuthorizedError'
} else {
error = 'InvalidServerResponse';
}
// Only retry if error is QuotaExceeded or DeviceQuotaExceeded
if (operation.retry(currentAttempt <= 3 && ['QuotaExceeded', 'DeviceQuotaExceeded', 'InvalidServerResponse'].indexOf(error) >= 0 ? error : null)) {
return;
}
// Success, return message id (without id=)
CB(error, id);
}
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', respond);
});
request.on('error', function (error) {
CB(error, null);
});
request.end(mpayload);
});
}
}
// Subscribe devices to topic
// If topic does not exist, a new one is created
this.subscribeToTopic = (deviceTokens, topicName, CB) => {
const options = TopicOptions('iid.googleapis.com', '/iid/v1:batchAdd', 'POST', this.serverKey.slice(0));
const subscriptionData = TopicData(topicName, deviceTokens);
TopicRequest(options, subscriptionData, (err, res) => {
CB(err, res);
});
}
// Unsubscribe device to topic
this.unsubscribeToTopic = (deviceTokens, topicName, CB) => {
const options = TopicOptions('iid.googleapis.com', '/iid/v1:batchRemove', 'POST', this.serverKey.slice(0));
const unsubscriptionData = TopicData(topicName, deviceTokens);
TopicRequest(options, unsubscriptionData, (err, res) => {
CB(err, res);
});
}
}
else{ //accountkey object passed, new SDK 'de-promisefy' use
const config = 'private_key' in accountKey || 'privateKey' in accountKey ?
{credential: firebaseadmin.credential.cert(accountKey)} :
accountKey;
if(name) {
admin = firebaseadmin.initializeApp(config, name);
} else {
admin = firebaseadmin.initializeApp(config);
}
this.send = function(payload, _callback){
if (!_callback) {
throw Error('You must provide a callback function(err,result)')
}
else{
if(!payload) _callback(new Error('You must provide a payload object'))
else{
if(payload.to) {
if (typeof payload.to == 'string') {
var to = payload.to
delete payload.to
if (to.startsWith('/topics/')) {
var topic = to.slice(8)//anything after '/topics/'
admin.messaging().sendToTopic(topic, payload)
.then(function(response){_callback(null, response)})
.catch(function (err) {_callback(err)})
}
else{
admin.messaging().sendToDevice(to,payload)
.then(function (response) {_callback(null,response)})
.catch(function (error) {_callback(error)})
}
}
else{
var err = new Error('Invalid "to" field in payload');
_callback(err)
}
}
else if(payload.registration_ids){
var regIds = payload.registration_ids;
delete payload.registration_ids;
if(regIds instanceof Array && typeof regIds[0] == 'string')
{
admin.messaging().sendToDevice(regIds, payload)
.then(function (response) {_callback(null,response)})
.catch(function (error) {_callback(error)})
}
else{
var err = new Error('Invalid "registration_ids" field in payload');
_callback(err)
}
}
else{
var err = new Error('Invalid payload object');
_callback(err)
}
}
}
}
}
}
module.exports = FCM;
Back to Directory
File Manager