Open a url on user web browser using nodeJS - javascript

I am building a simple VoIP app using asterisk-manager module on nodeJS. The asterisk server is installed on centos 7 (basic install) and is hosted on a virtual machine. The code below, listens for agent login event, and popups a url when it receives dtmf key:
var port = 5038,
host = 'asteriskIP',
username = 'popup',
password = 'popup',
open = require('open'),
mysql = require('mysql'),
ami = new require('asterisk-manager')(port, host, username, password, true);
ami.keepConnected();
//Mysql server connection pool
var pool = mysql.createPool({
host: host,
user: 'user',
password: 'password',
database: 'db'
});
ami.on('newstate', function (stateEvent) {
var channelState = stateEvent.channelstate;
if (channelState === '6') {
return false;
}
/*
Listen for new channel after agent login
*/
ami.on('newchannel', function (e) {
/* Check if caller id number is empty (This is necessary owning to new channel created as a result of
DTMF. If this returns true, return false else execute mysql query.
*/
if (e.calleridnum === '' && isNaN(e.calleridnum)) {
return false;
} else if (e.calleridnum !== '' && !isNaN(e.calleridnum)) {
var callerId = e.calleridnum;
sql = "INSERT INTO dtmf (caller_id) VALUES ?",
values = [[callerId]];
pool.query(sql, [values], function (error) {
if (error) throw error;
});
/*
Listen for DTMF on 'end' and update current caller table
*/
ami.on('dtmf', function (evt) {
var end = evt.end;
if (end === 'Yes') {
var digit = evt.digit;
sql = `UPDATE dtmf SET caller_lang = ${digit} WHERE caller_id = ?`,
values = [[callerId]];
pool.query(sql, [values], function (error) {
if (error) throw error;
});
/*
This piece of code retrieves DTMF code input and popsup
a url in the agents browser window.
*/
ami.on('bridge', function (evt) {
var state = evt.bridgestate;
if (state === 'Link') {
switch (digit) {
case '1':
open('http://someurl?' + digit);
break;
case '2':
open('http://someurl?' + digit);
break;
default:
}
}
})
}
});
}
return false;
});
});
Everything works fine when I run this code on my mac. However, when I deployed the code to the virtual machine, it inserts and updates the database normally, but no url pops up in my browser. Please is there a way nodeJS app deployed on a virtual machine, can open a window on a users local browser? Thanks.

For security reason there is no browser or OS that will let you pop up a browser window on someone else computer without first being connected.
For that i think you would have to build a client app for example a widget, service or browser extension that would be running on the person computer ... This client could use Socket.io to listen and react to event happening on the Node.js end.
That could be one solution.

Related

Web Serial API crashes Zebra DS9208 on initial connection

