commit
143f0ea67b
@ -2,7 +2,6 @@ const PSQL = require('cartodb-psql');
|
|||||||
const cors = require('../middleware/cors');
|
const cors = require('../middleware/cors');
|
||||||
const user = require('../middleware/user');
|
const user = require('../middleware/user');
|
||||||
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
||||||
const layergroupToken = require('../middleware/layergroup-token');
|
|
||||||
const credentials = require('../middleware/credentials');
|
const credentials = require('../middleware/credentials');
|
||||||
const authorize = require('../middleware/authorize');
|
const authorize = require('../middleware/authorize');
|
||||||
const dbConnSetup = require('../middleware/db-conn-setup');
|
const dbConnSetup = require('../middleware/db-conn-setup');
|
||||||
@ -26,13 +25,12 @@ AnalysesController.prototype.register = function (app) {
|
|||||||
app.get(
|
app.get(
|
||||||
`${mapconfigBasePath}/analyses/catalog`,
|
`${mapconfigBasePath}/analyses/catalog`,
|
||||||
cors(),
|
cors(),
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorize(this.authApi),
|
authorize(this.authApi),
|
||||||
dbConnSetup(this.pgConnection),
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS_CATALOG),
|
||||||
|
cleanUpQueryParams(),
|
||||||
createPGClient(),
|
createPGClient(),
|
||||||
getDataFromQuery({ queryTemplate: catalogQueryTpl, key: 'catalog' }),
|
getDataFromQuery({ queryTemplate: catalogQueryTpl, key: 'catalog' }),
|
||||||
getDataFromQuery({ queryTemplate: tablesQueryTpl, key: 'tables' }),
|
getDataFromQuery({ queryTemplate: tablesQueryTpl, key: 'tables' }),
|
||||||
|
@ -1,665 +0,0 @@
|
|||||||
const cors = require('../middleware/cors');
|
|
||||||
const user = require('../middleware/user');
|
|
||||||
const vectorError = require('../middleware/vector-error');
|
|
||||||
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
|
||||||
const layergroupToken = require('../middleware/layergroup-token');
|
|
||||||
const credentials = require('../middleware/credentials');
|
|
||||||
const dbConnSetup = require('../middleware/db-conn-setup');
|
|
||||||
const authorize = require('../middleware/authorize');
|
|
||||||
const rateLimit = require('../middleware/rate-limit');
|
|
||||||
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
|
||||||
const cacheControlHeader = require('../middleware/cache-control-header');
|
|
||||||
const cacheChannelHeader = require('../middleware/cache-channel-header');
|
|
||||||
const surrogateKeyHeader = require('../middleware/surrogate-key-header');
|
|
||||||
const lastModifiedHeader = require('../middleware/last-modified-header');
|
|
||||||
const sendResponse = require('../middleware/send-response');
|
|
||||||
const DataviewBackend = require('../backends/dataview');
|
|
||||||
const AnalysisStatusBackend = require('../backends/analysis-status');
|
|
||||||
const MapStoreMapConfigProvider = require('../models/mapconfig/provider/map-store-provider');
|
|
||||||
const dbParamsFromResLocals = require('../utils/database-params');
|
|
||||||
|
|
||||||
const SUPPORTED_FORMATS = {
|
|
||||||
grid_json: true,
|
|
||||||
json_torque: true,
|
|
||||||
torque_json: true,
|
|
||||||
png: true,
|
|
||||||
png32: true,
|
|
||||||
mvt: true
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALLOWED_DATAVIEW_QUERY_PARAMS = [
|
|
||||||
'filters', // json
|
|
||||||
'own_filter', // 0, 1
|
|
||||||
'no_filters', // 0, 1
|
|
||||||
'bbox', // w,s,e,n
|
|
||||||
'start', // number
|
|
||||||
'end', // number
|
|
||||||
'column_type', // string
|
|
||||||
'bins', // number
|
|
||||||
'aggregation', //string
|
|
||||||
'offset', // number
|
|
||||||
'q', // widgets search
|
|
||||||
'categories', // number
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {prepareContext} prepareContext
|
|
||||||
* @param {PgConnection} pgConnection
|
|
||||||
* @param {MapStore} mapStore
|
|
||||||
* @param {TileBackend} tileBackend
|
|
||||||
* @param {PreviewBackend} previewBackend
|
|
||||||
* @param {AttributesBackend} attributesBackend
|
|
||||||
* @param {SurrogateKeysCache} surrogateKeysCache
|
|
||||||
* @param {UserLimitsApi} userLimitsApi
|
|
||||||
* @param {LayergroupAffectedTables} layergroupAffectedTables
|
|
||||||
* @param {AnalysisBackend} analysisBackend
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function LayergroupController(
|
|
||||||
pgConnection,
|
|
||||||
mapStore,
|
|
||||||
tileBackend,
|
|
||||||
previewBackend,
|
|
||||||
attributesBackend,
|
|
||||||
surrogateKeysCache,
|
|
||||||
userLimitsApi,
|
|
||||||
layergroupAffectedTablesCache,
|
|
||||||
analysisBackend,
|
|
||||||
authApi
|
|
||||||
) {
|
|
||||||
this.pgConnection = pgConnection;
|
|
||||||
this.mapStore = mapStore;
|
|
||||||
this.tileBackend = tileBackend;
|
|
||||||
this.previewBackend = previewBackend;
|
|
||||||
this.attributesBackend = attributesBackend;
|
|
||||||
this.surrogateKeysCache = surrogateKeysCache;
|
|
||||||
this.userLimitsApi = userLimitsApi;
|
|
||||||
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
|
||||||
|
|
||||||
this.dataviewBackend = new DataviewBackend(analysisBackend);
|
|
||||||
this.analysisStatusBackend = new AnalysisStatusBackend();
|
|
||||||
this.authApi = authApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = LayergroupController;
|
|
||||||
|
|
||||||
LayergroupController.prototype.register = function(app) {
|
|
||||||
const { base_url_mapconfig: mapConfigBasePath } = app;
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getTile(this.tileBackend, 'map_tile'),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
incrementSuccessMetrics(global.statsClient),
|
|
||||||
incrementErrorMetrics(global.statsClient),
|
|
||||||
tileError(),
|
|
||||||
vectorError(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:z/:x/:y.:format`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getTile(this.tileBackend, 'map_tile'),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
incrementSuccessMetrics(global.statsClient),
|
|
||||||
incrementErrorMetrics(global.statsClient),
|
|
||||||
tileError(),
|
|
||||||
vectorError(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:layer/:z/:x/:y.(:format)`,
|
|
||||||
distinguishLayergroupFromStaticRoute(),
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getTile(this.tileBackend, 'maplayer_tile'),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
incrementSuccessMetrics(global.statsClient),
|
|
||||||
incrementErrorMetrics(global.statsClient),
|
|
||||||
tileError(),
|
|
||||||
vectorError(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:layer/attributes/:fid`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getFeatureAttributes(this.attributesBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
const forcedFormat = 'png';
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(['layer']),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache,
|
|
||||||
forcedFormat
|
|
||||||
),
|
|
||||||
getPreviewImageByCenter(this.previewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(['layer']),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache,
|
|
||||||
forcedFormat
|
|
||||||
),
|
|
||||||
getPreviewImageByBoundingBox(this.previewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Undocumented/non-supported API endpoint methods.
|
|
||||||
// Use at your own peril.
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/dataview/:dataviewName`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getDataview(this.dataviewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:layer/widget/:dataviewName`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
getDataview(this.dataviewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/dataview/:dataviewName/search`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
dataviewSearch(this.dataviewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
createMapStoreMapConfigProvider(
|
|
||||||
this.mapStore,
|
|
||||||
this.userLimitsApi,
|
|
||||||
this.pgConnection,
|
|
||||||
this.layergroupAffectedTablesCache
|
|
||||||
),
|
|
||||||
dataviewSearch(this.dataviewBackend),
|
|
||||||
cacheControlHeader(),
|
|
||||||
cacheChannelHeader(),
|
|
||||||
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
|
||||||
lastModifiedHeader(),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get(
|
|
||||||
`${mapConfigBasePath}/:token/analysis/node/:nodeId`,
|
|
||||||
cors(),
|
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
|
||||||
authorize(this.authApi),
|
|
||||||
dbConnSetup(this.pgConnection),
|
|
||||||
analysisNodeStatus(this.analysisStatusBackend),
|
|
||||||
sendResponse()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function distinguishLayergroupFromStaticRoute () {
|
|
||||||
return function distinguishLayergroupFromStaticRouteMiddleware(req, res, next) {
|
|
||||||
if (req.params.token === 'static') {
|
|
||||||
return next('route');
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function analysisNodeStatus (analysisStatusBackend) {
|
|
||||||
return function analysisNodeStatusMiddleware(req, res, next) {
|
|
||||||
const { nodeId } = req.params;
|
|
||||||
const dbParams = dbParamsFromResLocals(res.locals);
|
|
||||||
|
|
||||||
analysisStatusBackend.getNodeStatus(nodeId, dbParams, (err, nodeStatus, stats = {}) => {
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'GET NODE STATUS';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set({
|
|
||||||
'Cache-Control': 'public,max-age=5',
|
|
||||||
'Last-Modified': new Date().toUTCString()
|
|
||||||
});
|
|
||||||
|
|
||||||
res.body = nodeStatus;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapStoreMapConfigProvider (
|
|
||||||
mapStore,
|
|
||||||
userLimitsApi,
|
|
||||||
pgConnection,
|
|
||||||
affectedTablesCache,
|
|
||||||
forcedFormat = null
|
|
||||||
) {
|
|
||||||
return function createMapStoreMapConfigProviderMiddleware (req, res, next) {
|
|
||||||
const { user, token, cache_buster, api_key } = res.locals;
|
|
||||||
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
|
||||||
const { layer, z, x, y, scale_factor, format } = req.params;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
user, token, cache_buster, api_key,
|
|
||||||
dbuser, dbname, dbpassword, dbhost, dbport,
|
|
||||||
layer, z, x, y, scale_factor, format
|
|
||||||
};
|
|
||||||
|
|
||||||
if (forcedFormat) {
|
|
||||||
params.format = forcedFormat;
|
|
||||||
params.layer = params.layer || 'all';
|
|
||||||
}
|
|
||||||
|
|
||||||
res.locals.mapConfigProvider = new MapStoreMapConfigProvider(
|
|
||||||
mapStore,
|
|
||||||
user,
|
|
||||||
userLimitsApi,
|
|
||||||
pgConnection,
|
|
||||||
affectedTablesCache,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDataview (dataviewBackend) {
|
|
||||||
return function getDataviewMiddleware (req, res, next) {
|
|
||||||
const { user, mapConfigProvider } = res.locals;
|
|
||||||
const { dataviewName } = req.params;
|
|
||||||
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
|
||||||
|
|
||||||
const params = Object.assign({ dataviewName, dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
|
|
||||||
|
|
||||||
dataviewBackend.getDataview(mapConfigProvider, user, params, (err, dataview, stats = {}) => {
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'GET DATAVIEW';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.body = dataview;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function dataviewSearch (dataviewBackend) {
|
|
||||||
return function dataviewSearchMiddleware (req, res, next) {
|
|
||||||
const { user, mapConfigProvider } = res.locals;
|
|
||||||
const { dataviewName } = req.params;
|
|
||||||
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
|
||||||
|
|
||||||
const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
|
|
||||||
|
|
||||||
dataviewBackend.search(mapConfigProvider, user, dataviewName, params, (err, searchResult, stats = {}) => {
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'GET DATAVIEW SEARCH';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.body = searchResult;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFeatureAttributes (attributesBackend) {
|
|
||||||
return function getFeatureAttributesMiddleware (req, res, next) {
|
|
||||||
req.profiler.start('windshaft.maplayer_attribute');
|
|
||||||
|
|
||||||
const { mapConfigProvider } = res.locals;
|
|
||||||
const { token } = res.locals;
|
|
||||||
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
|
||||||
const { layer, fid } = req.params;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
token,
|
|
||||||
dbuser, dbname, dbpassword, dbhost, dbport,
|
|
||||||
layer, fid
|
|
||||||
};
|
|
||||||
|
|
||||||
attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => {
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'GET ATTRIBUTES';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.body = tile;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatusCode(tile, format){
|
|
||||||
return tile.length === 0 && format === 'mvt' ? 204 : 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseFormat (format = '') {
|
|
||||||
const prettyFormat = format.replace('.', '_');
|
|
||||||
return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTile (tileBackend, profileLabel = 'tile') {
|
|
||||||
return function getTileMiddleware (req, res, next) {
|
|
||||||
req.profiler.start(`windshaft.${profileLabel}`);
|
|
||||||
|
|
||||||
const { mapConfigProvider } = res.locals;
|
|
||||||
const { token } = res.locals;
|
|
||||||
const { layer, z, x, y, format } = req.params;
|
|
||||||
|
|
||||||
const params = { token, layer, z, x, y, format };
|
|
||||||
|
|
||||||
tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats = {}) => {
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers) {
|
|
||||||
res.set(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatStat = parseFormat(req.params.format);
|
|
||||||
|
|
||||||
res.statusCode = getStatusCode(tile, formatStat);
|
|
||||||
res.body = tile;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPreviewImageByCenter (previewBackend) {
|
|
||||||
return function getPreviewImageByCenterMiddleware (req, res, next) {
|
|
||||||
const width = +req.params.width;
|
|
||||||
const height = +req.params.height;
|
|
||||||
const zoom = +req.params.z;
|
|
||||||
const center = {
|
|
||||||
lng: +req.params.lng,
|
|
||||||
lat: +req.params.lat
|
|
||||||
};
|
|
||||||
|
|
||||||
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
|
||||||
const { mapConfigProvider: provider } = res.locals;
|
|
||||||
|
|
||||||
previewBackend.getImage(provider, format, width, height, zoom, center, (err, image, headers, stats = {}) => {
|
|
||||||
req.profiler.done(`render-${format}`);
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'STATIC_MAP';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers) {
|
|
||||||
res.set(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
|
|
||||||
|
|
||||||
res.body = image;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPreviewImageByBoundingBox (previewBackend) {
|
|
||||||
return function getPreviewImageByBoundingBoxMiddleware (req, res, next) {
|
|
||||||
const width = +req.params.width;
|
|
||||||
const height = +req.params.height;
|
|
||||||
const bounds = {
|
|
||||||
west: +req.params.west,
|
|
||||||
north: +req.params.north,
|
|
||||||
east: +req.params.east,
|
|
||||||
south: +req.params.south
|
|
||||||
};
|
|
||||||
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
|
||||||
const { mapConfigProvider: provider } = res.locals;
|
|
||||||
|
|
||||||
previewBackend.getImage(provider, format, width, height, bounds, (err, image, headers, stats = {}) => {
|
|
||||||
req.profiler.done(`render-${format}`);
|
|
||||||
req.profiler.add(stats);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
err.label = 'STATIC_MAP';
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers) {
|
|
||||||
res.set(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
|
|
||||||
|
|
||||||
res.body = image;
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function incrementSuccessMetrics (statsClient) {
|
|
||||||
return function incrementSuccessMetricsMiddleware (req, res, next) {
|
|
||||||
const formatStat = parseFormat(req.params.format);
|
|
||||||
|
|
||||||
statsClient.increment('windshaft.tiles.success');
|
|
||||||
statsClient.increment(`windshaft.tiles.${formatStat}.success`);
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function incrementErrorMetrics (statsClient) {
|
|
||||||
return function incrementErrorMetricsMiddleware (err, req, res, next) {
|
|
||||||
const formatStat = parseFormat(req.params.format);
|
|
||||||
|
|
||||||
statsClient.increment('windshaft.tiles.error');
|
|
||||||
statsClient.increment(`windshaft.tiles.${formatStat}.error`);
|
|
||||||
|
|
||||||
next(err);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function tileError () {
|
|
||||||
return function tileErrorMiddleware (err, req, res, next) {
|
|
||||||
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
|
|
||||||
res.statusCode = 204;
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
|
||||||
let errMsg = err.message ? ( '' + err.message ) : ( '' + err );
|
|
||||||
|
|
||||||
// Rewrite mapnik parsing errors to start with layer number
|
|
||||||
const matches = errMsg.match("(.*) in style 'layer([0-9]+)'");
|
|
||||||
|
|
||||||
if (matches) {
|
|
||||||
errMsg = `style${matches[2]}: ${matches[1]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
err.message = errMsg;
|
|
||||||
err.label = 'TILE RENDER';
|
|
||||||
|
|
||||||
next(err);
|
|
||||||
};
|
|
||||||
}
|
|
75
lib/cartodb/controllers/layergroup/analysis.js
Normal file
75
lib/cartodb/controllers/layergroup/analysis.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
const cors = require('../../middleware/cors');
|
||||||
|
const user = require('../../middleware/user');
|
||||||
|
const layergroupToken = require('../../middleware/layergroup-token');
|
||||||
|
const cleanUpQueryParams = require('../../middleware/clean-up-query-params');
|
||||||
|
const credentials = require('../../middleware/credentials');
|
||||||
|
const dbConnSetup = require('../../middleware/db-conn-setup');
|
||||||
|
const authorize = require('../../middleware/authorize');
|
||||||
|
const rateLimit = require('../../middleware/rate-limit');
|
||||||
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||||
|
const sendResponse = require('../../middleware/send-response');
|
||||||
|
const dbParamsFromResLocals = require('../../utils/database-params');
|
||||||
|
|
||||||
|
module.exports = class AnalysisController {
|
||||||
|
constructor (
|
||||||
|
analysisStatusBackend,
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
authApi,
|
||||||
|
surrogateKeysCache
|
||||||
|
) {
|
||||||
|
this.analysisStatusBackend = analysisStatusBackend;
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
this.authApi = authApi;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
register (app) {
|
||||||
|
const { base_url_mapconfig: mapConfigBasePath } = app;
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/analysis/node/:nodeId`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ANALYSIS),
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
analysisNodeStatus(this.analysisStatusBackend),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function analysisNodeStatus (analysisStatusBackend) {
|
||||||
|
return function analysisNodeStatusMiddleware(req, res, next) {
|
||||||
|
const { nodeId } = req.params;
|
||||||
|
const dbParams = dbParamsFromResLocals(res.locals);
|
||||||
|
|
||||||
|
analysisStatusBackend.getNodeStatus(nodeId, dbParams, (err, nodeStatus, stats = {}) => {
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'GET NODE STATUS';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
'Cache-Control': 'public,max-age=5',
|
||||||
|
'Last-Modified': new Date().toUTCString()
|
||||||
|
});
|
||||||
|
|
||||||
|
res.body = nodeStatus;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
93
lib/cartodb/controllers/layergroup/attributes.js
Normal file
93
lib/cartodb/controllers/layergroup/attributes.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const cors = require('../../middleware/cors');
|
||||||
|
const user = require('../../middleware/user');
|
||||||
|
const layergroupToken = require('../../middleware/layergroup-token');
|
||||||
|
const cleanUpQueryParams = require('../../middleware/clean-up-query-params');
|
||||||
|
const credentials = require('../../middleware/credentials');
|
||||||
|
const dbConnSetup = require('../../middleware/db-conn-setup');
|
||||||
|
const authorize = require('../../middleware/authorize');
|
||||||
|
const rateLimit = require('../../middleware/rate-limit');
|
||||||
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||||
|
const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider');
|
||||||
|
const cacheControlHeader = require('../../middleware/cache-control-header');
|
||||||
|
const cacheChannelHeader = require('../../middleware/cache-channel-header');
|
||||||
|
const surrogateKeyHeader = require('../../middleware/surrogate-key-header');
|
||||||
|
const lastModifiedHeader = require('../../middleware/last-modified-header');
|
||||||
|
const sendResponse = require('../../middleware/send-response');
|
||||||
|
|
||||||
|
module.exports = class AttribitesController {
|
||||||
|
constructor (
|
||||||
|
attributesBackend,
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
authApi,
|
||||||
|
surrogateKeysCache
|
||||||
|
) {
|
||||||
|
this.attributesBackend = attributesBackend;
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
this.authApi = authApi;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
register (app) {
|
||||||
|
const { base_url_mapconfig: mapConfigBasePath } = app;
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:layer/attributes/:fid`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES),
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getFeatureAttributes(this.attributesBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getFeatureAttributes (attributesBackend) {
|
||||||
|
return function getFeatureAttributesMiddleware (req, res, next) {
|
||||||
|
req.profiler.start('windshaft.maplayer_attribute');
|
||||||
|
|
||||||
|
const { mapConfigProvider } = res.locals;
|
||||||
|
const { token } = res.locals;
|
||||||
|
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
||||||
|
const { layer, fid } = req.params;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
token,
|
||||||
|
dbuser, dbname, dbpassword, dbhost, dbport,
|
||||||
|
layer, fid
|
||||||
|
};
|
||||||
|
|
||||||
|
attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => {
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'GET ATTRIBUTES';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.body = tile;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
199
lib/cartodb/controllers/layergroup/dataview.js
Normal file
199
lib/cartodb/controllers/layergroup/dataview.js
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
const cors = require('../../middleware/cors');
|
||||||
|
const user = require('../../middleware/user');
|
||||||
|
const layergroupToken = require('../../middleware/layergroup-token');
|
||||||
|
const cleanUpQueryParams = require('../../middleware/clean-up-query-params');
|
||||||
|
const credentials = require('../../middleware/credentials');
|
||||||
|
const dbConnSetup = require('../../middleware/db-conn-setup');
|
||||||
|
const authorize = require('../../middleware/authorize');
|
||||||
|
const rateLimit = require('../../middleware/rate-limit');
|
||||||
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||||
|
const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider');
|
||||||
|
const cacheControlHeader = require('../../middleware/cache-control-header');
|
||||||
|
const cacheChannelHeader = require('../../middleware/cache-channel-header');
|
||||||
|
const surrogateKeyHeader = require('../../middleware/surrogate-key-header');
|
||||||
|
const lastModifiedHeader = require('../../middleware/last-modified-header');
|
||||||
|
const sendResponse = require('../../middleware/send-response');
|
||||||
|
|
||||||
|
const ALLOWED_DATAVIEW_QUERY_PARAMS = [
|
||||||
|
'filters', // json
|
||||||
|
'own_filter', // 0, 1
|
||||||
|
'no_filters', // 0, 1
|
||||||
|
'bbox', // w,s,e,n
|
||||||
|
'start', // number
|
||||||
|
'end', // number
|
||||||
|
'column_type', // string
|
||||||
|
'bins', // number
|
||||||
|
'aggregation', //string
|
||||||
|
'offset', // number
|
||||||
|
'q', // widgets search
|
||||||
|
'categories', // number
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = class DataviewController {
|
||||||
|
constructor (
|
||||||
|
dataviewBackend,
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
authApi,
|
||||||
|
surrogateKeysCache
|
||||||
|
) {
|
||||||
|
this.dataviewBackend = dataviewBackend;
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
this.authApi = authApi;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
register (app) {
|
||||||
|
const { base_url_mapconfig: mapConfigBasePath } = app;
|
||||||
|
|
||||||
|
// Undocumented/non-supported API endpoint methods.
|
||||||
|
// Use at your own peril.
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/dataview/:dataviewName`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW),
|
||||||
|
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getDataview(this.dataviewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:layer/widget/:dataviewName`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW),
|
||||||
|
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getDataview(this.dataviewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/dataview/:dataviewName/search`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH),
|
||||||
|
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
dataviewSearch(this.dataviewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:layer/widget/:dataviewName/search`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.DATAVIEW_SEARCH),
|
||||||
|
cleanUpQueryParams(ALLOWED_DATAVIEW_QUERY_PARAMS),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
dataviewSearch(this.dataviewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getDataview (dataviewBackend) {
|
||||||
|
return function getDataviewMiddleware (req, res, next) {
|
||||||
|
const { user, mapConfigProvider } = res.locals;
|
||||||
|
const { dataviewName } = req.params;
|
||||||
|
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
||||||
|
|
||||||
|
const params = Object.assign({ dataviewName, dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
|
||||||
|
|
||||||
|
dataviewBackend.getDataview(mapConfigProvider, user, params, (err, dataview, stats = {}) => {
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'GET DATAVIEW';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.body = dataview;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataviewSearch (dataviewBackend) {
|
||||||
|
return function dataviewSearchMiddleware (req, res, next) {
|
||||||
|
const { user, mapConfigProvider } = res.locals;
|
||||||
|
const { dataviewName } = req.params;
|
||||||
|
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
||||||
|
|
||||||
|
const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
|
||||||
|
|
||||||
|
dataviewBackend.search(mapConfigProvider, user, dataviewName, params, (err, searchResult, stats = {}) => {
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'GET DATAVIEW SEARCH';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.body = searchResult;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
114
lib/cartodb/controllers/layergroup/index.js
Normal file
114
lib/cartodb/controllers/layergroup/index.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
const DataviewBackend = require('../../backends/dataview');
|
||||||
|
const AnalysisStatusBackend = require('../../backends/analysis-status');
|
||||||
|
|
||||||
|
const TileController = require('./tile');
|
||||||
|
const AttributesController = require('./attributes');
|
||||||
|
const StaticController = require('./static');
|
||||||
|
const DataviewController = require('./dataview');
|
||||||
|
const AnalysisController = require('./analysis');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {prepareContext} prepareContext
|
||||||
|
* @param {PgConnection} pgConnection
|
||||||
|
* @param {MapStore} mapStore
|
||||||
|
* @param {TileBackend} tileBackend
|
||||||
|
* @param {PreviewBackend} previewBackend
|
||||||
|
* @param {AttributesBackend} attributesBackend
|
||||||
|
* @param {SurrogateKeysCache} surrogateKeysCache
|
||||||
|
* @param {UserLimitsApi} userLimitsApi
|
||||||
|
* @param {LayergroupAffectedTables} layergroupAffectedTables
|
||||||
|
* @param {AnalysisBackend} analysisBackend
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function LayergroupController(
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
tileBackend,
|
||||||
|
previewBackend,
|
||||||
|
attributesBackend,
|
||||||
|
surrogateKeysCache,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
analysisBackend,
|
||||||
|
authApi
|
||||||
|
) {
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.tileBackend = tileBackend;
|
||||||
|
this.previewBackend = previewBackend;
|
||||||
|
this.attributesBackend = attributesBackend;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
|
||||||
|
this.dataviewBackend = new DataviewBackend(analysisBackend);
|
||||||
|
this.analysisStatusBackend = new AnalysisStatusBackend();
|
||||||
|
this.authApi = authApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LayergroupController;
|
||||||
|
|
||||||
|
LayergroupController.prototype.register = function(app) {
|
||||||
|
|
||||||
|
const tileController = new TileController(
|
||||||
|
this.tileBackend,
|
||||||
|
this.pgConnection,
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
this.authApi,
|
||||||
|
this.surrogateKeysCache
|
||||||
|
);
|
||||||
|
|
||||||
|
tileController.register(app);
|
||||||
|
|
||||||
|
const attributesController = new AttributesController(
|
||||||
|
this.attributesBackend,
|
||||||
|
this.pgConnection,
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
this.authApi,
|
||||||
|
this.surrogateKeysCache
|
||||||
|
);
|
||||||
|
|
||||||
|
attributesController.register(app);
|
||||||
|
|
||||||
|
const staticController = new StaticController(
|
||||||
|
this.previewBackend,
|
||||||
|
this.pgConnection,
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
this.authApi,
|
||||||
|
this.surrogateKeysCache
|
||||||
|
);
|
||||||
|
|
||||||
|
staticController.register(app);
|
||||||
|
|
||||||
|
const dataviewController = new DataviewController(
|
||||||
|
this.dataviewBackend,
|
||||||
|
this.pgConnection,
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
this.authApi,
|
||||||
|
this.surrogateKeysCache
|
||||||
|
);
|
||||||
|
|
||||||
|
dataviewController.register(app);
|
||||||
|
|
||||||
|
const analysisController = new AnalysisController(
|
||||||
|
this.analysisStatusBackend,
|
||||||
|
this.pgConnection,
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
this.authApi,
|
||||||
|
this.surrogateKeysCache
|
||||||
|
);
|
||||||
|
|
||||||
|
analysisController.register(app);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
const MapStoreMapConfigProvider = require('../../../models/mapconfig/provider/map-store-provider');
|
||||||
|
|
||||||
|
module.exports = function createMapStoreMapConfigProvider (
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
pgConnection,
|
||||||
|
affectedTablesCache,
|
||||||
|
forcedFormat = null
|
||||||
|
) {
|
||||||
|
return function createMapStoreMapConfigProviderMiddleware (req, res, next) {
|
||||||
|
const { user, token, cache_buster, api_key } = res.locals;
|
||||||
|
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
|
||||||
|
const { layer, z, x, y, scale_factor, format } = req.params;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
user, token, cache_buster, api_key,
|
||||||
|
dbuser, dbname, dbpassword, dbhost, dbport,
|
||||||
|
layer, z, x, y, scale_factor, format
|
||||||
|
};
|
||||||
|
|
||||||
|
if (forcedFormat) {
|
||||||
|
params.format = forcedFormat;
|
||||||
|
params.layer = params.layer || 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.mapConfigProvider = new MapStoreMapConfigProvider(
|
||||||
|
mapStore,
|
||||||
|
user,
|
||||||
|
userLimitsApi,
|
||||||
|
pgConnection,
|
||||||
|
affectedTablesCache,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
161
lib/cartodb/controllers/layergroup/static.js
Normal file
161
lib/cartodb/controllers/layergroup/static.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
const cors = require('../../middleware/cors');
|
||||||
|
const user = require('../../middleware/user');
|
||||||
|
const layergroupToken = require('../../middleware/layergroup-token');
|
||||||
|
const cleanUpQueryParams = require('../../middleware/clean-up-query-params');
|
||||||
|
const credentials = require('../../middleware/credentials');
|
||||||
|
const dbConnSetup = require('../../middleware/db-conn-setup');
|
||||||
|
const authorize = require('../../middleware/authorize');
|
||||||
|
const rateLimit = require('../../middleware/rate-limit');
|
||||||
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||||
|
const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider');
|
||||||
|
const cacheControlHeader = require('../../middleware/cache-control-header');
|
||||||
|
const cacheChannelHeader = require('../../middleware/cache-channel-header');
|
||||||
|
const surrogateKeyHeader = require('../../middleware/surrogate-key-header');
|
||||||
|
const lastModifiedHeader = require('../../middleware/last-modified-header');
|
||||||
|
const sendResponse = require('../../middleware/send-response');
|
||||||
|
|
||||||
|
module.exports = class StaticController {
|
||||||
|
constructor (
|
||||||
|
previewBackend,
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
authApi,
|
||||||
|
surrogateKeysCache
|
||||||
|
) {
|
||||||
|
this.previewBackend = previewBackend;
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
this.authApi = authApi;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
register (app) {
|
||||||
|
const { base_url_mapconfig: mapConfigBasePath } = app;
|
||||||
|
|
||||||
|
const forcedFormat = 'png';
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/static/center/:token/:z/:lat/:lng/:width/:height.:format`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC),
|
||||||
|
cleanUpQueryParams(['layer']),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
forcedFormat
|
||||||
|
),
|
||||||
|
getPreviewImageByCenter(this.previewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC),
|
||||||
|
cleanUpQueryParams(['layer']),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache,
|
||||||
|
forcedFormat
|
||||||
|
),
|
||||||
|
getPreviewImageByBoundingBox(this.previewBackend),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getPreviewImageByCenter (previewBackend) {
|
||||||
|
return function getPreviewImageByCenterMiddleware (req, res, next) {
|
||||||
|
const width = +req.params.width;
|
||||||
|
const height = +req.params.height;
|
||||||
|
const zoom = +req.params.z;
|
||||||
|
const center = {
|
||||||
|
lng: +req.params.lng,
|
||||||
|
lat: +req.params.lat
|
||||||
|
};
|
||||||
|
|
||||||
|
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
||||||
|
const { mapConfigProvider: provider } = res.locals;
|
||||||
|
|
||||||
|
previewBackend.getImage(provider, format, width, height, zoom, center, (err, image, headers, stats = {}) => {
|
||||||
|
req.profiler.done(`render-${format}`);
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'STATIC_MAP';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers) {
|
||||||
|
res.set(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
|
||||||
|
|
||||||
|
res.body = image;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPreviewImageByBoundingBox (previewBackend) {
|
||||||
|
return function getPreviewImageByBoundingBoxMiddleware (req, res, next) {
|
||||||
|
const width = +req.params.width;
|
||||||
|
const height = +req.params.height;
|
||||||
|
const bounds = {
|
||||||
|
west: +req.params.west,
|
||||||
|
north: +req.params.north,
|
||||||
|
east: +req.params.east,
|
||||||
|
south: +req.params.south
|
||||||
|
};
|
||||||
|
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
||||||
|
const { mapConfigProvider: provider } = res.locals;
|
||||||
|
|
||||||
|
previewBackend.getImage(provider, format, width, height, bounds, (err, image, headers, stats = {}) => {
|
||||||
|
req.profiler.done(`render-${format}`);
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
err.label = 'STATIC_MAP';
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers) {
|
||||||
|
res.set(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
|
||||||
|
|
||||||
|
res.body = image;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
230
lib/cartodb/controllers/layergroup/tile.js
Normal file
230
lib/cartodb/controllers/layergroup/tile.js
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
const cors = require('../../middleware/cors');
|
||||||
|
const user = require('../../middleware/user');
|
||||||
|
const layergroupToken = require('../../middleware/layergroup-token');
|
||||||
|
const cleanUpQueryParams = require('../../middleware/clean-up-query-params');
|
||||||
|
const credentials = require('../../middleware/credentials');
|
||||||
|
const dbConnSetup = require('../../middleware/db-conn-setup');
|
||||||
|
const authorize = require('../../middleware/authorize');
|
||||||
|
const rateLimit = require('../../middleware/rate-limit');
|
||||||
|
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||||
|
const createMapStoreMapConfigProvider = require('./middlewares/map-store-map-config-provider');
|
||||||
|
const cacheControlHeader = require('../../middleware/cache-control-header');
|
||||||
|
const cacheChannelHeader = require('../../middleware/cache-channel-header');
|
||||||
|
const surrogateKeyHeader = require('../../middleware/surrogate-key-header');
|
||||||
|
const lastModifiedHeader = require('../../middleware/last-modified-header');
|
||||||
|
const sendResponse = require('../../middleware/send-response');
|
||||||
|
const vectorError = require('../../middleware/vector-error');
|
||||||
|
|
||||||
|
const SUPPORTED_FORMATS = {
|
||||||
|
grid_json: true,
|
||||||
|
json_torque: true,
|
||||||
|
torque_json: true,
|
||||||
|
png: true,
|
||||||
|
png32: true,
|
||||||
|
mvt: true
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = class TileController {
|
||||||
|
constructor (
|
||||||
|
tileBackend,
|
||||||
|
pgConnection,
|
||||||
|
mapStore,
|
||||||
|
userLimitsApi,
|
||||||
|
layergroupAffectedTablesCache,
|
||||||
|
authApi,
|
||||||
|
surrogateKeysCache
|
||||||
|
) {
|
||||||
|
this.tileBackend = tileBackend;
|
||||||
|
this.pgConnection = pgConnection;
|
||||||
|
this.mapStore = mapStore;
|
||||||
|
this.userLimitsApi = userLimitsApi;
|
||||||
|
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||||
|
this.authApi = authApi;
|
||||||
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
register (app) {
|
||||||
|
const { base_url_mapconfig: mapConfigBasePath } = app;
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:z/:x/:y@:scale_factor?x.:format`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getTile(this.tileBackend, 'map_tile'),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
incrementSuccessMetrics(global.statsClient),
|
||||||
|
incrementErrorMetrics(global.statsClient),
|
||||||
|
tileError(),
|
||||||
|
vectorError(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:z/:x/:y.:format`,
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getTile(this.tileBackend, 'map_tile'),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
incrementSuccessMetrics(global.statsClient),
|
||||||
|
incrementErrorMetrics(global.statsClient),
|
||||||
|
tileError(),
|
||||||
|
vectorError(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
`${mapConfigBasePath}/:token/:layer/:z/:x/:y.(:format)`,
|
||||||
|
distinguishLayergroupFromStaticRoute(),
|
||||||
|
cors(),
|
||||||
|
user(),
|
||||||
|
layergroupToken(),
|
||||||
|
credentials(),
|
||||||
|
authorize(this.authApi),
|
||||||
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.TILE),
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
createMapStoreMapConfigProvider(
|
||||||
|
this.mapStore,
|
||||||
|
this.userLimitsApi,
|
||||||
|
this.pgConnection,
|
||||||
|
this.layergroupAffectedTablesCache
|
||||||
|
),
|
||||||
|
getTile(this.tileBackend, 'maplayer_tile'),
|
||||||
|
cacheControlHeader(),
|
||||||
|
cacheChannelHeader(),
|
||||||
|
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
|
||||||
|
lastModifiedHeader(),
|
||||||
|
incrementSuccessMetrics(global.statsClient),
|
||||||
|
incrementErrorMetrics(global.statsClient),
|
||||||
|
tileError(),
|
||||||
|
vectorError(),
|
||||||
|
sendResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function distinguishLayergroupFromStaticRoute () {
|
||||||
|
return function distinguishLayergroupFromStaticRouteMiddleware(req, res, next) {
|
||||||
|
if (req.params.token === 'static') {
|
||||||
|
return next('route');
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFormat (format = '') {
|
||||||
|
const prettyFormat = format.replace('.', '_');
|
||||||
|
return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusCode(tile, format){
|
||||||
|
return tile.length === 0 && format === 'mvt' ? 204 : 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTile (tileBackend, profileLabel = 'tile') {
|
||||||
|
return function getTileMiddleware (req, res, next) {
|
||||||
|
req.profiler.start(`windshaft.${profileLabel}`);
|
||||||
|
|
||||||
|
const { mapConfigProvider } = res.locals;
|
||||||
|
const { token } = res.locals;
|
||||||
|
const { layer, z, x, y, format } = req.params;
|
||||||
|
|
||||||
|
const params = { token, layer, z, x, y, format };
|
||||||
|
|
||||||
|
tileBackend.getTile(mapConfigProvider, params, (err, tile, headers, stats = {}) => {
|
||||||
|
req.profiler.add(stats);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers) {
|
||||||
|
res.set(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatStat = parseFormat(req.params.format);
|
||||||
|
|
||||||
|
res.statusCode = getStatusCode(tile, formatStat);
|
||||||
|
res.body = tile;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementSuccessMetrics (statsClient) {
|
||||||
|
return function incrementSuccessMetricsMiddleware (req, res, next) {
|
||||||
|
const formatStat = parseFormat(req.params.format);
|
||||||
|
|
||||||
|
statsClient.increment('windshaft.tiles.success');
|
||||||
|
statsClient.increment(`windshaft.tiles.${formatStat}.success`);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementErrorMetrics (statsClient) {
|
||||||
|
return function incrementErrorMetricsMiddleware (err, req, res, next) {
|
||||||
|
const formatStat = parseFormat(req.params.format);
|
||||||
|
|
||||||
|
statsClient.increment('windshaft.tiles.error');
|
||||||
|
statsClient.increment(`windshaft.tiles.${formatStat}.error`);
|
||||||
|
|
||||||
|
next(err);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tileError () {
|
||||||
|
return function tileErrorMiddleware (err, req, res, next) {
|
||||||
|
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
|
||||||
|
res.statusCode = 204;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
||||||
|
let errMsg = err.message ? ( '' + err.message ) : ( '' + err );
|
||||||
|
|
||||||
|
// Rewrite mapnik parsing errors to start with layer number
|
||||||
|
const matches = errMsg.match("(.*) in style 'layer([0-9]+)'");
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
errMsg = `style${matches[2]}: ${matches[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
err.message = errMsg;
|
||||||
|
err.label = 'TILE RENDER';
|
||||||
|
|
||||||
|
next(err);
|
||||||
|
};
|
||||||
|
}
|
@ -6,7 +6,6 @@ const ResourceLocator = require('../models/resource-locator');
|
|||||||
const cors = require('../middleware/cors');
|
const cors = require('../middleware/cors');
|
||||||
const user = require('../middleware/user');
|
const user = require('../middleware/user');
|
||||||
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
||||||
const layergroupToken = require('../middleware/layergroup-token');
|
|
||||||
const credentials = require('../middleware/credentials');
|
const credentials = require('../middleware/credentials');
|
||||||
const dbConnSetup = require('../middleware/db-conn-setup');
|
const dbConnSetup = require('../middleware/db-conn-setup');
|
||||||
const authorize = require('../middleware/authorize');
|
const authorize = require('../middleware/authorize');
|
||||||
@ -101,13 +100,12 @@ MapController.prototype.composeCreateMapMiddleware = function (endpointGroup, us
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
cors(),
|
cors(),
|
||||||
cleanUpQueryParams(['aggregation']),
|
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, endpointGroup),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorize(this.authApi),
|
authorize(this.authApi),
|
||||||
dbConnSetup(this.pgConnection),
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, endpointGroup),
|
||||||
|
cleanUpQueryParams(['aggregation']),
|
||||||
initProfiler(isTemplateInstantiation),
|
initProfiler(isTemplateInstantiation),
|
||||||
checkJsonContentType(),
|
checkJsonContentType(),
|
||||||
this.getCreateMapMiddlewares(useTemplate),
|
this.getCreateMapMiddlewares(useTemplate),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const cors = require('../middleware/cors');
|
const cors = require('../middleware/cors');
|
||||||
const user = require('../middleware/user');
|
const user = require('../middleware/user');
|
||||||
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
const cleanUpQueryParams = require('../middleware/clean-up-query-params');
|
||||||
const layergroupToken = require('../middleware/layergroup-token');
|
|
||||||
const credentials = require('../middleware/credentials');
|
const credentials = require('../middleware/credentials');
|
||||||
const dbConnSetup = require('../middleware/db-conn-setup');
|
const dbConnSetup = require('../middleware/db-conn-setup');
|
||||||
const authorize = require('../middleware/authorize');
|
const authorize = require('../middleware/authorize');
|
||||||
@ -56,13 +55,12 @@ NamedMapsController.prototype.register = function(app) {
|
|||||||
app.get(
|
app.get(
|
||||||
`${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`,
|
`${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`,
|
||||||
cors(),
|
cors(),
|
||||||
cleanUpQueryParams(),
|
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorize(this.authApi),
|
authorize(this.authApi),
|
||||||
dbConnSetup(this.pgConnection),
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_TILES),
|
||||||
|
cleanUpQueryParams(),
|
||||||
getNamedMapProvider({
|
getNamedMapProvider({
|
||||||
namedMapProviderCache: this.namedMapProviderCache,
|
namedMapProviderCache: this.namedMapProviderCache,
|
||||||
label: 'NAMED_MAP_TILE'
|
label: 'NAMED_MAP_TILE'
|
||||||
@ -83,13 +81,12 @@ NamedMapsController.prototype.register = function(app) {
|
|||||||
app.get(
|
app.get(
|
||||||
`${mapconfigBasePath}/static/named/:template_id/:width/:height.:format`,
|
`${mapconfigBasePath}/static/named/:template_id/:width/:height.:format`,
|
||||||
cors(),
|
cors(),
|
||||||
cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
|
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED),
|
|
||||||
layergroupToken(),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorize(this.authApi),
|
authorize(this.authApi),
|
||||||
dbConnSetup(this.pgConnection),
|
dbConnSetup(this.pgConnection),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.STATIC_NAMED),
|
||||||
|
cleanUpQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
|
||||||
getNamedMapProvider({
|
getNamedMapProvider({
|
||||||
namedMapProviderCache: this.namedMapProviderCache,
|
namedMapProviderCache: this.namedMapProviderCache,
|
||||||
label: 'STATIC_VIZ_MAP', forcedFormat: 'png'
|
label: 'STATIC_VIZ_MAP', forcedFormat: 'png'
|
||||||
|
@ -27,10 +27,10 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${templateBasePath}/`,
|
`${templateBasePath}/`,
|
||||||
cors(),
|
cors(),
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_CREATE),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
checkContentType({ action: 'POST', label: 'POST TEMPLATE' }),
|
checkContentType({ action: 'POST', label: 'POST TEMPLATE' }),
|
||||||
authorizedByAPIKey({ authApi: this.authApi, action: 'create', label: 'POST TEMPLATE' }),
|
authorizedByAPIKey({ authApi: this.authApi, action: 'create', label: 'POST TEMPLATE' }),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_CREATE),
|
||||||
createTemplate({ templateMaps: this.templateMaps }),
|
createTemplate({ templateMaps: this.templateMaps }),
|
||||||
sendResponse()
|
sendResponse()
|
||||||
);
|
);
|
||||||
@ -39,10 +39,10 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${templateBasePath}/:template_id`,
|
`${templateBasePath}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_UPDATE),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
checkContentType({ action: 'PUT', label: 'PUT TEMPLATE' }),
|
checkContentType({ action: 'PUT', label: 'PUT TEMPLATE' }),
|
||||||
authorizedByAPIKey({ authApi: this.authApi, action: 'update', label: 'PUT TEMPLATE' }),
|
authorizedByAPIKey({ authApi: this.authApi, action: 'update', label: 'PUT TEMPLATE' }),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_UPDATE),
|
||||||
updateTemplate({ templateMaps: this.templateMaps }),
|
updateTemplate({ templateMaps: this.templateMaps }),
|
||||||
sendResponse()
|
sendResponse()
|
||||||
);
|
);
|
||||||
@ -51,9 +51,9 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${templateBasePath}/:template_id`,
|
`${templateBasePath}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_GET),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorizedByAPIKey({ authApi: this.authApi, action: 'get', label: 'GET TEMPLATE' }),
|
authorizedByAPIKey({ authApi: this.authApi, action: 'get', label: 'GET TEMPLATE' }),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_GET),
|
||||||
retrieveTemplate({ templateMaps: this.templateMaps }),
|
retrieveTemplate({ templateMaps: this.templateMaps }),
|
||||||
sendResponse()
|
sendResponse()
|
||||||
);
|
);
|
||||||
@ -62,9 +62,9 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${templateBasePath}/:template_id`,
|
`${templateBasePath}/:template_id`,
|
||||||
cors(),
|
cors(),
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_DELETE),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorizedByAPIKey({ authApi: this.authApi, action: 'delete', label: 'DELETE TEMPLATE' }),
|
authorizedByAPIKey({ authApi: this.authApi, action: 'delete', label: 'DELETE TEMPLATE' }),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_DELETE),
|
||||||
destroyTemplate({ templateMaps: this.templateMaps }),
|
destroyTemplate({ templateMaps: this.templateMaps }),
|
||||||
sendResponse()
|
sendResponse()
|
||||||
);
|
);
|
||||||
@ -73,9 +73,9 @@ NamedMapsAdminController.prototype.register = function (app) {
|
|||||||
`${templateBasePath}/`,
|
`${templateBasePath}/`,
|
||||||
cors(),
|
cors(),
|
||||||
user(),
|
user(),
|
||||||
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_LIST),
|
|
||||||
credentials(),
|
credentials(),
|
||||||
authorizedByAPIKey({ authApi: this.authApi, action: 'list', label: 'GET TEMPLATE LIST' }),
|
authorizedByAPIKey({ authApi: this.authApi, action: 'list', label: 'GET TEMPLATE LIST' }),
|
||||||
|
rateLimit(this.userLimitsApi, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED_LIST),
|
||||||
listTemplates({ templateMaps: this.templateMaps }),
|
listTemplates({ templateMaps: this.templateMaps }),
|
||||||
sendResponse()
|
sendResponse()
|
||||||
);
|
);
|
||||||
|
@ -5,10 +5,6 @@ const authErrorMessageTemplate = function (signer, user) {
|
|||||||
|
|
||||||
module.exports = function layergroupToken () {
|
module.exports = function layergroupToken () {
|
||||||
return function layergroupTokenMiddleware (req, res, next) {
|
return function layergroupTokenMiddleware (req, res, next) {
|
||||||
if (!req.params.token) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = res.locals.user;
|
const user = res.locals.user;
|
||||||
const layergroupToken = LayergroupToken.parse(req.params.token);
|
const layergroupToken = LayergroupToken.parse(req.params.token);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user