i want to connect db dynamically when user login nodejs mongodb - node.js

please help me to get out of it.
with login api i want to set mongoose connection string.
is it possible that with login api i set connection string and it works for other api also(which will call after login api)??
like first user login at that time i set db and for further api call works for that db?
i had tried
How to connect multiple mongodb database dynamically using mongoose?
but in this solution in each api they specifies requested param.
Can we set connection string of mongo by api call?
this is my app.js
var mongoose = require('mongoose');
var connections = [test = mongoose.createConnection('mongodb://localhost:27017/test'),
demo = mongoose.createConnection('mongodb://localhost:27017/demo')];
exports.getDatabaseConnection = function (dbName) {
if (connections[dbName]) {
//database connection already exist. Return connection object
return connections['dbName'];
} else {
connections[dbName] = mongoose.createConnection('mongodb://localhost:27017/' + dbName);
return connections['dbName'];
}
}
and this is my user.js
exports.authenticate = function (req, res) {
var db = app.getDatabaseConnection(req.body.keydb);
var userDetail = db.model('userdetail', UserSchema);
userDetail.findOne({
email: req.body.email,
userType: req.body.userType
}, function (err, user) {
if (err) throw err;
if (!user) {
res.send({ status: 400, message: req.i18n.__("user.authIncorrectEmail") });
} else {...}
});
}
when i am requesting keydb value as demo..then it should select demo string which is in connection array.
but it is not working.what i am doing wrong??

i am trying to connect mongoDb as per user login request comes. i want to set db once only not on each api call. if user logout then db should be disconnect and when again user login it should again connect.
You need to keep a list of database connections, one per session. One way to do it would be to have an object where the keys are the session IDs and the values are the database connections. Alternatively you can keep one connection per user ID if you want to allow a situation where the same user has multiple sessions and they each share the database connection. Now when you create a session on user login you also create a new database connection. When you destroy a session on user logout you have to close the connection and remove it from the object where it was stored. For all other endpoints except login and logout you just look up the connection and use it.
This is how you can achieve your goal but keep in mind that keeping a separate database connection per logged in user has many disadvantages and wastes a lot of resources, both in your application and in the database.
Can we set connection string of mongo by api call?
Yes, of course. But this will mean that people will be able to trick your app into e.g. breaking into other people's databases and you will be responsible for all of the legal consequences of that.

Related

Connections from mongoose createConnection not getting closed. Multi tenant app

I've been creating a multi tenant app where I've been creating the database connections on the fly as soon as I resolve the tenant database connection string from the request that has just hit the server.
It's working as expected, but the connections keeps adding up and never they are never getting disconnected.
From what I've been reading, it seems like the mongoose.connect manages the connections but the mongoose.createConnection doesn't, I'm not sure if my undestanding is correct here.
I thought on creating my own connection pool with map in memory and use the connection from the map if it already exists in the map, but I'm not sure if this is a good approach.
Does anyone know if there is a npm connection pool package already built for this issue? Or any implementation ideas?
I also thought closing out each connection manually when the request lifecycle ends, but it will affect the performance if I have to connect and disconnect from mongo per each request, instead of using a connection pool.
Here is the part of the code I'm creating the connection, nothing special here because I'm always creating the connection.
// ... Resolve connection string from request
let tentantConn;
try {
// One connection per tenant
tentantConn = await mongoose.createConnection(
decrypt(tenant.dbUrl),
{
useNewUrlParser: true,
useUnifiedTopology: true
});
}catch (e) {
req.log.info({ message: `Unauthorized - Error connecting to tenant database: ${currentHostname}`, error: e.message });
return reply.status(401).send({ message: `Unauthorized - Error connecting to tenant database: ${currentHostname}`, error: e.message });
}
// ...
The connection pool is implemented on the driver level:
https://github.com/mongodb/node-mongodb-native/blob/main/src/cmap/connection_pool.ts
By default it opens 5 connection per server. You can change pool size but you cannot disable it.
Now, terminology is a bit confusing, as a single mongodb server / cluster can have multiple databases. They share the same connection string - same 5 connections from the pool regardless of number of databases.
Assumption your tenants have individual clusters and do connect to different mongodb servers, in order to close these connections you need to explicitly call
await mongoose.connection.close()
Took me few days to be able to get back to this issue, but I was able to tweak my code and now the connection count on mongodb atlas seems to be stable. I'm not super happy to be using a global variable to fix this issue, but it is solving my issue for now.
async function switchTenantConnection(aConnStr, aDbName, aAsyncOpenCallback){
const hasConn = global.connectionPoolTest !== null;
if(!hasConn){
const tentantConn = await getTenantConnectionFromEncryptStr(aConnStr);
if(aAsyncOpenCallback){
tentantConn.once('open', aAsyncOpenCallback);
}
tentantConn.once('disconnected', async function () {
global.connectionPoolTest = null;
});
tentantConn.once('error', async function () {
global.connectionPoolTest = null;
});
global.connectionPoolTest= { dbName: aDbName, connection: tentantConn, createdAt: new Date() };
return tentantConn;
}
return global.connectionPoolTest.connection.useDb(aDbName);
}