I am attempting to use a Zebra DS9208 scanner to capture barcode data into a web page. For some reason, when I first select the serial port and connect to the scanner, the very first scan causes the barcode reader to crash (it locks into read mode, refuses to read additional scans, then disconnects from the computer and reboots itself). When I establish the connection a second time, the scanner continuously reads scans without issues. Anyone able to spot anything in my code that could cause this?
Connect Script:
async function connect() {
// - Request a port and open a connection.
port = await navigator.serial.requestPort();
// - Wait for the port to open.
await port.open({ baudRate: 9600 });
// Send a bunch of Bell characters so we can hear that the scanner understands us.
await quadBeep();
// Read the stream let textDecoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(textDecoder.writable);
reader = textDecoder.readable.getReader();
readScans();
}
Beep Script (used to make the scanner beep by sending a BELL character)
function quadBeep() {
console.log('Quad Beep Requested');
//Write to output stream
const writer = port.writable.getWriter();
const data = new Uint8Array([07, 07, 07, 07]);
writer.write(data);
//allow the serial port to be closed later
writer.releaseLock();
return;
}
Read Loop to source data from the scanner:
`async function readScans() {
//Listen to data coming from the serial device
async function readScans() {
while (true) {
try {
const { value, done } = await reader.read();
await saveScan(value); //process the scan
console.log('readScans Barcode: ' + value);
document.getElementById('scan').innerHTML += value;
if (done) {
//Allow the serial port to be closed later.
console.log('readScans done value: ' + done);
reader.releaseLock();
break;
}
} catch(error) {
console.log('readScans Error: ' + error);
break;
}
}
}
Save Function, to write the data (eventually will submit the barcode via AJAX)
function saveScan(barcode) {
var session = document.getElementById('sessionID').getAttribute('data-value');
if (barcode == previousBarcode) {
//duplicate scan.
console.log('saveScan Duplicate');
return;
} else {
//Submit the scan
previousBarcode = barcode; //store the barcode so it doesn't get rescanned
console.log('saveScan Previous set to: ' + barcode);
//future AJAX FUNCTION GOES HERE
return;
}
}

Getting a function to execute on a node server when data recieved

I'll try to keep this simple. I'm working on a paging app.
What I have: A terminal without internet, running a desktop app made from node.js using express,ejs, ect. I also have a physical server with internet access and a mssql database on this machine I have a node.js server that interfaces with the database, collects the data and send sends it over to terminal via websocket when the app is launched, the node.js app gets this data and is rendered to the web interface using ejs. This data is displayed in a form with a button that when clicked fires the app.post route. In the app.post that data is packaged in an array, and sent back to the server using a second websocket connection. I can then take that form data (a name, phone#, and a radio button value) and form a SMS message using and using twilio send a message to that number (with addition info)
Yes, I have two websocket connections one Server > client serves data to web app
and another client > server serves form data to the server.
What's working: I get data from the boh server/database > webserver/client and data is displayed correctly when I hit the button data is packaged and send back to the boh server.
On the boh server i have a function that get the data array and parses it out, and sends an SMS message using twilio
My issue: I have to restart the server app to get it to process the data and send the message, if I hit the page button, it does all the stuff in the background it should, packages the array and sends the info to the server. The server is waiting for the data to be sent and client has sent the data, however it will only send off the text message is a stop and restart the node.js server, if i do that, the server starts and runs through the initial process of getting the sql data, and sets it up to be called when the app launches, then continues on to read that data was sent from the client, received and it will parse the data from the SMS message through a function send the message, wait a few seconds and then grab the response and confirm delivery of message. I am quite sure I am missing something basic here, but I have different functions but nothing I do will seem to get it to fire when the data is received.
I'm new at node and not very advanced in js, but I understand on some level why its not firing as is right, script is running, data has not been sent, so it stops when it gets here, then i hit the button, and it does nothing, because the script is stopped, and it doesn't has know way of knowing that data was sent, but when i rerun the script, the data that was sent is still sitting there wait to be received, so it recognizes the open websocket, and the sent data and work appropriately, I feel like a am missing something on the server side that tell it to wait for the data send but have not been able to make it work
twilws.onopen = async() => {//when the page button is presses, it starts a websocket server on the client, if that makes sense
twilws.send('test')
if (twilws.readyState === WebSocket.OPEN) {
logger.info('Twilio Sockpuppet Connected')
} else {
logger.error('Twilio Sockpuppet Fail')
}
twilws.onmessage = async (e) => {//after i hit the page button the server is on and the array is sent, i am trying to get this to be waiting for data and when it arrives go, but it will only do that when i restart the script
try {
data = JSON.parse(e.data);//parse the data
logger.info('got the edata ' + e.data)
//main(data) this will fire but not when the button is pressed.
} catch (er) {
logger.error('socket parse error: ' + e.data);
}
}
}
twilws.onclose = () => {//close the connection, purges that data so that the websocket can be recreated and an array with new data sent.
logger.info('Web Socket Connection Closed');
twilws.close(1000, 'all done');
};
Here is the whole server
I believe i may be blocking something, sorry about the bad formatting i have been changing and trying different things for a couple weeks off and on now and have not had a chance to clean thing up.
const express = require("express");
const cors = require("cors");
const app = express();
const db = require("./app/models");
const twilioconfig = require('./configs/twilioConfig.js');
const path = require('path');
const WebSocket = require('ws');
const sql = require('mssql');
const WebSocketServer = require('websocket').server;
const WebSocketClient = require('websocket').client;
const WebSocketFrame = require('websocket').frame;
/*const WebSocketRouter = require('websocket').router;*/
const W3CWebSocket = require('websocket').w3cwebsocket;
const http = require('http');
const https = require('https');
const twilio = require('twilio');
const { Console } = require("console");
app.disable('view cache');
const pino = require('pino')
const SonicBoom = require('sonic-boom')
const logger = require('pino')()
const transport = pino.transport({
target: 'pino/file',
options: { destination: './logs/logs.txt', level: 'info', mkdir: true, append: true }
})
pino(transport)
/*const dbConfig2 = require("./app/config/db.config.js");*/
/*const config = require("./app/config/config.js");*/
const { client } = require("websocket");
const { err } = require("./node_modules/pino-std-serializers/index");
const { setInterval } = require("node:timers/promises");
app.use(pino)
//const webserver = app.listen(8080, function () {
// console.log('Node WEb Server is running..');
//});
var params = {
autoReconnect: false, //Enable/Disable reconnect when the server closes connection (boolean)
autoReconnectInterval: 1000, //Milliseconds to wait between reconnect attempts (number)
autoReconnectMaxRetries: 600, //Max number of reconnect attempts to allow (number)
requestTimeout: 30000, //Milliseconds to wait for a response before resending the request (number)
requestRetryInterval: 5000, //Milliseconds between request retry checks. This garbage collects the retry queue (number)
requestRetryQueueMaxLength: 10 //Max queue length of retry queue before old messages start getting dropped (number)
}
var wss = new WebSocket.Server({ port: 8081 })
sql.connect(config, function (err) {
if (err)
logger.error(err);
const sqlRequest = new sql.Request();
const sqlQuery = "SELECT TOP 5 guest_name,guest_phone_number,CONVERT(varchar,creation_time, 126) AS creation_time,CONVERT(varchar,last_modified_timestamp, 126) AS last_modified_timestamp,party_size from dbo.WaitList where status = '4' AND CAST(creation_time as date) = CAST( GETDATE() AS Date ) ORDER BY creation_time ASC";
logger.info("query passes preflight.....lets get data")
sqlRequest.query(sqlQuery, function (err, data) {
if (err) {
logger.error(err)
} else {
logger.info("We do preliminary query now.")
}
//console.table(data.recordset);
//logger.info('rows affected ' + data.rowsAffected);
//console.log(data.recordset[0]);
var array = [];
for (let i = 0; i < data.rowsAffected; i++) {
var a = data.recordset[i];
array.push(a);
}
wss.on('connection', ws => {
logger.info('Client connection established')
ws.on('message', function () {
sqlRequest.query(sqlQuery, function (err, data) {
if (err) {
logger.error(err)
}
//console.table(data.recordset);
//logger.info(data.rowsAffected);
//console.log(data.recordset[0]);
var array = [];
for (let i = 0; i < data.rowsAffected; i++) {
var a = data.recordset[i];
array.push(a);
}
})
wss.clients
.forEach(client => {
logger.info('sending data')
client.send(JSON.stringify(array))
})
})
})
})
});
const twilws = new W3CWebSocket('ws://172.16.0.101:8082', params);
twilws.onopen = () => {//when the page button is presses, it starts a websocket server on the client, if that makes sense
twilws.send('test')
if (twilws.readyState === WebSocket.OPEN) {
logger.info('Twilio Sockpuppet Connected')
} else {
logger.error('Twilio Sockpuppet Fail')
}
twilws.onmessage = async (e) => {//after i hit the page button the server is on and the array is sent, i am trying to get this to be waiting for data and when it arrives go, but it will only do that when i restart the script
try {
data = JSON.parse(e.data);//parse the data
logger.info('got the edata ' + e.data)
//main(data) this will fire but not when the button is pressed.
} catch (er) {
logger.error('socket parse error: ');
}
}
}
twilws.onclose = () => {//close the connection, purges that data so that the websocket can be recreated and an array with new data sent.
logger.info('Web Socket Connection Closed');
twilws.close(1000, 'all done');
};
//async function main(s) {
// /* these settings are loaded from configs/twilioConfig.js, go here to set the store account info and edit message body.*/
// const TWILIO_ACCOUNT_SID = twilioconfig.twilioOptions.TWILIO_ACCOUNT_SID;
// const TWILIO_AUTH_TOKEN = twilioconfig.twilioOptions.TWILIO_AUTH_TOKEN;
// const STORE_TWILIO_NUMBER = twilioconfig.twilioOptions.STORE_TWILIO_NUMBER;
// const TEXT_TWILIO_BODY = twilioconfig.twilioOptions.TEXT_TWILIO_BODY;
// var messarray = new Array([s])
// var num = JSON.stringify(messarray[0][0][0])
// var state = JSON.stringify(messarray[0][0][1])
// logger.info(num + ', ' + state)
// var readyin = state
// var custnum = num
// /*logger.info(TWILIO_ACCOUNT_SID + ' ' + TWILIO_AUTH_TOKEN)*/
// var customer = new twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);//these are our twilio account sid and token, set in twilioConfig, get from oneNote
// if (readyin == 0) {
// waitmsg = TEXT_TWILIO_BODY + "Your Table is Ready!";
// } else if (readyin == 15) {
// waitmsg = TEXT_TWILIO_BODY + "Your Table will be ready in about 15 minutes!";
// }
// logger.info(`Recieved Form data.....Twilio data read successfully....`);//log that we got the data from the gui form
// // send the message to the customer number through twilio, custnum from form, from is the store number, alson in twilioConfig
// customer.messages.create({
// to: custnum,
// from: STORE_TWILIO_NUMBER,
// body: waitmsg
// })
// .then(message => {
// var messageid = message.sid
// logger.info(messageid + ' Hey i found this, we might need it in a sec')
// const msid = JSON.stringify({ "MessageSid": messageid })
// const twilid = messageid
// setTimeout(getStatus, 5000);
// function getStatus() {
// customer.messages(messageid).fetch()
// .then(call => {
// const d = new Date(call.dateCreated).toLocaleString();
// const messageStatus = call.status
// var twil_response_array = [twilid, messageStatus, d]
// logger.info(twil_response_array)
// wss.on('connection', ws => {
// ws.on('message', function () {
// })
// wss.clients
// .forEach(client => {
// client.send(twil_response_array)
// })
// })
// })
// }
// //})
app.listen(8180, function () {
logger.info('Server is running..');
});
});

Unable to use mongoose model

I am coding a simple registration form using mongoose.
I have use a javascript file to process the values of the registration form.
Here is my registrationButtonAction.js
window.onload = function() {
var User = require('/models/Mongoose Database/user_database');
// this line is causing the problem
var registerButton = document.getElementById("registerMe");
var firstName = document.getElementById("firstName");
var lastName = document.getElementById("lastName");
var usernameRegister = document.getElementById("usernameRegister");
var passwordRegister = document.getElementById("passwordRegister");
var repasswordRegister = document.getElementById("repasswordRegister");
registerButton.onclick = function () {
if(!firstName.value || !passwordRegister.value || !repasswordRegister.value || !usernameRegister.value){
alert("Enter all required fields");
}else if (passwordRegister.value != repasswordRegister.value){
alert("Passwords must match");
}else {
var newUser = new User({
username : usernameRegister.value,
password : passwordRegister.value
});
User.find({username:usernameRegister.value}, function (error, user) {
if (error) throw error;
if(user){
window.location("/register");
}else {
newUser.save(function (error) {
if(error) throw error;
});
window.location("/login");
}
// user.comparePassword(passwordRegister.value, function (error, isMatch) {
// if (error) throw error;
//
// return 1;
//})
});
}
}
}
When I comment the var User = require('/models/Mongoose Database/user_database');, all the checks are working fine inside the onclick function. But when I uncomment it, it is not recognizing the button click.
I want to know whether this is a correct way of taking values from the registration page and storing them in a mongoose database.
You are mixing server and client code. Mongoose models and Node.js functions are not accessible inside window.onload on your client.
To put it simply, you need to create a REST API to perform database operations on the server. You have all the right tools aready, just need to reorder them.
The flow would be as such :
get the values entered in the browser
call an endpoint on your server (for example /api/createUser)
in the express router, have a route called /api/createUser in which you can access your User model and perform creation/deletion/update, etc.
My suggestion would be for you to go through this tutorial which should remove your confusion and bring you up to speed in no time. Good Luck!
Also, Passport can help you with authentication, but I believe you should first learn how to build a basic API. Authentication is a tricky beast ;)

Parse sessionToken revoked on user password update

My mobile hybrid app uses an expressJS server as the backend to proxy requests to parse.com via the REST API. I also use express for my own user authentication with an SSO provider. I've followed this tutorial and modified the approach a bit to work for my own setup (not using CloudCode, also not authenticating with GitHub). I'm also using the newish Revokable Sessions that came about earlier this year (March 2015?) Essentially the tutorial and my auth approach can be boiled down to doing the following on a remote backend (ExpressJS / CloudCode):
Login As User to Obtain Session Token
Create a new user if username doesn't already exist, and then continue
Create a new random password for the user (update the users password with masterKey)
With new password, log in as the user to generate the Parse sessionToken
Pass back the sessionToken to the client app
This all works fine, it seems to be the 'approach' for logging in with third-party authentication providers.
Problem is that each time my user logs in, the sessionToken essentially is re-created (as in, it destroys the old and creates a new token). I can see this in the dataBrowser as the session objectId is different. Also, and the main problem is that my user may have logged in on other devices, or on the web app, and essentially each time a user switches devices, the session token is invalidated on the other devices. Returning to that device then requires another login.
The new enhance session blog post mentions that the new revokable sessions provides for 'unique sessions' per device, however from my experience this doesn't seem to be working for users logging in over the REST API from my express backend. Perhaps this unique session would only work if the app itself was communicating to parse and thus would be able to pass along an installationId to distinguish the different devices.
I've posted my authentication code below, the parse object is from this npm parse library
upsertUser function:
/**
* This function checks to see if this user has logged in before.
* If the user is found, update their password in order to 'become' them and then return
* the user's Parse session token. If not found, createNewUser
*/
exports.upsertUser = function(ssoData, deferred) {
var userCreated = (typeof deferred != "undefined") ? true : false;
var deferred = deferred || q.defer();
var query = {
where: {username: ssoData.uid.trim()}
};
// set masterKey
parse.masterKey = parseConfig.MASTER_KEY;
// find existing user by username
parse.getUsers( query, function (err, res, body, success) {
if ( body.length ) {
var userId = body[0].objectId;
var username = body[0].username;
var password = new Buffer(24);
_.times(24, function (i) {
password.set(i, _.random(0, 255));
});
password = password.toString('base64');
parse.updateUser(userId, {password: password}, function (err, res, body, success) {
if ( typeof body.updatedAt != 'undefined' ) {
console.log('user update at: ', body.updatedAt);
parse.loginUser(username, password, function (err, res, body, success) {
deferred.resolve( body.sessionToken );
});
}
});
} else if ( userCreated === false ) {
console.log('object not found, create new user');
self.createNewUser(ssoData, deferred);
} else {
deferred.resolve();
}
});
return deferred.promise;
}
createNewUser function:
/**
* This function creates a Parse User with a random login and password, and
* Once completed, this will return upsertUser.
*/
exports.createNewUser = function(ssoData, deferred) {
// Generate a random username and password.
var password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
});
var newUser = {
username: ssoData.uid,
password: password.toString('base64'),
};
// Sign up the new User
parse.createUser(newUser, function(err, res, body, success) {
if (err) {
console.log('new parse user err', err)
}
if (typeof body.sessionToken != "undefined") {
self.upsertUser(ssoData, deferred);
} else {
deferred.resolve();
}
});
}
Any ideas how I can avoid invalidating sessionTokens upon subsequent logins?
Shoot, it seems there's a toggle on the settings page that I missed:
Revoke existing session tokens when user changes password
Simple as that i guess ;-) Still, I don't think I'll get unique sessions across devices.

Dynamic database connection to mongodb or mongoose from nodejs

I am trying to create a multi-tenant app (saas), where each client has its own database.
My situation is:
I created a middleware that would determine who the client is based on a subdomain, then retrieve the client's database connection info from a general database. I don't know how to establish a connection object for this client so as to be able to use in my controllers. And should I do this in the middleware or in a controller? And if it's in the model, how do i pass the connection string and parameters (I could use session, but I don't know how to access session from within model).
How do i do the following?
Organisation: where do I create the db connection for client dynamically?
Inject/pass connection parameters to controller or model (where connection definition is made)
After dynamic connection has been made, how do i access it globally for that client?
This is an example of my middleware, and i would like to create a mongoose connection which i would like to make dynamic (pass in client's connection info):
function clientlistener() {
return function (req, res, next) {
console.dir('look at my sub domain ' + req.subdomains[0]);
// console.log(req.session.Client.name);
if (req.session.Client && req.session.Client.name === req.subdomains[0]) {
var options = session.Client.options;
var url = session.Client.url
var conn = mongoose.createConnection(url, options);
next();
}
}
}
How do I access this connection object from inside the controller? Or from the model?
Thank you.
This is to help others who may find themselves in similar situation as I did. I hope that it could be standardized. I dont think we should have to reinvent the wheel every time someone needs to make a multi-tenant application.
This example describes a multi-tenant structure with each client having its own database.
Like i said there might be a better way of doing this, but because i didn't get help myself, this was my solution.
So here are the goals this solution targets:
each client is identified by subdomain e.g client1.application.com,
application checks if subdomain is valid,
application looks up and obtains connection information (database url, credentials, etc) from master database,
application connects to client database ( pretty much hands over to client),
application takes measures to ensure integrity and resource management (e.g use the same database connection for members of the same client, rather than make new connection).
Here is the code
in your app.js file
app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients
I've created two middlewares :
clientListener - to identify the client connecting,
setclientdb - gets client details from Master database, after client is identified, and then establishes connection to client database.
clientListener middleware
I check who the client is by checking the subdomain from the request object. I do a bunch of checks to be sure the client is valid (I know the code is messy, and can be made cleaner). After ensuring the client is valid, I store the clients info in session. I also check that if the clients info is already stored in session, there is no need to query the database again. We just need to make sure that the request subdomain, matches that which is already stored in session.
var Clients = require('../models/clients');
var basedomain = dbConfig.baseDomain;
var allowedSubs = {'admin':true, 'www':true };
allowedSubs[basedomain] = true;
function clientlistener() {
return function(req, res, next) {
//console.dir('look at my sub domain ' + req.subdomains[0]);
// console.log(req.session.Client.name);
if( req.subdomains[0] in allowedSubs || typeof req.subdomains[0] === 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0] ){
//console.dir('look at the sub domain ' + req.subdomains[0]);
//console.dir('testing Session ' + req.session.Client);
console.log('did not search database for '+ req.subdomains[0]);
//console.log(JSON.stringify(req.session.Client, null, 4));
next();
}
else{
Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
if(!err){
if(!client){
//res.send(client);
res.send(403, 'Sorry! you cant see that.');
}
else{
console.log('searched database for '+ req.subdomains[0]);
//console.log(JSON.stringify(client, null, 4));
//console.log(client);
// req.session.tester = "moyo cow";
req.session.Client = client;
return next();
}
}
else{
console.log(err);
return next(err)
}
});
}
}
}
module.exports = clientlistener;
setclientdb middleware:
I check everything again making sure that the client is valid. Then the connection to the client's database with the info retrieved from session is opened.
I also make sure to store all active connections into a global object, so as to prevent new connections to the database upon each request(we don't want to overload each clients mongodb server with connections).
var mongoose = require('mongoose');
//var dynamicConnection = require('../models/dynamicMongoose');
function setclientdb() {
return function(req, res, next){
//check if client has an existing db connection /*** Check if client db is connected and pooled *****/
if(/*typeof global.App.clientdbconn === 'undefined' && */ typeof(req.session.Client) !== 'undefined' && global.App.clients[req.session.Client.name] !== req.subdomains[0])
{
//check if client session, matches current client if it matches, establish new connection for client
if(req.session.Client && req.session.Client.name === req.subdomains[0] )
{
console.log('setting db for client ' + req.subdomains[0]+ ' and '+ req.session.Client.dbUrl);
client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);
client.on('connected', function () {
console.log('Mongoose default connection open to ' + req.session.Client.name);
});
// When the connection is disconnected
client.on('disconnected', function () {
console.log('Mongoose '+ req.session.Client.name +' connection disconnected');
});
// If the Node process ends, close the Mongoose connection
process.on('SIGINT', function() {
client.close(function () {
console.log(req.session.Client.name +' connection disconnected through app termination');
process.exit(0);
});
});
//If pool has not been created, create it and Add new connection to the pool and set it as active connection
if(typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined')
{
clientname = req.session.Client.name;
global.App.clients[clientname] = req.session.Client.name;// Store name of client in the global clients array
activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array
console.log('I am now in the list of active clients ' + global.App.clients[clientname]);
}
global.App.activdb = activedb;
console.log('client connection established, and saved ' + req.session.Client.name);
next();
}
//if current client, does not match session client, then do not establish connection
else
{
delete req.session.Client;
client = false;
next();
}
}
else
{
if(typeof(req.session.Client) === 'undefined')
{
next();
}
//if client already has a connection make it active
else{
global.App.activdb = global.App.clientdbconn[req.session.Client.name];
console.log('did not make new connection for ' + req.session.Client.name);
return next();
}
}
}
}
module.exports = setclientdb;
Last but not the least
Since I am using a combination of mongoose and native mongo, We have to compile our models at run time. Please see below
Add this to your app.js
// require your models directory
var models = require('./models');
// Create models using mongoose connection for use in controllers
app.use(function db(req, res, next) {
req.db = {
User: global.App.activdb.model('User', models.agency_user, 'users')
//Post: global.App.activdb.model('Post', models.Post, 'posts')
};
return next();
});
Explanation:
Like I said earlier on I created a global object to store the active database connection object: global.App.activdb
Then I use this connection object to create (compile) mongoose model, after i store it in the db property of the req object: req.db. I do this so that i can access my models in my controller like this for example.
Example of my Users controller:
exports.list = function (req, res) {
req.db.User.find(function (err, users) {
res.send("respond with a resource" + users + 'and connections ' + JSON.stringify(global.App.clients, null, 4));
console.log('Worker ' + cluster.worker.id + ' running!');
});
};
I will come back and clean this up eventually. If anyone wants to help me, that be nice.
Hello everyone, here is a much more updated solution.
So here are the goals this solution targets:
each client is identified by subdomain e.g client1.application.com,
application checks if subdomain is valid,
application looks up and obtains connection information (database url, credentials, etc) from master database,
application connects to client database ( pretty much hands over to client),
application takes measures to ensure integrity and resource management (e.g use the same database connection for members of the same client, rather than make new connection).
updates
use of promises,
automatic import & compilation of models
New middleware ; modelsinit (used to automatically import and compile mongoose models)
Clean up of middlewares (setclientdb, clientlistener, modelsInit)
Please see below for some Explanations
**
modelsInit Middleware
**
features
tests if models are already compiled. If so, skip.
tests to see if request is not a tenant request; i.e (request to apps homepage, admin page, etc)
'use strict';
/**
* Created by moyofalaye on 3/17/14.
*/
var path = require('path');
var config = require('../../config/config');
// Globbing model files
config.getGlobbedFiles('./app/models/*.js').forEach(function (modelPath) {
require(path.resolve(modelPath));
});
function modelsInit() {
return function (req, res, next) {
//console.log(req.subdomains[0]);
switch (req.subdomains[0]) {
case 'www':
case undefined:
return next();
break;
case 'admin':
return next();
break;
// default:
// return
}
var clientname = req.session.Client.name;
// test if models are not already compiled if so, skip
if (/*typeof req.db === 'undefined' && */ typeof global.App.clientModel[clientname] === 'undefined') {
req.db = {};
//Get files from models directory
config.getGlobbedFiles('./app/models/clientmodels/**/*.js').forEach(function (modelPath) {
console.log('the filepath is ' + modelPath);
//Deduce/ extrapulate model names from the file names
//Im not very good with regxp but this is what i had to do, to get the names from the filename e.g users.server.models.js (this is my naming convention, so as not to get confused with server side models and client side models
var filename = modelPath.replace(/^.*[\\\/]/, '');
var fullname = filename.substr(0, filename.lastIndexOf('.'));
var endname = fullname.indexOf('.');
var name = fullname.substr(0, endname);
req.db[name] = require(path.resolve(modelPath))(global.App.activdb);
console.log('the filename is ' + name);
});
global.App.clientModel[clientname] = req.db;
console.log(global.App.clients);
return next();
}
// since models exist, pass it to request.db for easy consumption in controllers
req.db = global.App.clientModel[clientname];
return next();
};
}
module.exports = modelsInit;
Todo: Further Explanation
ClientListener.js
var config = require('../../config/config');
var Clients = require('../models/clients');
var basedomain = config.baseDomain;
var allowedSubs = {'admin': true, 'www': true};
allowedSubs[basedomain] = true;
//console.dir(allowedSubs);
function clientlistener() {
return function (req, res, next) {
//check if client has already been recognized
if (req.subdomains[0] in allowedSubs || typeof req.subdomains[0] == 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0]) {
console.log('did not search database for ' + req.subdomains[0]);
//console.log(JSON.stringify(req.session.Client, null, 4));
return next();
}
//look for client in database
else {
Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
if (!err) {
//if client not found
if (!client) {
//res.send(client);
res.status(403).send('Sorry! you cant see that.');
console.log(client);
}
// client found, create session and add client
else {
console.log('searched database for ' + req.subdomains[0]);
req.session.Client = client;
return next();
}
}
else {
console.log(err);
return next(err)
}
});
}
}
}
module.exports = clientlistener;
setclientdb.js
var client;
var clientname;
var activedb;
var Promise = require("bluebird");
Promise.promisifyAll(require("mongoose"));
//mongoose = require('mongoose');
function setclientdb() {
return function (req, res, next) {
//check if client is not valid
if (typeof(req.session.Client) === 'undefined' || req.session.Client && req.session.Client.name !== req.subdomains[0]) {
delete req.session.Client;
client = false;
return next();
}
//if client already has an existing connection make it active
else if (global.App.clients.indexOf(req.session.Client.name) > -1) {
global.App.activdb = global.App.clientdbconn[req.session.Client.name]; //global.App.clientdbconnection is an array of or established connections
console.log('did not make new connection for ' + req.session.Client.name);
return next();
}
//make new db connection
else {
console.log('setting db for client ' + req.subdomains[0] + ' and ' + req.session.Client.dbUrl);
client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);
client.on('connected', function () {
console.log('Mongoose default connection open to ' + req.session.Client.name);
//If pool has not been created, create it and Add new connection to the pool and set it as active connection
if (typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined') {
clientname = req.session.Client.name;
global.App.clients.push(req.session.Client.name);// Store name of client in the global clients array
activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array and set it as the current active database
console.log('I am now in the list of active clients ' + global.App.clients[clientname]);
global.App.activdb = activedb;
console.log('client connection established, and saved ' + req.session.Client.name);
return next();
}
});
// When the connection is disconnected
client.on('disconnected', function () {
console.log('Mongoose ' + req.session.Client.name + ' connection disconnected');
});
// If the Node process ends, close the Mongoose connection
process.on('SIGINT', function () {
client.close(function () {
console.log(req.session.Client.name + ' connection disconnected through app termination');
process.exit(0);
});
});
}
}
}
module.exports = setclientdb;
Further Explanations Coming

Categories

Resources