using socket io to get updates - node.js

i'm using in socket.io to get live updates from database.
here is my code:
var q = "SELECT * FROM notifications";
db.query(q, function(err, rows, fields) {
if(rows[0]) {
io.sockets.emit('newNotifications', rows[0]);
}
});
its work, but i want to add WHERE userid = "+myuserid+"
the question is how to do this?
i tried to do this with this code:
socket.on('connect', function () {
socket.send({"myuserid":"1"});
console.log('connected');
});
thanks.

You basically need to tie a userid to a particular a particular socket. How you do this is dependent on how you're handling authentication.
Once you have a userid attached to the socket, you have a couple options for getting them individual notifications. The most straightforward is to do as you wanted above. Create a query with a condition limiting the results. You need to do this on a per-socket basis though, so you'll need something like the following.
data.watchForNotification = function(userId, fn) {
// Please sanitize this and make sure you're not vulnerable to sql injection attacks
var q = "SELECT ... WHERE UserId = " + userId;
data.watchedUsers[userId] = setInterval(function() {
db.query(q, function(err, rows, fields) {
if(rows.[0]) {
fn(rows[0]);
}
});
}, 10000);
}
data.stopWatching(userId) {
if(data.watchedUsers[userId]) {
clearInterval(data.watchedUsers[userId]);
delete data.watchedUsers[userId];
}
}
io.on('connection', function(socket) {
// authenticate and set socket.userId
data.watchForNotifications(socket.userId, function(notification) {
socket.emit('notification', notification);
});
socket.on('disconnect', function() {
data.stopWatching(socket.userId);
});
});
This will setup an interval that checks every 10 seconds for new notifications and will send those to the connected socket when they come in. It will also stop the database queries for that user if they disconnect. There is a lot of pseudo code and abstraction work going on here, but it should be enough to get you where you want to go.

Related

How to auto-save to MongoDB with Mongoose without lag or freezing?

I'm writing a text editing app with Node.js & express and want to achieve Google Docs-esque auto-saving whenever the user edits their text.
Currently I'm doing this by saving to the database with AJAX whenever the user presses a key within the textarea. As soon as I start typing at any decent speed the saving process freezes up and doesn't save most of the content.
This however works perfectly when typing slowly.
I'm currently using mLab, MongoDB hosting, could this be the problem?
In fact, what is the best way to handle this task?
edit.ejs (front-end js):
$(document).ready(function() {
$('#board-lyrics').keyup(updateLyrics);
$('#board-title').keyup(updateLyrics);
function updateLyrics() {
let boardData = {
title: $('#board-title').val(),
content: $('#board-lyrics').val()
}
$.ajax({
type: "POST",
url: `./<%= id %>`,
data: boardData,
success: function(data) {
},
error: function() {
console.log('error');
}
});
}
});
app.js
app.post('/edit/:id', urlencodedParser, (req, res) => {
let user = req.user;
let boardId = req.params.id;
let query = {"_id": req.user.id};
let update = {"$set": {}};
let index;
for (let i = 0; i < user.boards.length; i++) {
if (user.boards[i]._id == boardId) {
index = i;
}
}
update.$set[`boards.${index}.title`] = req.body.title;
update.$set[`boards.${index}.content`] = req.body.content;
let options = { new: true };
User.findOneAndUpdate(query, update, options, function(err, doc){
console.log(query);
console.log(update);
console.log(doc);
});
});
Okay, two things here.
firstly, it isn't a good idea to update your DB on every keystroke. Think about it. You are making a post request to your server more than once in a second with a payload and touching DB. Not ideal. So either you keep this data cached and save it once it crosses a threshold (say after one paragraph or X number of characters) or,
secondly, you can do some tweaks at the front-end side also here. Make sure you catch only valid or wanted keystrokes. Use reactive programming. Check for Rxjs and filter out invalid characters or catch only at a certain interval. Hope this will help.

double sockets in room

