Node js apple push notification with sending custom data - node.js

Now push notification works properly, but I don't know how to send custom data with push notification.
My node js library for push notification is 'apn'.
Code for push notification :
var apns = require('apn');
/* Data to be send with push notification */
var ntfnDetails = {};
ntfnDetails = data.ntfnDetails;
var options = {
cert: 'FitCert.pem', /* Certificate file path */
certData: null, /* String or Buffer containing certificate data, if supplied uses this instead of cert file path */
key: 'FITHUDLEKEY.pem', /* Key file path */
keyData: null, /* String or Buffer containing key data, as certData */
passphrase: 'titech!##', /* A passphrase for the Key file */
ca: null, /* String or Buffer of CA data to use for the TLS connection */
gateway: 'gateway.push.apple.com',/* gateway address */
port: 2195, /* gateway port */
enhanced: true, /* enable enhanced format */
errorCallback: undefined, /* Callback when error occurs function(err,notification) */
cacheLength: 100 /* Number of notifications to cache for error purposes */
};
var apnsConnection = new apns.Connection(options);
var myDevice = data.device_id;
var note = new apns.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 3;
note.alert = 'Hellooo';
note.payload = {'messageFrom': 'Caroline'};
apnsConnection.pushNotification(note, myDevice);
In this I want to send 'ntfnDetails' variable with this push notification.
Please help me to find a solution...
Thanks in advance.

I think you can set it in payload
note.payload = {'messageFrom': 'Caroline', 'ntfnDetails' : ntfnDetails };
Check this apple-push-notification-with-sending-custom-data

Related

Azure function to send telemetry data to iotDevice

I am trying to develop a azure function that receives messages from one the built-in eventhub process it and send the result to another IoT Device configured in the Azure IoT Hub.
Below is the code:
module.exports = function (context, IoTHubMessages) {
var Mqtt = require('azure-iot-device-mqtt').Mqtt;
var DeviceClient = require('azure-iot-device').Client
var Message = require('azure-iot-device').Message
var connectionString = '{connectionstring of the target device}';
var acRoom1 = DeviceClient.fromConnectionString(connectionString, Mqtt);
var totalPerson = 0;
var events = IoTHubMessages.length;
context.log(JSON.stringify(IoTHubMessages));
context.log(Array.isArray(IoTHubMessages));
context.log(`Number of entries: ${IoTHubMessages.length}`);
IoTHubMessages.forEach(message => {
context.log(`Processed message: ${JSON.stringify(message)}`);
totalPerson = totalPerson + message.personCount;
context.log(`Total count: ${totalPerson}`);
});
var avgPersonCount = Math.round( totalPerson / events );
context.log(`Average person count: ${avgPersonCount}`);
var temp = 24;
if ( avgPersonCount > 5){
temp = 20;
}
else if ((avgPersonCount>2) && (avgPersonCount <= 5)){
temp = 22;
}
else {
temp = 24;
}
var msg = new Message(`Setting temperature to ${temp} C`);
context.log('Sending message: ' + msg.getData());
context.log(`Temeperature set to ${temp} C`);
acRoom1.sendEvent(msg);
context.done();
};
The issue I have is that the event that I am sending to device is coming back to this azure functions again. I believe, i need to do something in the Message Routing, but not sure what needs to be done.
The flow of the entire solution ( that I want to achieve ) is as below
Camera -- > Azure IOT Hub --> Azure Function --> AC
So please follow as below example shows on Message routing.
Routing on Message Body If you are routing on $body.property
You have to add the property in the body payload which is being sent by the device (device code is not shown here, only portal query is shown here).
and you can test it out by...
Routing on system property
The Iot Hub will assign this property on every message , so simply do setting on Portal side.(just give device name in the query, for quick you can test by using it on portal side)
App Property as said by Matthijs in his response, below snap shows on device C# sample code. And then you have to write the query which matches the app property.
Verify on Destination side In my example the destination is Blob container.
You could filter events by device ID, but a more scalable way would be to add an appProperty. If you want to send all the AC events to a different endpoint, you can add an appProperty to the message the AC is sending. Example:
var msg = new Message(`Setting temperature to ${temp} C`);
msg .properties.add('eventType', 'AC');
context.log('Sending message: ' + msg.getData());
context.log(`Temeperature set to ${temp} C`);
acRoom1.sendEvent(msg);
After that, you can go to your IoT Hub and add a new route. You can route these events to a different endpoint like so:
Because your camera doesn't send this appProperty, it will rely on the fallback route and your Azure Function will still handle those events. Another, perhaps more reliant option is to send only the camera messages to a specific route. But either will work!
I figured out the answer. Thanks to #Matthijs van der Veer for the hint.
1. Firstly disable the fall back rule. Now I have a 2 route
Instead of azure-iot-device package, i shifted to azure-iothub package.
module.exports = function (context, IoTHubMessages) {
var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;
var connectionString = '{connection-string-policy-service}';
var targetDevice = '{destination-deviceid}';
var serviceClient = Client.fromConnectionString(connectionString);
serviceClient.open(function (err) {
if (err) {
context.log('Could not connect: ' + err.message);
}
});
var totalPerson = 0;
var events = IoTHubMessages.length;
context.log(JSON.stringify(IoTHubMessages));
context.log(`Number of entries: ${IoTHubMessages.length}`);
IoTHubMessages.forEach(message => {
context.log(`Processed message: ${JSON.stringify(message)}`);
totalPerson = totalPerson + message.personCount;
context.log(`Total count: ${totalPerson}`);
});
var avgPersonCount = Math.round( totalPerson / events );
context.log(`Average person count: ${avgPersonCount}`);
var temp = 24;
if ( avgPersonCount > 5){
temp = 20;
}
else if ((avgPersonCount>2) && (avgPersonCount <= 5)){
temp = 22;
}
else {
temp = 24;
}
var msg = new Message(`Setting temperature to ${temp} C`);
msg .properties.add('eventType', 'AC');
context.log('Sending message: ' + msg.getData());
context.log(`Temeperature set to ${temp} C`);
serviceClient.send(targetDevice, msg);
context.done();
};

