Hapi.js Subdomain Routing to Plugins - javascript

I am looking for a way to route different subdomains to different plugins. I looked through the API docs, and didn't find anything helpful.

I ended up making a simple class to create plugins that only work on certain subdomains. Here it is.
var Plugin = function(attributes, routes) {
// Add our routes to the server
this.register = function(plugin, options, next) {
// Loop through the selected servers and add the routes
plugin.servers.forEach(function(server) {
// Loop through the routes and add the vhost option
routes.map(function(route) {
route.vhost = attributes.vhosts.map(function(vhost) {
return vhost + "." + server.info.host;
});
});
// Add the routes
server.route(routes);
});
next();
};
// Add our attributes
this.register.attributes = attributes;
};
Then you can make a new plugin and specify the subdomains easily. Example:
var plugin = new Plugin([
// Your route or routes here
], {
vhosts: ["array", "of", "subdomains"]
});

Related

How to remove limit parameter from URL in express-paginate?

I am trying to add paging using express-paginate module. But i am getting limit parameter in url like this: http://example.com:3010/feeds?page=2&limit=10.
But i don't want to use limit in url. How i can remove limit from url?
Below is my pug file code.
if paginate.hasPreviousPages || paginate.hasNextPages(pageCount)
.navigation.well-sm#pagination
ul.pager
if paginate.hasPreviousPages
li.previous
a(href=paginate.href(true)).prev
i.fa.fa-arrow-circle-left
| Previous
if pages
each page in pages
a.btn.btn-default(href=page.url)= page.number
if paginate.hasNextPages(pageCount)
li.next
a(href=paginate.href()).next
| Next
i.fa.fa-arrow-circle-right`
I think you can add this by creating a simple middleware function, e.g.
const app = express();
const DEFAULT_PAGE_COUNT = 10;
// Intercept all calls and add a default page count.
app.use(function(req, res, next) {
if (!("limit" in req.query)) {
req.query.limit = DEFAULT_PAGE_COUNT;
}
next();
});
And actually, I believe the module supplies this middleware function, e.g.
// keep this before all routes that will use pagination
// paginate.middleware(limit, maxLimit)
const paginate = require('express-paginate');
app.use(paginate.middleware(10, 50));

Extend node application with new actions

I've created application which is handle some basic actions like save(e.g. saving a file in file system) edit etc ,now I want that some users will have the ability to extend this functionality with new actions,for example user
will clone the application and add additional file with new actions (and some callback) and then register on some event
I should this new actions under the hood,my question is how can I take this new actions from new file and run then in my process,simple example will be very helpful
UPDATE
lets assume that this is my file that handle the action and user want to add additional action like delete
var fs = require('fs');
module.exports = {
fileAction: function (req, res, filePath) {
var urlAction = urlPath.substr(urlPath.lastIndexOf("/") + 1);
if (urlAction === 'save') {
this.save(req,res,filePath);
} else
this.delete(req,res,filePath);
},
save: function (req,res,filePath) {
var writeStream = fs.createWriteStream(filePath, {flags: 'w'});
req.pipe(writeStream);
res.writeHead(200, { 'Content-Type': 'text/plain' });
},
delete: function (req,res,filePath) {
},
}
and the delete code should be something like this
filePath = 'C://test.txt';
fs.unlinkSync(filePath);
Now as lordvlad suggest user should have a new file with his specific implementation which should be used by lordvlad suggestion design flow,my question is how to add this delete functionality(which is very simple) and make it work,like some POC for this.
I could imagine some plugin system like so:
main.js
// some setup
var EE = require('events').EventEmitter;
var glob = require('glob');
var eventBus = new EE();
// find plugins
glob("plugins/*.js", function(err, files) {
if (err) {
// some error handling here
}
files.forEach(function(file) {
var plugin = require(file);
plugin(eventBus);
});
});
plugins/my-plugin.js
module.exports = function(eventBus) {
// do something interesting
// listen for events on the event bus
eventBus.on("foo", function(e){
// do something
});
eventEmitter.emit("pluginReady", "my-plugin");
};
Of course you could substitute the event emitter with some global object, or make the plugin handle callbacks by passing a callback instead of an event bus. I think the key aspect is to load plugins (done within the glob ... require block) and and to make them fit into your system (which you will need to figure out yourself or provide some code samples of what you already have so somebody can give you another hint).
UPDATE after OPs update
main.js
var glob = require('glob');
var xtend = require('xtend');
module.exports = {
save: function(..){..},
load: function(..){..},
}
// typeof module.exports.delete === 'undefined',
// so you cannot call module.exports delete or similar
glob("plugins/*.js", function(err, files) {
files.forEach(function(file){
var plugin = require(file);
// call plugin initializer if available
if (typeof plugin.init === "function")
plugin.init();
xtend(module.exports, plugin);
});
// here all plugins are loaded and can be used
// i.e. you can do the following
module.exports.delete(...);
});
plugins/my-plugin.js
module.exports = {
delete: function(..){..},
init: function() {
// do something when the plugin loads
}
}
Mind though, that any plugin could overwrite another plugin's methods.

RequireJS: how to add a prefix to path

I'm developing a SPA using Knockout.js V3 and RequireJS.
I have ko components written like this:
define(['text!settings.html'],
function( htmlString) {
'use strict';
function SettingsViewModel(params) {
...
}
// Return component definition
return {
viewModel: SettingsViewModel,
template: htmlString
};
});
Now i want to support localization and for that I have duplicated html for each supported language, so for example:
en/settings.html
de/settings.html
se/settings.html
I would like to let the user change a language and refresh the app with the new language, is it possible to instruct require text plugin to add the language prefix to all html, so when i write:
text!settings.html
it will actually load:
text!de/settings.html
Not sure if you can let the text plugin prefix the urls. What you might be able to do is create a custom template loader:
var templateFromLanguageUrlLoader = {
loadTemplate: function(name, templateConfig, callback) {
if (templateConfig.languageUrl) {
// Language from config or default language
var lang = templateConfig.lang || 'de';
var fullUrl = lang + '/' + templateConfig.languageUrl;
$.get(fullUrl, function(markupString) {
ko.components.defaultLoader.loadTemplate(name, markupString, callback);
});
} else {
// Unrecognized config format. Let another loader handle it.
callback(null);
}
}
};
// Register it
ko.components.loaders.unshift(templateFromLanguageUrlLoader );
Then your component would look something like this:
define([],
function() {
'use strict';
function SettingsViewModel(params) {
...
}
// Return component definition
return {
viewModel: SettingsViewModel,
template: {
languageUrl: 'settings.html',
language: 'nl' // overwrite default
}
};
});
Eventually i went with a different solution, i just added a variable to the path:
define(['text!' + globals.bundlePath + 'settings.html']
This variable is initialized from session storage (and get's a default if nothing found) and so when a user changes language, i keep it in session storage and refresh the app, and that way the pages are now loaded with the new language.

how to avoid 'this' issue in function used as express route callback

I am trying to write a module that creates generic handlers for express routes
e.g.
//create a new route handler with some config
//every routeHanlder method needs to be able to access this config
var handler = new routeHandler({config: "value"});
//handle a get route ("Example 1")
app.get('route', handler.read)
//handle a get route with params ("Example 2")
app.get('route.:id', function(req, res){
handler.read(req,res,{query: {_id: req.params.id}});
});
I am having trouble making "example 1" work...
app.get('route', handler.read)
...as I loose the value of 'this' inside handler.read
I understand why the value of 'this' is different, but I can't figure out how to make it work, or another way to get the desired results without using 'this'.
Here is a plunker link
To summarise I am trying to find a way to make my routeHandler objects (see plunker above, and code paste below) work when used as the callback of an express route (see "example 1" above).
var routeHandler = function(config){
if (!(this instanceof(routeHandler))) {
return new routeHandler(config);
}
config = config || {};
if(config.configData){
this.configData = config.configData;
}
};
routeHandler.prototype = {
read: function(req, res, options){
//The problem: accessing configData without using this
console.log("inside callback", this, this.configData);
options = options || {};
}
};
Edit: I would like the ability to create multiple instances of the route handler with different config data e.g.
var handlerOne = new RouteHandler("configDataOne");
var handlerTwo = new RouteHandler("configDataTwo");
app.get('/firstRoute', handlerOne.read);
app.get('/secondRoute', handlerTwo.read);
You can save routeHandler's configData in express object "app" like below:
app.set("routeHandlerConfigData", "identifier or whatever value you want to store");
then make your routeHandler a simple middleware
var routeHandler = function(req, res, next){
var configData = req.app.get("routeHandlerConfigData");
//Do whatever you want
};
I was inspired by a great comment form yerforkferchips who suggested adding my routerHandler functions inside the constructor like this
this.read = (function read(...) { ... }).bind(this);
Which lets me do exactly what I wanted in my question
app.get('route', handler.read);
BUT i realised that I could use closures in my prototype functions which would sort my 'this' problem and that I would also be able to take in configuration data without having to wrap handler.read in a separate callback function on app.get
RouteHandler.prototype = {
read: function(config){
return function(req, res){
//I have access to req, res and config
}
}
}
so now I can do this
app.get('route', handler.read("configData"));
instead of this
app.get('route', function(req, res){
hander.read(req, res, "configData");
});

Partial update while supporting seo

Using NodeJs I'm trying to do something quite similar to Meteor: I want to send only the parts of a page that actually changed. My dilemma is that I know how to create such a framework to respond to link clicks and send partial updates but such a framework won't cater to direct browsing to a page other than the index (which is what is required for search engines and people without javascript to use your site).
I can also figure out how to make a framework to support entire page reloads, handlebars and a simple node server instance would take care of that. Hoeever, I can't figure out how to create a way that would allow me to write one method to tell the framework the partial updates for a page and let the framework figure out what else needs to be loaded.
A way I can think of would be to create the index page every time (for entire page loads) and apply partial updates to that but that can quickly become expensive if a subpage differs a lot from a very crowded index.
An example method would look something like this:
function images(id) {
if (id == null) {
// load all images from database
template.images = db.result();
template.content = template.loadblock('gallery');
}
else {
// retrieve single image
template.content = template.loadblock('single_image');
template.image = db.result();
}
}
On a partisl updste calling this method for domain.com/images would work just fine because it's clear what had changed.
For an entire page load this function would miss things like a header, footer ,navigation, etc.
In an answer I would look for an example where this has been done or some tips that Can point me in the right direction. I'm sorry for any typoes I wrote this post on an ipad. If you have any questions about my question just ask and I'll update as needed.
Update:
A possible example of a solution might be the following code. It's to give an idea, it probably won't actually run
// As a convention, don't pass around raw values if they aren't static but pass around functions such as
data.images = function () {
// run db query
// return an object with the images
}
// This constraint might be limited to the index() method
var routes = {
// This now allows us to do things like this:
index: function() {
var data;
// Initialise everything needed for the index
data.title = 'Index';
data.nav = { Index: '/', Images: '/images' };
data.content = 'Hello World';
},
categories: function() {
var data;
data.content = render('gallery', function () { /* load and return images as object */ }); // Not sure about this dynamic subtemplating but oh well
}
// This now allows us to do the following:
function request(page, type) {
if (type == 'update') {
if (routes[page] != undefined && typeof routes[page] == 'function') {
respond(routes[page]());
}
}
else {
if (routes[page] != undefined && typeof routes[page] == 'function') {
var data = mergeArrays(routes['index'](), routes[page]());
// index.html which is just a Handlebars template
respond(data);
}
}
}
Here is a pattern I often use (in Express apps):
function respond(req, res, name, resource) {
if(req.accepts('json')) {
// Send JSON to clients who want it
res.send(resource);
} else {
// Render with layout only for non-XHR requests
resource.layout = !req.xhr;
res.render('resources/' + name, resource);
}
}
Example usage:
app.get('/images', function(req, res, next) {
getImages(function(err, images) {
if(err) return next(err);
respond(req, res, 'images', images);
});
});
app.get('/images/:id', function(req, res, next) {
getImage(req.params.id, function(err, image) {
if(err) return next(err);
respond(req, res, 'image', image);
});
});
image.jade:
img(src=uri, alt=title)
images.jade:
#gallery
for image in images
include image
Clients who ask for JSON get that, otherwise they get the full page only if it's a non-XHR request. XHR requests get just the HTML snippet for the requested resource. This works well for quite simple apps, where resources mostly correspond to pages.

Categories

Resources