Това е, за да помогна на други, които може да се окажат в подобна ситуация като мен. Надявам се, че може да се стандартизира. Не мисля, че трябва да преоткриваме колелото всеки път, когато някой трябва да направи приложение за няколко наематели.
Този пример описва структура с множество наематели, като всеки клиент има своя собствена база данни. Както казах, може да има по-добър начин да се направи това, но тъй като аз не получих помощ, това беше моето решение.
Ето целите, към които е насочено това решение:
- всеки клиент се идентифицира от поддомейн, напр. client1.application.com,
- приложението проверява дали поддомейнът е валиден,
- приложението търси и получава информация за връзката (URL адрес на базата данни, идентификационни данни и т.н.) от основната база данни,
- приложението се свързва с клиентска база данни (почти се предава на клиента),
- приложението взема мерки за гарантиране на целостта и управлението на ресурсите (напр. използване на същата връзка към базата данни за членове на същия клиент, вместо да прави нова връзка).
Ето кода
във вашия app.js
файл
app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients
Създадох два междинни софтуера:
clientListener
- за идентифициране на свързващия се клиент,setclientdb
- получава подробности за клиента от основната база данни, след като клиентът бъде идентифициран, и след това установява връзка с клиентската база данни.
мидълуер на clientListener
Проверявам кой е клиентът, като проверявам поддомейна от обекта на заявката. Правя куп проверки, за да съм сигурен, че клиентът е валиден (знам, че кодът е разхвърлян и може да се направи по-чист). След като се уверя, че клиентът е валиден, съхранявам информацията за клиентите в сесия. Проверявам също, че ако информацията за клиентите вече е съхранена в сесия, няма нужда да се прави заявка към базата данни отново. Просто трябва да се уверим, че поддомейнът на заявката съответства на този, който вече е съхранен в сесията.
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 междинен софтуер:
Проверявам всичко отново, като се уверя, че клиентът е валиден. След това се отваря връзката към клиентската база данни с информацията, извлечена от сесията.
Също така се уверявам, че съхранявам всички активни връзки в глобален обект, за да предотвратя нови връзки към базата данни при всяка заявка (не искаме да претоварваме всеки клиент mongodb сървър с връзки).
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;
Последно, но не на последно място
Тъй като използвам комбинация от мангуста и местно монго, трябва да компилираме нашите модели по време на изпълнение. Моля, вижте по-долу
Добавете това към вашия 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();
});
Обяснение:
Както казах по-рано, създадох глобален обект за съхраняване на активния обект за връзка с базата данни:global.App.activdb
След това използвам този обект за връзка, за да създам (компилирам) модел на мангуста, след като го запазя в свойството db на обекта req:req.db
. Правя това, за да мога да получа достъп до моите модели в моя контролер като този например.
Пример за моя потребителски контролер:
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!');
});
};
В крайна сметка ще се върна и ще почистя това. Ако някой иска да ми помогне, да е мило.