How to get current socket object or id with in a sails controller?

I would like to access the currently connected socket id with in a sails.js (v0.12 ) controller function.
sails.sockets.getId(req.socket); is showing undefined since this is not a socket request
My objective is to set the online status of my user in the database when he logged in successfully
login: function (req, res) {
Util.login(req, function(){
var socketId = sails.sockets.getId(req.socket);
console.log('socketId ===', socketId); // return undefined
});
},
Basically i would like to access the current user's socket object in a controller or access current user's session object with in a socket on method
Also i'm not sure that how can i rewrite my old sockets.onConnect
handler
onConnect: function(session, socket) {
// Proceed only if the user is logged in
if (session.me) {
//console.log('test',session);
User.findOne({id: session.me}).exec(function(err, user) {
var socketId = sails.sockets.getId(socket);
user.status = 'online';
user.ip = socket.handshake.address;
user.save(function(err) {
// Publish this user creation event to every socket watching the User model via User.watch()
User.publishCreate(user, socket);
});
// Create the session.users hash if it doesn't exist already
session.users = session.users || {};
// Save this user in the session, indexed by their socket ID.
// This way we can look the user up by socket ID later.
session.users[socketId] = user;
// Persist the session
//session.save();
// Get updates about users being created
User.watch(socket);
// Send a message to the client with information about the new user
sails.sockets.broadcast(socketId, 'user', {
verb :'list',
data:session.users
});
});
}
},
You need to pass the req object to the method.
if (req.isSocket) {
let socketId = sails.sockets.getId(req);
sails.log('socket id: ' + socketId);
}
Since the request is not a socket request, you might need to do something like
Send back some identifier to the client once logged in.
Use the identifier to join a room. (One user per room. )
Broadcast messages to the room with the identifier whenever you need to send message to client.
https://gist.github.com/crtr0/2896891
Update:
From sails migration guide
The onConnect lifecycle callback has been deprecated. Instead, if you need to do something when a new socket is connected, send a request from the newly-connected client to do so. The purpose of onConnect was always for optimizing performance (eliminating the need to do this initial extra round-trip with the server), yet its use can lead to confusion and race conditions. If you desperately need to eliminate the server roundtrip, you can bind a handler directly on sails.io.on('connect', function (newlyConnectedSocket){}) in your bootstrap function (config/bootstrap.js). However, note that this is discouraged. Unless you're facing true production performance issues, you should use the strategy mentioned above for your "on connection" logic (i.e. send an initial request from the client after the socket connects). Socket requests are lightweight, so this doesn't add any tangible overhead to your application, and it will help make your code more predictable.
// in some controller
if (req.isSocket) {
let handshake = req.socket.manager.handshaken[sails.sockets.getId(req)];
if (handshake) {
session = handshake.session;
}
}

How to add current logged in status to Users Schema in MongoDB using Passport and node.js

