Phonegap PushNotification, how to use my node push-server with phonegap? - javascript

So, i'm on Phonegap, i use :
https://github.com/phonegap-build/PushPlugin/
https://github.com/argon/node-apn
So, i install the first plugin and i can get my phone token. After that, i created a node server.js file in my root directory with :
var apn = require('apn');
var token = "MY TOKEN";
var device = new apn.Device(token);
var notification = new apn.Notification();
notification.expiry = Math.floor(Date.now() / 1000) + 3600;
notification.badge = 1;
notification.alert = "This is a Push Notification=)";
notification.payload = {'prop': 'special value'};
notification.device = device;
var options = {
gateway: 'gateway.sandbox.push.apple.com',
cert: 'CER.pem',
key: 'KEY.pem',
passphrase: 'password'
}
var apnsConnection = new apn.Connection(options);
apnsConnection.pushNotification(notification, device);
When i start my server with node server.js in command line, i can see my push notification on my phone, so all it's ok.
But my question, i need to send push notification in different place in my code (phonegap). How can i do that ?
When my server.js is running, how can i send other push notification from my phonegap application ?

What you have above is code that you can collect together, and expose a function to call it multiple times. For example, a very simple implementation would be:
var apn = require('apn');
var options = {
gateway: 'gateway.sandbox.push.apple.com',
cert: 'CER.pem',
key: 'KEY.pem',
passphrase: 'password'
};
var apnsConnection = new apn.Connection(options);
module.exports.pushNotification = function(token, alert) {
var device = new apn.Device(token);
var notification = new apn.Notification();
notification.alert = alert;
notification.device = device;
apnsConnection.pushNotification(notification, device);
};
Imagine you name this file pns.js for "push notification service". Now in your server.js, you could instead require that module you just created and call the pushNotification function:
var pns = require("./pns.js");
pns.pushNotification("MY TOKEN", "This is a Push Notification");
Now you've got the same function when you execute the server.js. From here, you could instead pull this function into other modules that need to call it from the Node.js side of things.
If you need to call it from a remote process, you could look into a web framework like Express, and build an API which calls the same code. The token and alert message could then be passed in to this function call. Doing this would likely turn your server.js into a running web server which listens for requests and sends push notifications on demand.

A bit late, but for people with the same question, look at this tool:
https://www.npmjs.com/package/node-pushserver
...it does exactly what you want. It supports both iOS and Android.
Run this on a server and your app can: register the device by POSTing to http://yourserver:8000:/subscribe. Devices are stored in a mongodb database. By POSTing a http request to http://yourserver:8000/send, you can send push notifications to a single registered device, a subset or all of them.
Have fun!

Related

Unable to retrieve instance from Eureka server using JS client

I'm having a problem trying to get a service URL discover by eureka.
I'm using eureka-js-client to connect to Eureka and for testing purposes I've created two microservices, I've called it: ms1 and ms2.
What I've tried is:
Start Eureka server to allow services register into it
Start ms1 and register into Eureka
Start ms2, register into Eureka and get ms1 URL.
To accomplish this I've launched eureka server as a Spring Boot app using #EnableEurekaServer. This part works fine, I can access http://localhost:8761/ and see the dashboard.
Then, in my microservices I've this configuration
this._client = new Eureka({
instance: {
app: 'ms1',
instanceId: 'ms1',
hostName: 'localhost',
ipAddr: '127.0.0.1',
statusPageUrl: `http://localhost:${port ? port : this._port}`,
healthCheckUrl: `http://localhost:${port? port : this._port}/health`,
port: {
'$': port? port: this._port,
'#enabled': true,
},
vipAddress: 'myvip',
dataCenterInfo: {
'#class': 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo',
name: 'MyOwn',
},
},
eureka: {
host: 'localhost',
port: 8761,
servicePath: '/eureka/apps/'
},
})
And the same for ms2 changing the name.
When I run the project it output registered with eureka: ms1/ms1 and services seems to be registered in eureka correctly:
But now the problem is trying to get the URL of one of the two services. From either of the two services, if I try to get the Eureka instances I always get an empty list.
I have this code:
let instances: any = this.getClient().getInstancesByAppId(microserviceName);
let instance = null;
let url = ''
if (instances != null && instances.length > 0) {
instance = instances[0];
let protocol = instance.securePort["#enabled"] == "true" ? "https" : "http";
url = `${protocol}//${instance.ipAddr}:${instance.port.$}/`
}
Where in "microserviceName" variable I've tried:
"ms1"
"MS1"
"ms1/ms1"
But the response is always an empty array with this output:
Unable to retrieve instances for appId: ms1
So, what's the problem? Have I missed something? I think the flow is correct:
Start Eureka server.
Register services into server.
Look for instances in the server.
Thanks in advance.
Finally I solved my own issue. All was working good, the ms2 was able to find ms1 using the code I posted, so the problem was:
My ms2 file was like this:
EurekaService.getClient().start()
EurekaService.getUrl('ms1')
EurekaService.getClient()?.stop()
And it seems like EurekaService.getClient().start() does not block until it ends (or is available or whatever), so the client is not up and can't get the instance ms1.
Note that the method getUrl() has the code provided in the OP:
let instances: any = this.getClient().getInstancesByAppId(microserviceName);
let instance = null;
...
So I've changed the code like this:
start()
async function start(){
EurekaService.getClient().start()
await new Promise(f => setTimeout(f, 1000));
const url = EurekaService.getUrl('ms1')
console.log("url = ",url)
EurekaService.getClient()?.stop()
}
And works perfectly, the output log is:
registered with eureka: ms2/ms2
url = http//127.0.0.1:8002/
de-registered with eureka: ms2/ms2
So, start method is not async so I can't use await or .then(), I have to set a timeout and wait to complete.
I don't know if there is a better way to do this or by the nature of the architecture can't be controlled when is available.
By the way, for me, 1 second timeout is enough.

