I am using node.js with socket.io to push real time notifications to users. However, currently I am just sending back a query result done in my socket.io code and sending it back to the client but I need to let socket know about the changes that occur and to either update with the changes or re-query the db to check for the new number and send that to the client.
For example if a user gets a friend request then the notification count will change and I want socket.io to push the new notification count number to the user.
here is my socket.io code in my app.js file:
io.on('connection', function(socket) {
var sessionID = socket.handshake.sessionID,
session = new connect.middleware.session.Session({ sessionStore: sessionStore }, socket.handshake.session)
console.log('socket: new ' + sessionID)
socket.broadcast.emit('arpNewConn', session.passport.user)
var intervalID = setInterval(function() {
socket.handshake.session.reload(function() {
socket.handshake.session.touch().save()
})
socket.emit('pulse', { heartbeat: new Date().toString(), timestamp: new Date().getTime() })
}, 300 * 1000)
socket.on('disconnect', function() {
console.log('socket: dump ' + sessionID)
socket.broadcast.emit('arpLostConn', session.passport.user)
clearInterval(intervalID)
})
socket.emit('entrance', {message: 'Message works'});
dbnotif.findOne(userID, function (err, user) {
if(err) throw err;
notify = user.notifications;
socket.emit('notify', {notific: notify});
});
});
Here is the client side:
div#CheckSocket
script(src='http://localhost:3000/socket.io/socket.io.js')
script.
$(document).ready(function () {
console.log('socket');
var socket = io.connect('http://localhost:3000/');
console.log('entered1');
socket.on('entrance', function (data) {
console.log('entered');
console.log(data.message);
});
socket.on('notify', function (data) {
console.log('noting');
console.log(data.notific);
if(data.notific !== 0)
$('.notifications').html(data.notific);
});
socket.on('reconnecting', function(data) {
setStatus('reconnecting');
console.log('entered2');
});
function setStatus(msg) {
console.log('connection status: ' + msg);
console.log('entered5');
}
});
Here is the example of adding a friend in the route file:
exports.addContactPost = function(req, res, err) {
async.waterfall([
function(callback) {
var success;
var newFriend = new Friend ({
userId: req.signedCookies.userid,
friend_id: mongoose.Types.ObjectId(req.body.otherUser),
friend_status: 1
});
newFriend.save(function(err){
if(err) {
console.log(err);
} else {
console.log("saved it");
success = true;
}
});
callback(null, success)
},
function(success, callback) {
//if(success === true) {
var success2;
var newFriend2 = new Friend ({
userId: mongoose.Types.ObjectId(req.body.otherUser),
friend_id: req.signedCookies.userid,
friend_status: 2
});
newFriend2.save(function(err){
if(err) {
res.send("request not received");
} else {
success2 = true;
}
});
callback(null, success2);
//} else {
// res.send("error with request sent");
//}
},
function(success2, callback) {
console.log('callback3');
//if(success2 === true) {
var success3;
Notification.findOneAndUpdate({userId: mongoose.Types.ObjectId(req.body.otherUser)}, {
$inc: {notifications: 1}
}, function(err, notify) {
if(err) {
res.send(err);
} else {
console.log(notify);
if(notify.added_notifications === true) {
// enable mail and include general u have got a new request... do not include name because not storing it
}
}
success3 = true;
callback(null, success3);
}],
function(err, results) {
res.json({response: true});
console.log("Add successful");
});
};
Notes: dbnotif is a model being called by mongoose,
userID is a global variable available to the file
I helped him solve this question offline, but we ended up using an EventEmitter as a proxy.
// main.js
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
Then add it to each request as middleware:
// elsewhere in main.js
app.use(function(req, res, next) {
req.emitter = emitter;
next();
});
Then in external routes file:
// routes.js
exports.addContactPost = function(req, res, err) {
req.emitter.emit( 'some-key', whatever, data, you, want );
};
Related
I have seen multiple answers for the question but I am not able to solve this issue, so I am re-posting it with my code. I am new to this and need help to understand and fix this.
My Code
// Get logs from DB
router.get("/getlogs/:dbtype", function (req, res, next) {
var dbtype = req.params.dbtype;
if (dbtype == "mongodb") {
list = Logs.find(function (err, log) {
res.json(log);
})
} else if (dbtype == "mssql") {
var config = {
userName: 'user',
password: 'pass',
server: 'server',
options: {
instanceName: 'instance',
database: 'db',
}
};
// SQL Server connection
var connection = new Tedious.Connection(config);
connection.on('connect', function (err) {
// if (err) { console.log(err); res.json(err);}
// If no error, then good to proceed.
//console.log("SQL Server Connected SQL SQL");
//connection.on('debug', function(err) { console.log('debug:', err);});
var request = new Tedious.Request(`select top 1 * from table1`, function (err) {
//if (err) { console.log(err); res.json(err);}
});
request.on('row', function (columns) {
var row = {};
columns.forEach(function (column) {
row[column.metadata.colName] = column.value;
});
rows.push(row);
res.json(rows); **<-- this is where I get error**
});
request.on('end', function () {
res.json(rows);
})
connection.execSql(request);
});
}
});
It's because you are sending response every time you recive row.
Change this:
request.on('row', function (columns) {
var row = {};
columns.forEach(function (column) {
row[column.metadata.colName] = column.value;
});
rows.push(row);
res.json(rows);
});
to:
request.on('row', function (columns) {
var row = {};
columns.forEach(function (column) {
row[column.metadata.colName] = column.value;
});
rows.push(row);
});
request.on('doneInProc', function () {
res.json(rows);
});
My nodejs app is working well neither sockets server. Its connecting with user side but I can't handle any events.
var app = require("express")();
var http = require("http").createServer(app);
var io = require("socket.io")(http);
http.listen(appSettings.RUNNING_PORT, function () {
globals.debug('Server is running on port: ' + appSettings.RUNNING_PORT, 'success');
});
io.set('authorization', function (handshakeData, accept) {
if (handshakeData && handshakeData.headers && handshakeData.headers.referer) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('**' == domain) {
accept(null, true);
} else {
globals.debug('Bad site authentication data, game will be disabled.', 'danger');
return accept('Bad site authentication data, game will be disabled.', false);
}
} else {
accept('Failed transaction', false);
}
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
users.prepare(io, []);
users.defineUser(userToken, sock.id, next);
// start the game
game.prepare(io, []);
game.startPolling();
});
});
so I'm connecting here and calling for some methods. users.defineUser(userToken, sock.id, next);
this.defineUser = function (token, sockID, next) {
if (token) {
Promise.try(function () {
return db('users')
.where('key', '=', token)
.orderBy('id', 'desc')
.limit(1);
}).then(function (result) {
if (result && result.length > 0) {
self.io.to(sockID).emit('connect-success', {
message: 'Successfully connected',
data: {
name: result[0].name
}
});
globals.debug('User '+ result[0].name +' connected as ' + sockID, 'success');
next('Successfully connected', true);
} else {
self.io.to(sockID).emit('global-error', {
message: 'Only connected users are available to play'
});
next(false, false);
}
}).catch(function (e) {
if (e.code) {
self.io.to(sockID).emit('global-error', {
message: 'There was a problem with our database, please try later. ' + e.code
});
next(false, false);
}
});
} else {
self.io.to(sockID).emit('global-error', {
message: 'Only connected users are available to play'
});
next(false, false);
}
};
so I can see debug for "User Sandra connected as /#Q82F5WCvLZvgy65YAAAB" but when I try to send anything to user it does not work.
Even in frontend I can see that app were connected with user by calling socket.on('connection) method. So where the problem could be?
i am completely newbie in node.js, and trying learn how it actually works. I know that by default all node.js function calls are asynchronous. Now i need LDAP authentication in my application where i need wait for the server response to check whether the user credentials are right or wrong.The ldap part is working fine but i am not sure on how to return data from a function call in synchronous way. below is the part of my code.
router.js
var express = require('express');
var router = express.Router();
var tools = require('./authenticateUser');
router.post('/authenticateUser', function(req, res) {
// In the below line i am calling the method which
// should return the userDN (a string)
tools.searchUser(req.body.user, req.body.passwd);
res.render('home.jade');
});
authenticateUser.js
module.exports = {
searchUser : function (username, password) {
adminDN = *************;
adminPassword = '*********';
baseDN = '***';
var ldap = require('ldapjs');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var adminClient = ldap.createClient({
url: '*******'
});
var opts = {
filter: '(&(objectClass=userProxyFull)(sAMAccountName=' + username + '))',
scope: 'sub',
attribute: ['sAMAccountName']
};
console.log('--- going to try to connect user ---');
try {
adminClient.bind(adminDN, adminPassword, function (error) {
if (error) {
console.log(error.message);
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('adminClient disconnected');
}
});
} else {
// Searching Client ID in LDS
adminClient.search(baseDN, opts, function (error, search) {
console.log('Searching.....' + userDN);
search.on('searchEntry', function (entry) {
if (entry.object) {
// Here i need to return the object back
//to the router.js from where i call in a synchronous way
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
}
});
}
});
search.on('error', function (error) {
console.error('error: ' + error.message);
});
});
}
});
} catch (error) {
console.log(error);
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('client disconnected');
}
});
} finally {
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('client disconnected');
}
});
}
},
};
You have to pass res.render('home.jade') as a function(the callback) to your searchUser function.
It should look like
tools.searchUser(req.body.user,
req.body.password,
res}
)
searchUser function
searchUser : function (username, password,res) {
...
finally(){
res.render('home.jade');
}
}
I have used wikipedia-js for this project. This is my code for summary.js file.
var wikipedia = require("wikipedia-js");
var something = "initial";
module.exports = {
wikitext: function(topicname) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return;
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
something = htmlWikiText;
});
return something;
},
};
This module I am using in /wiki/:topicname route. The corresponding code in index.js is like this.
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
var first = summary.wikitext(topicname);
res.send("Hello "+first);
});
The problem is, everytime i visit a wiki/some-topic, the last return statement of summary.js executes before htmlWikiText is populated with content. So I always see hello initial on the browser page. Although after sometime it gets printed on terminal due to console.log statement.
So how should I resolve this issue?
I'm not going to try turning this code into synchronous. I'll just correct it to work as an asynchronous version.
You need to pass in callback to wikitext() and return the value in that callback. Here is the revised code of wikitext() and the route that calls it:
var wikipedia = require("wikipedia-js");
module.exports = {
wikitext: function(topicname, callback) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return callback(err);
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
callback(null, htmlWikiText);
});
}
};
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
summary.wikitext(topicname, function(err, result) {
if (err) {
return res.send(err);
}
if (!result) {
return res.send('No article found');
}
res.send("Hello "+result);
});
});
I am having trouble understanding node.js.
Example, MongoDB access, here's what I've got (mydb.js):
var mongodb = require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db = new mongodb.Db('mydb', server);
function authenticateAndGo(db, handle) {
db.authenticate('username', 'password', function(err) {
if (err) {
console.log(err);
return;
}
console.log('Database user authenticated');
var collection = new mongodb.Collection(db, 'test');
handle(collection);
});
}
function query(handle) {
db.open(function(err, db) {
if( err ) {
console.log(err);
return;
}
console.log('Database connected');
authenticateAndGo(db, handle);
});
};
exports.query = query;
So, if I want to use it later, I would
var mydb = require('./mydb');
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
But, If I do multiple calls, like so:
var mydb = require('./mydb');
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
I get an exception:
Error: db object already connecting, open cannot be called multiple times
I think that there is really something fundamental that I do not understand about all this and it is probable that this question is stupid ...
Anyway, all help is welcome.
Thanks in advance.
mydb.js:
var mongodb= require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db1 = new mongodb.Db('mydb', server);
// callback: (err, db)
function openDatabase(callback) {
db1.open(function(err, db) {
if (err)
return callback(err);
console.log('Database connected');
return callback(null, db);
});
}
// callback: (err, collection)
function authenticate(db, username, password, callback) {
db.authenticate(username, password, function(err, result) {
if (err) {
return callback (err);
}
if (result) {
var collection = new mongodb.Collection(db, 'test');
// always, ALWAYS return the error object as the first argument of a callback
return callback(null, collection);
} else {
return callback (new Error('authentication failed'));
}
});
}
exports.openDatabase = openDatabase;
exports.authenticate = authenticate;
use.js:
var mydb = require('./mydb');
// open the database once
mydb.openDatabase(function(err, db) {
if (err) {
console.log('ERROR CONNECTING TO DATABASE');
console.log(err);
process.exit(1);
}
// authenticate once after you opened the database. What's the point of
// authenticating on-demand (for each query)?
mydb.authenticate(db, 'usernsame', 'password', function(err, collection) {
if (err) {
console.log('ERROR AUTHENTICATING');
console.log(err);
process.exit(1);
}
// use the returned collection as many times as you like INSIDE THE CALLBACK
collection.find({}, {limit: 10})
.toArray(function(err, docs) {
console.log('\n------ 1 ------');
console.log(docs);
});
collection.find({}, {limit: 10})
.toArray(function(err, docs) {
console.log('\n------ 2 ------');
console.log(docs);
});
});
});
Result:
on success:
Database connected
Database user authenticated
------ 1 ------
[ { _id: 4f86889079a120bf04e48550, asd: 'asd' } ]
------ 2 ------
[ { _id: 4f86889079a120bf04e48550, asd: 'asd' } ]
on failure:
Database connected
{ [MongoError: auth fails] name: 'MongoError', errmsg: 'auth fails', ok: 0 }
[Original Answer]:
You're opening the db multiple times (once in each query). You should open the database just once, and use the db object in the callback for later use.
You're using the same variable name multiple times, and that might've caused some confusion.
var mongodb = require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db1 = new mongodb.Db('mydb', server);
function authenticateAndGo(db, handle) {
db.authenticate('username', 'password', function(err) {
if (err) {
console.log(err);
return;
}
console.log('Database user authenticated');
var collection = new mongodb.Collection(db, 'test');
handle(collection);
});
}
function query(handle) {
db1.open(function(err, db2) {
if( err ) {
console.log(err);
return;
}
console.log('Database connected');
authenticateAndGo(db2, handle);
});
};
exports.query = query;
I've changed the above code a little (db1 for the original db, db2 for the opened db). As you can see, you're opening db1 multiple times, which is not good. extract the code for opening into another method and use it ONCE and use the db2 instance for all your queries/updates/removes/...
You can only call "open" once. When the open callback fires, you can then do your queries on the DB object it returns. So one way to handle this is to queue up the requests until the open completes.
e.g MyMongo.js
var mongodb = require('mongodb');
function MyMongo(host, port, dbname) {
this.host = host;
this.port = port;
this.dbname = dbname;
this.server = new mongodb.Server(
'localhost',
9000,
{auto_reconnect: true});
this.db_connector = new mongodb.Db(this.dbname, this.server);
var self = this;
this.db = undefined;
this.queue = [];
this.db_connector.open(function(err, db) {
if( err ) {
console.log(err);
return;
}
self.db = db;
for (var i = 0; i < self.queue.length; i++) {
var collection = new mongodb.Collection(
self.db, self.queue[i].cn);
self.queue[i].cb(collection);
}
self.queue = [];
});
}
exports.MyMongo = MyMongo;
MyMongo.prototype.query = function(collectionName, callback) {
if (this.db != undefined) {
var collection = new mongodb.Collection(this.db, collectionName);
callback(collection);
return;
}
this.queue.push({ "cn" : collectionName, "cb" : callback});
}
and then a sample use:
var MyMongo = require('./MyMongo.js').MyMongo;
var db = new MyMongo('localhost', 9000, 'db1');
var COL = 'col';
db.query(COL, function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log("First:\n", docs);
});
});
db.query(COL, function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log("\nSecond:\n", docs);
});
});
I simply call the open function once directly after the db init:
var mongodb = require('mongodb');
var server = new mongodb.Server('foo', 3000, {auto_reconnect: true});
var db = new mongodb.Db('mydb', server);
db.open(function(){});
After that I do not have to care about that anymore because of auto_reconnect is true.
db.collection('bar', function(err, collection) { [...] };