When I press F5 repeatedly without stopping the same socket.username enters the room, generating a list of duplicate names. By spending time, this sockets duplicated vain automatically disconnecting .
I tried with these lines after disconnecting and not work!
delete io.sockets.adapter.sids[socket.id];
delete io.sockets.adapter.rooms[socket.id];
How can I avoid this? Tks.
// SERVER.JS
io.set('authorization', function (handshakeData, accept) {
if (handshakeData.headers.cookie) {
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie['sec_session_id'], 'secret');
if (handshakeData.cookie['sec_session_id'] != handshakeData.sessionID) return accept('Cookie is invalid.', false);
} else return accept('No cookie transmitted.', false);
accept(null, true);
});
io.sockets.on('connection', function(socket) {
socket.on('adduser', function(name, room, picuser, codeuser, operation) {
var handshakeData = socket.request;
var cookies = cookie.parse(handshakeData.headers.cookie);
if(name.length > 0)
{
if (!io.sockets.adapter.sids[socket.id][room]) {
var newCookie = changeCookie(cookies.sec_session_id);
cookiesSockets[cookies.sec_session_id] = newCookie;
cookiesSockets2[newCookie] = socket.id;
socket.color = getRandomColor();
socket.username = name;
socket.room = room;
socket.newid = newCookie;
socket.picuser = picuser;
socket.codeuser = codeuser;
// add the client's username to the global list
usernames[name] = name;
// send client to room
socket.join(room);
// echo to client they've connected
socket.emit('updatechat', socket.picuser, socket.codeuser, socket.color, getClock(), 'You', 'join in '+room+'.', room, 0, 0, getClock2(), operation);
// echo to room 1 that a person has connected to their room
socket.broadcast.to(room).emit('updatechat', socket.picuser, socket.codeuser, socket.color, getClock(), name,'join in room.', room, 0, 0, getClock2(), 3);
//update id's private chats on socket disconnecting and connecting...
if (SocketsUsernames[name])
{
if (SocketsUsernames[name]!=cookies.sec_session_id)
{
var cookieReal = SocketsUsernames[name];
var cookieChanged = cookiesSockets[cookieReal];
delete cookiesSockets[cookieReal];
delete cookiesSockets2[cookieChanged];
socket.broadcast.to(room).emit('updatechatpvt', room, cookieChanged, newCookie);
}
}
SocketsUsernames[name] = cookies.sec_session_id;
updateUsers(room);
}
else
socket.emit('updatechat',null, null, null, null, null, null, room, null, null, null, 7);
}
});
socket.on('disconnect', function(){
if (socket.username)
{
// remove the username from global usernames list
delete usernames[socket.username];
socket.leave('myroom');
}
});
});
//CLIENT.JS
socket.on('connect', function (){
// Retrieve the object from storage CHATROOM!!
var retrievedRooms = localStorage.getItem('myRooms');
console.log('retrievedRooms: ', JSON.parse(retrievedRooms));
if (retrievedRooms && Object.keys(JSON.parse(retrievedRooms)).length>0)
Object.keys(JSON.parse(retrievedRooms)).forEach(function(key) {
if (key)
{
myRooms[key]=key;
socket.emit('checkuser', key, function(callback){
if(!callback) socket.emit('adduser', $('#modalPerfil').attr('data-username'), key, $('#modalPerfil').attr('data-picuser'), $('#modalPerfil').attr('data-userid'), 1);
});
}
});
});
// SOLUTION IN CLIENT.JS - setTimeout
socket.on('connect', function (){
// Retrieve the object from storage CHATROOM!!
var retrievedRooms = localStorage.getItem('myRooms');
console.log('retrievedRooms: ', JSON.parse(retrievedRooms));
if (retrievedRooms && Object.keys(JSON.parse(retrievedRooms)).length>0)
Object.keys(JSON.parse(retrievedRooms)).forEach(function(key) {
if (key)
{
myRooms[key]=key;
setTimeout(function(){
socket.emit('checkuser', key, function(callback){
if(!callback) socket.emit('adduser', $('#modalPerfil').attr('data-username'), key, $('#modalPerfil').attr('data-picuser'), $('#modalPerfil').attr('data-userid'), 1);
});
}, 1000);
}
});
});
This code is causing you a couple problems:
socket.onclose = function (reason) {
roomsToLeaveClient = socket.rooms;
Object.getPrototypeOf(this).onclose.call(this, reason);
});
First off, There's an extra ) at the end that is closing off your .on('connect') block.
That then causes your socket.on('disconnect', ...) code to be outside the scope of socket so it probably isn't getting called appropriately. I'd suggest you don't need to listen for a close event at all. You can just listen for the disconnect event and do any of your cleanup business there.
And, since you're off one set of parens in that code, there's probably a corresponding error somewhere else in your code that still allows things to parse properly. Putting your code in a linter like http://jshint.com shows these types of errors.
Then, if you really did want to override .onclose, you can't just call the prototype version. Instead, you have to save the version that was in place before you assign your override and call that one. That's the only way the is compatible with any other overrides. But, you really ought to just be using .on() with event names to watch socket lifecycle things, then you don't have to worry about proper overrides in any way.
I can't tell right away if there are other issues, but this is a glaring one that needs to be fixed first.
Also, you do not need your roomsToLeaveClient handling. socket.io will automatically remove a disconnecting client from any rooms that it is in. That is not something you need to do.
If you want to know what rooms a socket is in so you can tell the other members of that room that you are leaving, then socket.io has a data structure that tells you exactly what rooms the socket is in and you can just iterate that data structure. You don't have to keep track of that yourself.