I’m quite new to backend development…
With using my API I would like to be able to display a list of users and also indicate if they are currently logged in. I got the basic authentification working using passport and json web token
I’m not looking to get the current logged in user.
I want to be able to retrieve a list of users and see if they are logged in or not.
Like this:
var users = Users.find({});
// console.log(users) output:
{
name: 'foo'
password: ...
isLoggedIn: false
},
{
name: 'bar'
password: ...
isLoggedIn: true
},
{
name: 'baz'
password: ...
isLoggedIn: false
}
isLoggedIn would be set to true if the user is currently logged in and to falseif not.
How can I do that? Thank you!
It sounds like what you would like to do is update your MongoDB database based on login/logout events. To do this you could use something like mongoose to work with your Node backend to easily access your database in MongoDB.
You can include mongoose after installing with npm install mongoose like so:
var mongoose = require('mongoose');
var User = mongoose.model('User');
Note that User corresponds to whatever schema you create for storing user information.
Assuming you have some sort of router object for handling requests, you could construct route handlers for /logout and /login and use your imported mongoose User model to retrieve and then modify a specific User object as such:
// whenever user goes to '/login' (you can have, say, your 'login' button make a request to this URL
router.get('/login', function(req,res) {
// your authentication here; passport stores the currently authenticated user in req.user
var username = req.user.name; // here we assume the username is stored as 'name' as you have in your code but change this based on your schema
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = true;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to some page here maybe
}
});
});
});
// whenever user goes to '/logout' (you can have a logout button make a request to this URL
router.get('/logout', function(req,res) {
// currently authenticated user is still in req.user
var username = req.user.name;
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = false;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to login/register page maybe
}
});
});
});
So to summarize what this code would do:
based on the url a user would go to, our route handler would fetch one correct, unique User object from our database based on the name (username)
it would do so by accessing the username property of req.user which corresponds to the currently authenticated user with Passport, which, again will be different for all users
update the field that we use to keep track of login status (isLoggedIn)
and then save the changes, after which we are done updating the state to reflect whether the user is logged in or not, so we can now redirect to some other page or display other content
Finally then, you could retrieve a list of all users similarly to your code like so:
User.find({}, function(err, users, data) {
// all users from your database are in `users`
console.log(users);
});
Edit for expired sessions:
So, to track expired sessions, since you're using Passport, would in theory require functionality to signal with some sort of event / callback / message, etc. the moment the session is deemed invalid. Now that is tough to monitor and from my experience with Passport, stuff like that isn't implemented in all authentication strategies and might vary based on the strategy to be used by developers (think for instance if a browser window is closed, based on Passports authentication strategy, or just browser, it might destroy the cookie for the session right away and our server has no way of knowing about it). I do recommend checking out all the authentication strategies Passport offers in case there are some better ones here.
Now, if you would like to add functionality to track the users passive login/logout status with sessions yourself, you could use something related to cookies. Again, not necessarily one to use, but here's a couple handy Express modules: cookie-parser and cookie-session.
Then, you could set and read cookies like this, using cookie-parser:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
You would put this code somewhere right after the user is authenticated:
// cookies are stored here
console.log(req.cookies);
// configure your cookie
var options = {
expires: 1000 * 60 * 60, // expires after one hour
httpOnly: true
}
// Set cookie
res.cookie('session', ('user-' + req.user.name), options);
And then, on the client side check if that cookie is valid continuously on some time interval, and if it expired Date.now() > cookie.expires then make a GET request to /logout, and there log out the user (currently still authenticated) by updating MongoDB and all.
However, since this would require making a mechanism to basically simulate an expired session, I would recommend using something analogous to a timeout, which would be much easier to implement. Just a note, this is sort of analogous to mechanisms on some pages you might have encountered where you get a pop-up saying 'You will be logged out due to inactivity'. In your main.js or whatever client-side script define a function to keep going on a time-out, unless the user does some action.
var inactivity = function () {
var t;
// user doing something on your page, so keep resetting time counter when events happen
document.onmousemove = resetTimer;
document.onkeypress = resetTimer;
// this is a callback function that will get called once a time-out countdown is done
function timeOut() {
// make a request to '/logout' here and logout the current user (you still will have access to req.user from Passport)
// also can redirect from back-end route handler to the login page for instance
}
// this gets called whenever an event happens, resetting the counter of sorts
function resetTimer() {
t = 0;
t = setTimeout(timeOut, 1000 * 60 ) // set this to however long you should wait to log out your user time (in milliseconds)
}
};
So basically what this approach would let you do, is automatically invalidate sessions yourself, which means you would have much greater control over updating the state of your database and logging users out.
Hope this helps!

A possible timeout in find() method - mongoDb

I'm writing a Node.js project using MongoDb and socket.io. I met one problem. I have a collection called rooms rooms = db.collection('rooms');
Here's my code when i'm trying to join a room
rooms.find({name: roomName}).limit(1).toArray()
.then(function (res) { //if successful
res = res[0]; //i get my username
If there's a room with the given id I connect to it, otherwise it must be created.
But when i create a new room i get undefined, but when I connect the second time (the room already exists) it's all ok, i get all the information. So when I create a room and connect to it, I don't get my username, but when it's created (I'm connecting to it the second time) it displays me the method.
var mongo = require('mongodb').MongoClient - module,
function http(io) { mongo.connect(config.mongodb_url)
.then(function (db) { ...
-connection,
"mongodb version": "^2.1.7"
This is how i create a room
rooms.findOneAndUpdate(
{name: roomName},
{$addToSet: {users: uuid}}
).then(users_update).catch(logger.error);
}).catch(logger.error);
I'm wondering if there are some timeout in the find method? Or where can be here the mistake?
I found the answer. It was just that simple: I added { upstream: true } to my findOneAndUpdate() function.

How can I listen to session destroyed event?

I'm currently develop an application with Sails.JS.
I want to count the number of online users and update it once they login/ logout or there session expire, but I don't know how to implement something like session destroyed event and can't update the number of online user whenever a session is expired without user logging out.
As other said above, there is no such events in the default session implementation, Sails session are close to ExpressJs Session, i recommend you to read this article about ExpressJs Sessions :
http://expressjs-book.com/forums/topic/express-js-sessions-a-detailed-tutorial/
Then one idea in order to achieve what you want could be to use a store and query inside of it.
Did you though about other solutions such as using socket.io (built in sails) and adding your users into a channel upon login and then simply counting user inside your channel ?
You can wrap the session.destroy() function like so:
var destroyWrapper = buildDestroyWrapper(function(req){
//do stuff after req.destroy was called
});
function buildDestroyWrapper(afterDestroy){
return function(req){
req.destroy();
afterDestroy(req);
};
}
//later, in your controller
function controllerAction(req,res,next){
destroyWrapper(req);
}
this method allows you to handle destruction differently, depending on what callback you pass to buildDestroyWrapper. For example:
var logAfterDestroy = buildDestroyWrapper(function(req){
console.log("session destroyed");
});
var killAfterDestroy = buildDestroyWrapper(function(req){
process.kill();
});
function buildDestroyWrapper(afterDestroy){
return function(req){
req.destroy();
afterDestroy(req);
};
}
//later, in your controller
function logoutAction(req,res,next){
logAfterDestroy(req);
}
function killAppAction(req,res,next){
killAfterDestroy(req);
}

Resources