Dette er for at hjælpe andre, der kan komme i lignende situation som jeg gjorde. Jeg håber, at det kunne standardiseres. Jeg tror ikke, vi skal genopfinde hjulet, hver gang nogen skal lave en ansøgning med flere lejere.
Dette eksempel beskriver en struktur med flere lejere, hvor hver klient har sin egen database. Som jeg sagde, kunne der være en bedre måde at gøre dette på, men fordi jeg ikke selv fik hjælp, var dette min løsning.
Så her er målene med denne løsning:
- hver klient identificeres af underdomæne, f.eks. client1.application.com,
- applikation kontrollerer, om underdomænet er gyldigt,
- applikation slår op og henter forbindelsesoplysninger (database-url, legitimationsoplysninger osv.) fra masterdatabasen,
- applikationen opretter forbindelse til klientdatabasen (stort set overdrages til klienten),
- applikationen træffer foranstaltninger for at sikre integritet og ressourcestyring (brug f.eks. den samme databaseforbindelse for medlemmer af den samme klient i stedet for at oprette ny forbindelse).
Her er koden
i din app.js
fil
app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients
Jeg har oprettet to middlewares :
clientListener
- for at identificere klienten, der forbinder,setclientdb
- henter klientoplysninger fra masterdatabasen, efter at klienten er identificeret, og etablerer derefter forbindelse til klientdatabasen.
clientListener-middleware
Jeg tjekker hvem klienten er ved at tjekke underdomænet fra anmodningsobjektet. Jeg foretager en masse kontroller for at være sikker på, at klienten er gyldig (jeg ved, at koden er rodet og kan gøres renere). Efter at have sikret, at klienten er gyldig, gemmer jeg klientoplysningerne i session. Jeg tjekker også, at hvis klientoplysningerne allerede er gemt i sessionen, er der ingen grund til at forespørge databasen igen. Vi skal bare sikre os, at anmodningens underdomæne matcher det, der allerede er gemt i sessionen.
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:
Jeg tjekker alt igen og er sikker på, at klienten er gyldig. Derefter åbnes forbindelsen til klientens database med de oplysninger, der er hentet fra session.
Jeg sørger også for at gemme alle aktive forbindelser i et globalt objekt, for at forhindre nye forbindelser til databasen ved hver anmodning (vi ønsker ikke at overbelaste hver klients mongodb-server med forbindelser).
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;
Sidst men ikke mindst
Da jeg bruger en kombination af mongoose og native mongo, er vi nødt til at kompilere vores modeller under kørslen. Se venligst nedenfor
Føj dette til din 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();
});
Forklaring:
Som jeg sagde tidligere oprettede jeg et globalt objekt til at gemme det aktive databaseforbindelsesobjekt:global.App.activdb
Så bruger jeg dette forbindelsesobjekt til at oprette (kompilere) mongoose-model, efter at jeg har gemt det i db-egenskaben for req-objektet:req.db
. Jeg gør dette for at få adgang til mine modeller i min controller som f.eks. på denne måde.
Eksempel på min brugercontroller:
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!');
});
};
Jeg vil vende tilbage og rydde op i det her til sidst. Hvis nogen vil hjælpe mig, er det rart.