Why might my first API call run fine, but the second one hang indefinitely?

I am trying to keep a session open with the Bloomberg Public API, relaying calls from my own service's API to it to fetch data. I am running the Node.JS / Express server locally right now. I have an API route that works fine the first time: I send the GET, and quickly get the response back. If I then send another GET to the same route, and I can see the data that the Bloomberg API returns in my server console, but it seems that the server gets stuck at the res.send(...) and I have no Idea why. I've tried numerous things like moving code blocks around and forcefully destroying variables, but to no avail. Do you guys see anything obvious that would/might work?
'use strict';
var _ = require('lodash');
var Blpapi = require('./blpapi.model');
var count = 0;
var blpapi = require('blpapi');
// Add 'authenticationOptions' key to session options if necessary.
var session = new blpapi.Session({ serverHost: '10.8.8.1', serverPort: 8194 });
var service_refdata = 1; // Unique identifier for refdata service
session.start();
session.on('SessionStarted', function(m) {
console.log(m);
session.openService('//blp/refdata', service_refdata);
});
session.on('ServiceOpened', function(m) {
console.log(m);
});
session.on('SessionStartupFailure', function(m) {
console.log('SessionStartupFailure', util.inspect(m));
session.stop();
session.destroy();
});
session.on('SessionTerminated', function(m) {
console.log('Session Terminated');
session.stop();
session.destroy();
});
exports.getStock = function (req, res) {
var stock = req.url.substring(8, req.url.length);
stock = stock.replace(/_/g, ' ');
session.on('HistoricalDataResponse', function(m) {
console.log(m);
if(m.eventType === 'RESPONSE' && m.correlations[0].value === 101) {
console.log('send');
res.send(m.data.securityData);
}
else {
res.send(500);
}
});
newRequest(stock);
};
function newRequest(sec) {
if(typeof sec !== 'string') return;
session.request('//blp/refdata', 'HistoricalDataRequest',
{ securities: [sec],
fields: ['PX_LAST', 'OPEN'],
startDate: "20140101",
endDate: "20140301",
periodicitySelection: "DAILY" }, 101);
}
function handleError(res, err) {
return res.send(500, err);
}
Edit1: If I change the res.send(m.data.securityData); to res.send(201);, the requests come back fine, so I'm figuring it has to do with that object.
I figured it out. It's because I was declaring the session.on('HistoricalDataResponse', .... statement inside of my route controller. Moving it out and adding a bit of logic around it solved the problem.

Code not terminating