I am a beginner with node.js and API's - would the below code send a notification from user1 to user2?

I am a beginner with node.js and API's, I followed the getstream.io tutorials and came up with the below code
Would the below code send a notification from user1 to user2?
And how do I confirm that this successfully happens?
var stream = require('getstream');
// Instantiate a new client (server side)
client = stream.connect('key', 'secret', '25553');
// creates token so user1 can read and write
var token = client.feed('user', '1').token;
var user1 = client.feed('user', '1', token);
// Create a bit more complex activity
activity = {
'name': 'Jack',
'location': {'type': 'point', 'coordinates': [37.769722,-122.476944]},
'to' : ['notification:user2']
};
user1.addActivity(activity)
.then(function(data) { /* on success */ })
.catch(function(reason) { /* on failure */ });
// update the buyer status value for the activity
activity.name = 'James';
// send the update to the APIs
client.updateActivities([activity]);
//client side
//creates new client for reading
var client2 = stream.connect('key', null, '25553');
//creates read only token from sever client
var readonlyToken = client.feed('user', '1').getReadOnlyToken();
//user 2 gets read only token from user 1
var user2 = client2.feed('user', '1', readonlyToken);
//notification seen
user2.get({limit:5, mark_seen:true})
.then(function(data) { /* on success */ })
.catch(function(reason) { /* on failure */ });
If this code is all on the back end then you don't need to generate the tokens. You only need the tokens to pass to the front-end client.
For your client-side code you don't want this line:
var readonlyToken = client.feed('user', '1').getReadOnlyToken();
As mentioned in other posts, you generate that read-only token on the back end, pass it to the front end, and it's only good for websocket connections to Stream, of which you have a very limited supply (only 500 connections on our free plan) so generally we recommend that your back end pulls the feeds and sends the activity data to the front end.
Finally, the mark_seen and mark_read flags are used on notification feeds only, and your code is trying to use it on your user feed which is a flat feed.

