2011-09-05 07:00:41 +08:00
|
|
|
var _ = require('underscore')
|
|
|
|
, Step = require('step')
|
2012-09-25 00:57:48 +08:00
|
|
|
, cartoData = require('./carto_data')
|
2012-09-24 23:57:39 +08:00
|
|
|
, Cache = require('./cache_validator')
|
|
|
|
, mapnik = require('mapnik')
|
|
|
|
;
|
2011-09-05 07:00:41 +08:00
|
|
|
|
|
|
|
module.exports = function(){
|
|
|
|
var me = {
|
|
|
|
base_url: '/tiles/:table',
|
2013-02-13 01:53:16 +08:00
|
|
|
base_url_notable: '/tiles',
|
2012-09-20 00:52:13 +08:00
|
|
|
grainstore: {
|
|
|
|
datasource: global.environment.postgres,
|
2012-09-24 23:57:39 +08:00
|
|
|
cachedir: global.environment.millstone.cache_basedir,
|
|
|
|
mapnik_version: global.environment.mapnik_version || mapnik.versions.mapnik
|
2012-09-20 00:52:13 +08:00
|
|
|
},
|
2011-09-20 09:27:23 +08:00
|
|
|
redis: global.environment.redis,
|
2011-10-13 21:22:54 +08:00
|
|
|
enable_cors: global.environment.enable_cors,
|
2012-05-02 02:00:14 +08:00
|
|
|
varnish_host: global.environment.varnish.host,
|
|
|
|
varnish_port: global.environment.varnish.port,
|
2012-05-03 02:32:54 +08:00
|
|
|
cache_enabled: global.environment.cache_enabled,
|
2012-07-21 01:02:36 +08:00
|
|
|
log_format: global.environment.log_format
|
2012-05-03 02:32:54 +08:00
|
|
|
};
|
|
|
|
|
2012-10-09 17:45:57 +08:00
|
|
|
// Be nice and warn if configured mapnik version
|
|
|
|
// is != instaled mapnik version
|
|
|
|
if ( mapnik.versions.mapnik != me.grainstore.mapnik_version ) {
|
|
|
|
console.warn("WARNING: detected mapnik version ("
|
|
|
|
+ mapnik.versions.mapnik + ") != configured mapnik version ("
|
|
|
|
+ me.grainstore.mapnik_version + ")");
|
|
|
|
}
|
|
|
|
|
2012-09-25 00:57:48 +08:00
|
|
|
// Set the cache chanel info to invalidate the cache on the frontend server
|
|
|
|
//
|
|
|
|
// @param req The request object.
|
|
|
|
// The function will have no effect unless req.res exists.
|
|
|
|
// It is expected that req.params contains 'table' and 'dbname'
|
|
|
|
//
|
|
|
|
// @param cb function(err, channel) will be called when ready.
|
|
|
|
// the channel parameter will be null if nothing was added
|
|
|
|
//
|
|
|
|
me.addCacheChannel = function(req, cb) {
|
|
|
|
// skip non-GET requests, or requests for which there's no response
|
|
|
|
if ( req.method != 'GET' || ! req.res ) { cb(null, null); return; }
|
|
|
|
var res = req.res;
|
|
|
|
var ttl = global.environment.varnish.ttl || 86400;
|
|
|
|
Cache.generateCacheChannel(req, function(channel){
|
|
|
|
res.header('X-Cache-Channel', channel);
|
2012-10-24 15:40:05 +08:00
|
|
|
var cache_policy = req.query.cache_policy;
|
|
|
|
if ( cache_policy == 'persist' ) {
|
|
|
|
res.header('Cache-Control', 'public,max-age=31536000'); // 1 year
|
|
|
|
} else {
|
|
|
|
res.header('Last-Modified', new Date().toUTCString());
|
|
|
|
res.header('Cache-Control', 'no-cache,max-age='+ttl+',must-revalidate, public');
|
|
|
|
}
|
2012-09-25 00:57:48 +08:00
|
|
|
cb(null, channel); // add last-modified too ?
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-05-03 02:32:54 +08:00
|
|
|
/**
|
|
|
|
* Whitelist input and get database name & default geometry type from
|
|
|
|
* subdomain/user metadata held in CartoDB Redis
|
|
|
|
* @param req - standard express request obj. Should have host & table
|
|
|
|
* @param callback
|
|
|
|
*/
|
|
|
|
me.req2params = function(req, callback){
|
|
|
|
|
|
|
|
// Whitelist query parameters and attach format
|
2012-11-14 22:28:58 +08:00
|
|
|
var good_query = ['sql', 'geom_type', 'cache_buster', 'cache_policy', 'callback', 'interactivity', 'map_key', 'api_key', 'style', 'style_version', 'style_convert' ];
|
2012-05-03 02:32:54 +08:00
|
|
|
var bad_query = _.difference(_.keys(req.query), good_query);
|
|
|
|
|
|
|
|
_.each(bad_query, function(key){ delete req.query[key]; });
|
|
|
|
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
|
|
|
|
|
|
|
|
// bring all query values onto req.params object
|
|
|
|
_.extend(req.params, req.query);
|
|
|
|
|
|
|
|
// for cartodb, ensure interactivity is cartodb_id or user specified
|
|
|
|
req.params.interactivity = req.params.interactivity || 'cartodb_id';
|
|
|
|
|
2012-09-03 20:54:23 +08:00
|
|
|
req.params.processXML = function(req, xml, callback) {
|
2012-09-05 21:41:22 +08:00
|
|
|
var dbuser = req.dbuser ? req.dbuser : global.settings.postgres.user;
|
|
|
|
if ( ! me.rx_dbuser ) me.rx_dbuser = /(<Parameter name="user"><!\[CDATA\[)[^\]]*(]]><\/Parameter>)/;
|
|
|
|
xml = xml.replace(me.rx_dbuser, "$1" + dbuser + "$2");
|
2012-09-03 20:54:23 +08:00
|
|
|
callback(null, xml);
|
|
|
|
}
|
|
|
|
|
2012-09-25 00:57:48 +08:00
|
|
|
var that = this;
|
|
|
|
|
2012-05-03 02:32:54 +08:00
|
|
|
Step(
|
|
|
|
function getPrivacy(){
|
|
|
|
cartoData.authorize(req, this);
|
|
|
|
},
|
|
|
|
function gatekeep(err, data){
|
|
|
|
if(err) throw err;
|
2012-09-06 02:16:55 +08:00
|
|
|
if(data === "0") throw new Error("Sorry, you are unauthorized (permission denied)");
|
2012-05-03 02:32:54 +08:00
|
|
|
return data;
|
|
|
|
},
|
|
|
|
function getDatabase(err, data){
|
|
|
|
if(err) throw err;
|
|
|
|
|
|
|
|
cartoData.getDatabase(req, this);
|
|
|
|
},
|
|
|
|
function getGeometryType(err, data){
|
|
|
|
if (err) throw err;
|
|
|
|
_.extend(req.params, {dbname:data});
|
|
|
|
|
|
|
|
cartoData.getGeometryType(req, this);
|
|
|
|
},
|
|
|
|
function finishSetup(err, data){
|
2012-09-25 00:57:48 +08:00
|
|
|
if ( err ) { callback(err, req); return; }
|
|
|
|
|
2012-05-03 02:32:54 +08:00
|
|
|
if (!_.isNull(data))
|
|
|
|
_.extend(req.params, {geom_type: data});
|
|
|
|
|
2012-09-25 00:57:48 +08:00
|
|
|
that.addCacheChannel(req, function(err, chan) {
|
|
|
|
callback(err, req);
|
|
|
|
});
|
2012-05-03 02:32:54 +08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Little helper method to get the current list of infowindow variables and return to client
|
|
|
|
* @param req
|
|
|
|
* @param callback
|
|
|
|
*/
|
|
|
|
me.getInfowindow = function(req, callback){
|
|
|
|
var that = this;
|
|
|
|
|
|
|
|
Step(
|
|
|
|
function(){
|
|
|
|
that.req2params(req, this);
|
|
|
|
},
|
|
|
|
function(err, data){
|
2012-08-15 02:01:05 +08:00
|
|
|
if (err) callback(err, null);
|
|
|
|
else cartoData.getInfowindow(data, callback);
|
2012-05-03 02:32:54 +08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Little helper method to get map metadata and return to client
|
|
|
|
* @param req
|
|
|
|
* @param callback
|
|
|
|
*/
|
|
|
|
me.getMapMetadata = function(req, callback){
|
|
|
|
var that = this;
|
|
|
|
|
|
|
|
Step(
|
|
|
|
function(){
|
|
|
|
that.req2params(req, this);
|
|
|
|
},
|
|
|
|
function(err, data){
|
2012-09-10 23:01:21 +08:00
|
|
|
if (err) callback(err, null);
|
|
|
|
else cartoData.getMapMetadata(data, callback);
|
2012-05-03 02:32:54 +08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper to clear out tile cache on request
|
|
|
|
* @param req
|
|
|
|
* @param callback
|
|
|
|
*/
|
|
|
|
me.flushCache = function(req, Cache, callback){
|
|
|
|
var that = this;
|
|
|
|
|
|
|
|
Step(
|
|
|
|
function(){
|
|
|
|
that.req2params(req, this);
|
|
|
|
},
|
|
|
|
function(err, data){
|
|
|
|
if (err) throw err;
|
2012-10-05 22:55:58 +08:00
|
|
|
if(Cache) {
|
|
|
|
Cache.invalidate_db(req.params.dbname, req.params.table);
|
|
|
|
}
|
2012-05-03 02:32:54 +08:00
|
|
|
callback(null, true);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
return me;
|
2011-10-13 21:22:54 +08:00
|
|
|
}();
|