I am using twitter API in my code and mongodb. The is reflecting the correct output in database, but it's not terminating. I guess the problem is with db.server.find({id:myid},cb); statement in code below. However, I don't know how to work it out.
var Twit = require('../lib/twitter'),
conf = require('../config1');
var myid;
var twit = new Twit(conf);
var databaseUrl = "mydb2"; // "username:password#example.com/mydb"
var collections = ["server", "followers"];
var db = require("mongojs").connect(databaseUrl, collections);
twit.get('account/verify_credentials', function (err, reply) {
myid = reply.id;
function addToServer(myid, cb) {
db.server.find({
id: myid
}, cb);
};
addToServer(myid, function (err, resp) {
if (err) {
console.log("err");
} else if (resp.length > 0) {
console.log("My Id present in server present");
} else {
console.log("New to the app.So updating server ");
db.server.insert({
id: myid
});
db.followers.insert({
id: myid,
following: []
})
}
});
});
P.S: This is a part of my code , I have also used process.exit(0) function, but still no help.
I think your issue is related to this: https://github.com/mafintosh/mongojs/issues/15.
Here's a gist. If I call db.close() the program exists, and if I don't, it doesn't. So process.on('exit') must not be the right place to call it.
But the issue is that that you have a persistent tcp connection open to the DB, and as long as that's running, the script won't shut down.
Is this a run-once script, or do you need to keep this thing running?
EDIT:
Since the script only needs to run once, I'd use callbacks on your 2 database queries and close the database down in the last callback.

Create a list of Connected Clients using socket.io

Here are 2 related questions. Posting them together makes more sense.
Question 1
I have a node.js app which emits an event out to all clients, and all current clients will respond with a ready emit. How can I create a list of all the clients that replied to the initial emit, and what kind of identification can be used to distinguish the clients?
Question2:
What I am trying to do after collect a list of connected clients, is to then access a MySQL database table of N number of rows and assign each client X rows each. These rows will be emitted back to their respective clients. How can this be done?
Current Code for Qn 1
Node Code
setInterval(function() {
util.log('Checking for new jobs...');
dbCheckQueue(function(results) { // checks if there are new rows to "distribute" to clients
if (results.length) {
util.log(results.length + ' new jobs found.');
io.sockets.emit('job_available');
}
});
}, 10*1000);
Client-side JS Code
socket.on('job_available', function() {
console.log('Job Available.. Responding with Ready!');
socket.emit('ready');
});
io.sockets.on('connection', function(socket) {
socket.on('ready', function() {
// UPDATE N rows with client_id in column checkout.
// Then SELECTS * from table where checkout = client_id
getListings(client_id, function(listings) {
socket.emit('job', listings); // send jobs
});
});
});
Current Code for Qn 2
The code works for a single client, but how do I loop through all connected clients and perform the same updating of column and selecting of rows?
io.sockets.on('connection', function(socket) {
socket.on('ready', function() {
// UPDATE N rows with client_id in column checkout.
// Then SELECTS * from table where checkout = client_id
getListings(client_id, function(listings) {
socket.emit('job', listings); // send jobs
});
});
});
Socket.io provides you with a public api for that, so instead of hacking something up like Bryan suggest you can use:
io.sockets.clients()
That will returns an array of all connected clients.
If you want all clients connected to a certain namespace:
io.of('/namespace').clients()
But you can even filter it even more.. if you want to have all sockets in a room:
io.sockets.clients('room name here as first argument')
Will return a array of connected sockets for the room room name here as first argument
You will need to keep track of the connected clients yourself. The simple way to do that would be to use an array:
var clients = [];
io.sockets.on('connect', function(client) {
clients.push(client);
client.on('disconnect', function() {
clients.splice(clients.indexOf(client), 1);
});
});
Then you can references that clients array on the server wherever you need to, in your ready event handler or whatever. Something like:
io.sockets.on('connection', function(socket) {
socket.on('ready', function() {
// UPDATE N rows with client_id in column checkout.
// Then SELECTS * from table where checkout = client_id
clients.forEach(function(client, index) {
var client_id = index; // Just use the index in the clients array for now
getListings(client_id, function(listings) {
socket.emit('job', listings); // send jobs
});
});
});
});

Resources