Ensuring a unique socket.id over multiple app instances using socket.io

I am running a Node.js + Socket.io app over multiple instances with a Redis cache layer. I am wondering if the socket.id field for each connection would be unique over all instances. Is there a possibility that a socket.id on one instance can be the same as socket.id on another instance?
As far as I can see, the socket.id is generated using the following code:
/**
* Interface to a `Client` for a given `Namespace`.
*
* #param {Namespace} nsp
* #param {Client} client
* #api public
*/
function Socket(nsp, client, query){
this.nsp = nsp;
this.server = nsp.server;
this.adapter = this.nsp.adapter;
this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
this.client = client;
this.conn = client.conn;
this.rooms = {};
this.acks = {};
this.connected = true;
this.disconnected = false;
this.handshake = this.buildHandshake(query);
this.fns = [];
}
I am unsure about what it is actually doing when creating the id field.
If you trace the flow further, you will realize the socket.io module depends on engine.io (https://github.com/socketio/engine.io). It uses the id generated by engine.io.
1.engine.io emits connection to socket.io server as per the code below:
Server.prototype.onconnection = function(conn){
debug('incoming connection with id %s', conn.id);
var client = new Client(this, conn);
conn.id has the unique id.
2.socket.io client code stores this id in its pointer
function Client(server, conn){
this.server = server;
this.conn = conn;
this.encoder = new parser.Encoder();
this.decoder = new parser.Decoder();
this.id = conn.id;
3.Then when socket is created, same id is used
this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
Now back to the original question. The id is generated by engine.io using the following method:
/**
* generate a socket id.
* Overwrite this method to generate your custom socket id
*
* #param {Object} request object
* #api public
*/
Server.prototype.generateId = function (req) {
return base64id.generateId();
};
Here base64id and generateId are part of npm package https://www.npmjs.com/package/base64id. This package is supposed to generate a random base 64 id. I hope this helps you to understand that there is a very less probability of having the same id for 2 connections. You do have an option to override this method if you don't prefer this algorithm
The id will always be unique .this is due to fact that the id is based from the socket it was connected . Every socket is different.So every id is differet.
socket()
|
bind()
|
recvfrom()
|
(wait for a sendto request from some client)
|
(process the sendto request)
|
sendto (in reply to the request from the client...for example, send an HTML file)

NodeJS STARTTLS Use SNI

I'm building a simple, STARTTLS capable POP3 Proxy in Node.JS and I'm having quite a hard time.
The proxy serves as a front-end for many back-end servers, so it must load their certificates dynamically, depending on the Client's connection.
I'm trying to use the SNICallback, which brings me the servername the client uses, but I can't set the right certificate after this, because I need one certificate before I have this call, when I create the secure context.
The code is as bellow:
// Load libraries
var net = require('net');
var tls = require('tls');
var fs = require('fs');
// Load certificates (created with openssl)
var certs = [];
for (var i = 1; i <= 8; i++) {
var hostName = 'localhost' + i;
certs[hostName] = {
key : fs.readFileSync('./private-key.pem'),
cert : fs.readFileSync('./public-cert' + i + '.pem'),
}
}
var server = net.createServer(function(socket) {
socket.write('+OK localhost POP3 Proxy Ready\r\n');
socket.on('data', function(data) {
if (data == "STLS\r\n") {
socket.write("+OK begin TLS negotiation\r\n");
upgradeSocket(socket);
} else if (data == "QUIT\r\n") {
socket.write("+OK Logging out.\r\n");
socket.end();
} else {
socket.write("-ERR unknown command.\r\n");
}
});
}).listen(10110);
and upgradeSocket() is as follows:
function upgradeSocket(socket) {
// I need this 'details' or handshake will fail with a message:
// SSL routines:ssl3_get_client_hello:no shared cipher
var details = {
key : fs.readFileSync('./private-key.pem'),
cert : fs.readFileSync('./public-cert1.pem'),
}
var options = {
isServer : true,
server : server,
SNICallback : function(serverName) {
return tls.createSecureContext(certs[serverName]);
},
}
sslcontext = tls.createSecureContext(details);
pair = tls.createSecurePair(sslcontext, true, false, false, options);
pair.encrypted.pipe(socket);
socket.pipe(pair.encrypted);
pair.fd = socket.fd;
pair.on("secure", function() {
console.log("TLS connection secured");
});
}
It handshakes correctly but the certificate I use is the static one in 'details', not the one I get in the SNICallback.
To test it I'm running the server and using gnutls-cli as a Client:
~$ gnutls-cli -V -s -p 10110 --crlf --insecure -d 5 localhost3
STLS
^D (Control+D)
The above command is supposed to get me the 'localhost3' certificate but it's getting the 'localhost1' because it's defined in 'details' var;
There are just too many examples throughout the internet with HTTPS or for TLS Clients, which it's a lot different from what I have here, and even for Servers as well but they're not using SNI. Any help will be appreciated.
Thanks in advance.
The answer is quite simple using tls.TLSSocket, though there is a gotcha with the listeners.
You have to remove all the listeners from the regular net.Socket you have, instantiate a new tls.TLSSocket using your net.Socket and put the listeners back on the tls.TLSSocket.
To achieve this easily, use a wrapper like Haraka's tls_socket pluggableStream over the regular net.Socket and replace the "upgrade"
function to something like:
pluggableStream.prototype.upgrade = function(options) {
var self = this;
var socket = self;
var netSocket = self.targetsocket;
socket.clean();
var secureContext = tls.createSecureContext(options)
var tlsSocket = new tls.TLSSocket(netSocket, {
// ...
secureContext : secureContext,
SNICallback : options.SNICallback
// ...
});
self.attach(tlsSocket);
}
and your options object would have the SNICallback defined as:
var options {
// ...
SNICallback : function(serverName, callback){
callback(null, tls.createSecureContext(getCertificateFor(serverName));
// ...
}
}

Cannot get node-apn to connect

I'm trying to make a connection to APNs. It simply won't connect. I get variations of:
apn Socket error occurred +609ms { [Error: socket hang up] code: 'ECONNRESET' }
and
apn Connection error occurred before TLS Handshake +0ms
This is for a Passbook pass. Not an app. I'm using Passbook certificates.
My code is:
var apns = require('apn');
var root = process.cwd();
var fs = require('fs');
var options = {
cert: root + '/certs/new/cert.pem', /* Certificate file path */
certData: null, /* String or Buffer containing certificate data, if supplied uses this instead of cert file path */
key: root + '/certs/new/key.pem', /* Key file path */
keyData: null, /* String or Buffer containing key data, as certData */
passphrase: 'secret', /* A passphrase for the Key file */
ca: null, /* String or Buffer of CA data to use for the TLS connection */
gateway: 'gateway.sandbox.push.apple.com',/* gateway address */
port: 2195, /* gateway port */
enhanced: true, /* enable enhanced format */
errorCallback: undefined, /* Callback when error occurs function(err,notification) */
cacheLength: 100 /* Number of notifications to cache for error purposes */
};
var apnsConnection = new apns.Connection(options);
var myDevice = new apns.Device('token');
var note = new apns.Notification();
note.payload = {};
note.device = myDevice;
apnsConnection.sendNotification(note);
It appears that I mixed up my certificates. I'm sure I tried swapping them earlier but obviously didn't.
cert: Your app cert.
key: Apple's WWDR
are you behind a proxy? that could be the issue (at least it is often in my case)
Try the following structure : Read the .cert and .key files manually and set them as certData and keyData property, respectivelly. Here is the core :
var key = root + '/certs/new/key.pem'
var cert = root + '/certs/new/cert.pem';
var certData = fs.readFileSync(cert, encoding='ascii');
var keyData = fs.readFileSync(key, encoding='ascii');
var apnsConnection = new apns.Connection({
certData: certData,
keyData: keyData,
gateway: 'gateway.sandbox.push.apple.com',
port: 2195,
... /* other configs of course */
});

Resources