Node.js code isn't running in javascript function

I'm working with Twilio and sending an example message when the provided code is ran in the terminal:
var accountSid = '...';
var authToken = '...';
var twilio = require('twilio');
var client = new twilio(accountSid, authToken);
client.messages.create({
body: 'Hello World!',
to: '+1-555-555-5555', // Text this number
from: '+18885555555' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
However, when I place it in a function to be called on when a button is pressed, then nothing happens.
function messageNow() {
var accountSid = '...';
var authToken = '...';
var twilio = require('twilio');
var client = new twilio(accountSid, authToken);
client.messages.create({
body: 'Hello World!',
to: '+1-555-555-5555', // Text this number
from: '+18885555555' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
}
And call from HTML button:
<button type="submit" id="Btn" class="buttons" onclick="messageNow()">SUBMIT</button>
Any help is appreciated.
Twilio developer evangelist here.
It sounds as though you are trying to run the Twilio Node.js module in the browser. This won't work as the module is only built for server side Node.js, not client side/browser JavaScript.
If you want to send messages via an interface with buttons, you will need to build a web application that can run the Twilio code on the server and present an interface in HTML, CSS and JavaScript that can call the server application.
There are a number of tutorials that take you through different ways you can build apps to send SMS messages with Twilio and Node.js. It might help to take a look there.

How can I send data to a specific socket?

My problem is that the current solution I have for sending a specific socket using the library "ws" with node.js is not good enough.
The reason is because if I connect with multiple tabs to the websocket server with the same userid which is defined on the client-side, it will only refer to the latest connection with the userid specified.
This is my code:
// Server libraries and configuration
var server = require("ws").Server;
var s = new server({ port: 5001});
// An array which I keep all websockets clients
var search = {};
s.on("connection", function(ws, req) {
ws.on("message", function(message){
// Here the server process the user information given from the client
message = JSON.parse(message);
if(message.type == "userinfo"){
ws.personName = message.data;
ws.id = message.id;
// Defining variable pointing to the unique socket
search[ws.id] = ws;
return;
}
})
})
As you can see, each time a socket with same id connects, it will refer to the latest one.
Example If you did not understand:
Client connect to server with ID: 1337
search[1337] defined as --> websocket 1
A new connection with same ID: 1337
search[1337] becomes instead a variable refering to websocket 2 instead
Websockets provide a means to create a low-latency network "socket" between a browser and a server.
Note that the client here is the browser, not a tab on a browser.
If you need to manage multiple user sessions between the browser and server, you'll need to write code to do it yourself.

Accessing Google Drive from a Firefox extension

I'm trying to access (CRUD) Google Drive from a Firefox extension. Extensions are coded in Javascript, but neither of the two existing javascript SDKs seem to fit; the client-side SDK expects "window" to be available, which isn't the case in extensions, and the server-side SDK seems to rely on Node-specific facilities, as a script that works in node no longer does when I load it in chrome after running it through browserify. Am I stuck using raw REST calls? The Node script that works looks like this:
var google = require('googleapis');
var readlineSync = require('readline-sync');
var CLIENT_ID = '....',
CLIENT_SECRET = '....',
REDIRECT_URL = 'urn:ietf:wg:oauth:2.0:oob',
SCOPE = 'https://www.googleapis.com/auth/drive.file';
var oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
scope: SCOPE // If you only need one scope you can pass it as string
});
var code = readlineSync.question('Auth code? :');
oauth2Client.getToken(code, function(err, tokens) {
console.log('authenticated?');
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(!err) {
console.log('authenticated');
oauth2Client.setCredentials(tokens);
} else {
console.log('not authenticated');
}
});
I wrap the node GDrive SDK using browserify on this script:
var Google = new function(){
this.api = require('googleapis');
this.clientID = '....';
this.clientSecret = '....';
this.redirectURL = 'urn:ietf:wg:oauth:2.0:oob';
this.scope = 'https://www.googleapis.com/auth/drive.file';
this.client = new this.api.auth.OAuth2(this.clientID, this.clientSecret, this.redirectURL);
}
}
which is then called using after clicking a button (if the text field has no code it launches the browser to get one):
function authorize() {
var code = document.getElementById("code").value.trim();
if (code === '') {
var url = Google.client.generateAuthUrl({access_type: 'offline', scope: Google.scope});
var win = Components.classes['#mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('navigator:browser');
win.gBrowser.selectedTab = win.gBrowser.addTab(url);
} else {
Google.client.getToken(code, function(err, tokens) {
if(!err) {
Google.client.setCredentials(tokens);
// store token
alert('Succesfully authorized');
} else {
alert('Not authorized: ' + err); // always ends here
}
});
}
}
But this yields the error Not authorized: Invalid protocol: https:
It is possible though, depending on the use case, it might also of limited interest.
Firefox ships with a tiny http server, just the bare bones. It is included for test purposes but this is not a reason to overlook it.
Lets follow the quickstart guide for running a Drive app in Javascript
The tricky part is to set the Redirect URIs and the Javascript Origins. Obviously the right setting is http://localhost, but how can you be sure that every user has port 80 available?
You can't and, unless you have control over your users, no port is guaranteed to work for everyone. With this in mind lets choose port 49870 and pray.
So now Redirect URIs and the Javascript Origins are set to http://localhost:49870
Assuming you use Add-on SDK, save the quickstart.html (remember to add your Client ID) in the data directory of your extension. Now edit your main.js
const self = require("sdk/self");
const { Cc, Ci } = require("chrome");
const tabs = require("sdk/tabs");
const httpd = require("sdk/test/httpd");
var quickstart = self.data.load("quickstart.html");
var srv = new httpd.nsHttpServer();
srv.registerPathHandler("/gdrive", function handler(request, response){
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
let converter = Cc["#mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
response.write(converter.ConvertFromUnicode(quickstart));
})
srv.start(49870);
tabs.open("http://localhost:49870/gdrive");
exports.onUnload = function (reason) {
srv.stop(function(){});
};
Notice that quickstart.html is not opened as a local file, with a resource: URI. The Drive API wouldn't like that. It is served at the url http://localhost:49870/gdrive. Needless to say that instead of static html we can use a template or anything else. Also the http://localhost:49870/gdrive can be scripted with a regular PageMod.
I don't consider this a real solution. It's just better than nothing.
From here https://developer.mozilla.org/en/docs/Working_with_windows_in_chrome_code you could try window = window || content || {}
Use the JavaScript client API and not the node.js client. Although browserify will make it work. You will have to expose your client secret in the latter. The flow of client side authentication is very diff than server side. Refer to https://developers.google.com/accounts/docs/OAuth2
Having said all this. Its really not that difficult to implement an app with REST based calls. The methods in all client libraries mimic the corresponding REST URLs. You could set up some functions of your own to handle request and response and the rest would feel the same.

Sending message to a specific connected users using webSocket?

I wrote a code for broadcasting a message to all users:
// websocket and http servers
var webSocketServer = require('websocket').server;
...
...
var clients = [ ];
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
...
});
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server.
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
...
var connection = request.accept(null, request.origin);
var index = clients.push(connection) - 1;
...
Please notice:
I don't have any user reference but only a connection .
All users connection are stored in an array.
Goal: Let's say that the Node.js server wants to send a message to a specific client (John). How would the NodeJs server know which connection John has? The Node.js server doesn't even know John. all it sees is the connections.
So, I believe that now, I shouldn't store users only by their connection, instead, I need to store an object, which will contain the userId and the connection object.
Idea:
When the page finishes loading (DOM ready) - establish a connection to the Node.js server.
When the Node.js server accept a connection - generate a unique string and send it to the client browser. Store the user connection and the unique string in an object. e.g. {UserID:"6", value: {connectionObject}}
At client side, when this message arrives - store it in a hidden field or cookie. (for future requests to the NodeJs server )
When the server wants to send a message to John:
Find john's UserID in the dictionary and send a message by the corresponding connection.
please notice there is no asp.net server code invloced here (in the message mechanism). only NodeJs .*
Question:
Is this the right way to go?
This is not only the right way to go, but the only way. Basically each connection needs a unique ID. Otherwise you won't be able to identify them, it's as simple as that.
Now how you will represent it it's a different thing. Making an object with id and connection properties is a good way to do that ( I would definitely go for it ). You could also attach the id directly to connection object.
Also remember that if you want communication between users, then you have to send target user's ID as well, i.e. when user A wants to send a message to user B, then obviously A has to know the ID of B.
Here's a simple chat server private/direct messaging.
package.json
{
"name": "chat-server",
"version": "0.0.1",
"description": "WebSocket chat server",
"dependencies": {
"ws": "0.4.x"
}
}
server.js
var webSocketServer = new (require('ws')).Server({port: (process.env.PORT || 5000)}),
webSockets = {} // userID: webSocket
// CONNECT /:userID
// wscat -c ws://localhost:5000/1
webSocketServer.on('connection', function (webSocket) {
var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
webSockets[userID] = webSocket
console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(webSockets))
// Forward Message
//
// Receive Example
// [toUserID, text] [2, "Hello, World!"]
//
// Send Example
// [fromUserID, text] [1, "Hello, World!"]
webSocket.on('message', function(message) {
console.log('received from ' + userID + ': ' + message)
var messageArray = JSON.parse(message)
var toUserWebSocket = webSockets[messageArray[0]]
if (toUserWebSocket) {
console.log('sent to ' + messageArray[0] + ': ' + JSON.stringify(messageArray))
messageArray[0] = userID
toUserWebSocket.send(JSON.stringify(messageArray))
}
})
webSocket.on('close', function () {
delete webSockets[userID]
console.log('deleted: ' + userID)
})
})
Instructions
To test it out, run npm install to install ws. Then, to start the chat server, run node server.js (or npm start) in one Terminal tab. Then, in another Terminal tab, run wscat -c ws://localhost:5000/1, where 1 is the connecting user's user ID. Then, in a third Terminal tab, run wscat -c ws://localhost:5000/2, and then, to send a message from user 2 to 1, enter ["1", "Hello, World!"].
Shortcomings
This chat server is very simple.
Persistence
It doesn't store messages to a database, such as PostgreSQL. So, the user you're sending a message to must be connected to the server to receive it. Otherwise, the message is lost.
Security
It is insecure.
If I know the server's URL and Alice's user ID, then I can impersonate Alice, ie, connect to the server as her, allowing me to receive her new incoming messages and send messages from her to any user whose user ID I also know. To make it more secure, modify the server to accept your access token (instead of your user ID) when connecting. Then, the server can get your user ID from your access token and authenticate you.
I'm not sure if it supports a WebSocket Secure (wss://) connection since I've only tested it on localhost, and I'm not sure how to connect securely from localhost.
For people using ws version 3 or above. If you want to use the answer provided by #ma11hew28, simply change this block as following.
webSocketServer.on('connection', function (webSocket) {
var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
webSocketServer.on('connection', function (webSocket, req) {
var userID = parseInt(req.url.substr(1), 10)
ws package has moved upgradeReq to request object and you can check the following link for further detail.
Reference: https://github.com/websockets/ws/issues/1114
I would like to share what I have done. Hope it doesn't waste your time.
I created database table holding field ID, IP, username, logintime and logouttime. When a user logs in logintime will be currect unixtimestamp unix. And when connection is started in websocket database checks for largest logintime. It will be come user logged in.
And for when user logs out it will store currect logouttime. The user will become who left the app.
Whenever there is new message, Websocket ID and IP are compared and related username will be displayed. Following are sample code...
// when a client connects
function wsOnOpen($clientID) {
global $Server;
$ip = long2ip( $Server->wsClients[$clientID][6] );
require_once('config.php');
require_once CLASSES . 'class.db.php';
require_once CLASSES . 'class.log.php';
$db = new database();
$loga = new log($db);
//Getting the last login person time and username
$conditions = "WHERE which = 'login' ORDER BY id DESC LIMIT 0, 1";
$logs = $loga->get_logs($conditions);
foreach($logs as $rows) {
$destination = $rows["user"];
$idh = md5("$rows[user]".md5($rows["time"]));
if ( $clientID > $rows["what"]) {
$conditions = "ip = '$ip', clientID = '$clientID'
WHERE logintime = '$rows[time]'";
$loga->update_log($conditions);
}
}
...//rest of the things
}
interesting post (similar to what I am doing).
We are making an API (in C#) to connect dispensers with WebSockets, for each dispenser we create a ConcurrentDictionary that stores the WebSocket and the DispenserId making it easy for each Dispenser to create a WebSocket and use it afterwards without thread problems (invoking specific functions on the WebSocket like GetSettings or RequestTicket).
The difference for you example is the use of ConcurrentDictionary instead of an array to isolate each element (never attempted to do such in javascript).
Best regards,

Categories

Resources