add public_url option

This commit is contained in:
Alban Mouton 2017-10-18 13:51:37 +02:00
parent 82f179b07c
commit 875521c5a8
10 changed files with 92 additions and 38 deletions

35
Dockerfile_test Normal file
View file

@ -0,0 +1,35 @@
# Run tests inside docker without requiring full installation of dependencies on local machine
# Simply run "docker build -f Dockerfile_test ."
# WARNING: sometimes it fails with a core dumped exception
FROM node:6-stretch
MAINTAINER Petr Sloup <petr.sloup@klokantech.com>
RUN apt-get -qq update \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install \
apt-transport-https \
curl \
unzip \
build-essential \
python \
libcairo2-dev \
libgles2-mesa-dev \
libgbm-dev \
libllvm3.9 \
libprotobuf-dev \
libxxf86vm-dev \
xvfb \
&& apt-get clean
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN wget -O test_data.zip https://github.com/klokantech/tileserver-gl/releases/download/v1.3.0/test_data.zip
RUN unzip -q test_data.zip -d test_data
ENV NODE_ENV="test"
COPY package.json .
RUN npm install
COPY / .
RUN xvfb-run --server-args="-screen 0 1024x768x24" npm test

View file

@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{name}} - TileServer GL</title> <title>{{name}} - TileServer GL</title>
{{#is_vector}} {{#is_vector}}
<link rel="stylesheet" type="text/css" href="/mapbox-gl.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
<link rel="stylesheet" type="text/css" href="/mapbox-gl-inspect.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl-inspect.css{{&key_query}}" />
<script src="/mapbox-gl.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
<script src="/mapbox-gl-inspect.min.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl-inspect.min.js{{&key_query}}"></script>
<style> <style>
body {background:#fff;color:#333;font-family:Arial, sans-serif;} body {background:#fff;color:#333;font-family:Arial, sans-serif;}
#map {position:absolute;top:0;left:0;right:250px;bottom:0;} #map {position:absolute;top:0;left:0;right:250px;bottom:0;}
@ -18,9 +18,9 @@
</style> </style>
{{/is_vector}} {{/is_vector}}
{{^is_vector}} {{^is_vector}}
<link rel="stylesheet" type="text/css" href="/mapbox.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
<script src="/mapbox.js{{&key_query}}"></script> <script src="{{public_url}}mapbox.js{{&key_query}}"></script>
<script src="/leaflet-hash.js{{&key_query}}"></script> <script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
<style> <style>
body { margin:0; padding:0; } body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; } #map { position:absolute; top:0; bottom:0; width:100%; }
@ -74,7 +74,7 @@
<h1 style="display:none;">{{name}}</h1> <h1 style="display:none;">{{name}}</h1>
<div id='map'></div> <div id='map'></div>
<script> <script>
var map = L.mapbox.map('map', '/data/{{id}}.json{{&key_query}}', { zoomControl: false }); var map = L.mapbox.map('map', '{{public_url}}data/{{id}}.json{{&key_query}}', { zoomControl: false });
map.eachLayer(function(layer) { map.eachLayer(function(layer) {
// do not add scale prefix even if retina display is detected // do not add scale prefix even if retina display is detected
layer.scalePrefix = '.'; layer.scalePrefix = '.';

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>TileServer GL - Server for vector and raster maps with GL styles</title> <title>TileServer GL - Server for vector and raster maps with GL styles</title>
<link rel="stylesheet" type="text/css" href="/index.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}index.css{{&key_query}}" />
<script> <script>
function toggle_xyz(id) { function toggle_xyz(id) {
var el = document.getElementById(id); var el = document.getElementById(id);
@ -17,7 +17,7 @@
</head> </head>
<body> <body>
<section> <section>
<h1 class="title {{#if is_light}}light{{/if}}"><img src="/images/logo.png" alt="TileServer GL" /></h1> <h1 class="title {{#if is_light}}light{{/if}}"><img src="{{public_url}}images/logo.png" alt="TileServer GL" /></h1>
<h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2> <h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2>
{{#if styles}} {{#if styles}}
<h2 class="box-header">Styles</h2> <h2 class="box-header">Styles</h2>
@ -25,9 +25,9 @@
{{#each styles}} {{#each styles}}
<div class="item"> <div class="item">
{{#if thumbnail}} {{#if thumbnail}}
<img src="/styles/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" /> <img src="{{public_url}}styles/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
{{else}} {{else}}
<img src="/images/placeholder.png" alt="{{name}} preview" /> <img src="{{public_url}}images/placeholder.png" alt="{{name}} preview" />
{{/if}} {{/if}}
<div class="details"> <div class="details">
<h3>{{name}}</h3> <h3>{{name}}</h3>
@ -35,10 +35,10 @@
<p class="services"> <p class="services">
services: services:
{{#if serving_data}} {{#if serving_data}}
<a href="/styles/{{@key}}/style.json{{&../key_query}}">GL Style</a> <a href="{{public_url}}styles/{{@key}}/style.json{{&../key_query}}">GL Style</a>
{{/if}} {{/if}}
{{#if serving_rendered}} {{#if serving_rendered}}
{{#if serving_data}}| {{/if}}<a href="/styles/{{@key}}.json{{&../key_query}}">TileJSON</a> {{#if serving_data}}| {{/if}}<a href="{{public_url}}styles/{{@key}}.json{{&../key_query}}">TileJSON</a>
{{/if}} {{/if}}
{{#if wmts_link}} {{#if wmts_link}}
| <a href="{{&wmts_link}}">WMTS</a> | <a href="{{&wmts_link}}">WMTS</a>
@ -52,14 +52,14 @@
<div class="viewers"> <div class="viewers">
{{#if serving_data}} {{#if serving_data}}
{{#if serving_rendered}} {{#if serving_rendered}}
<a class="btn" href="/styles/{{@key}}/{{&../key_query}}{{viewer_hash}}">Viewer</a> <a class="btn" href="{{public_url}}styles/{{@key}}/{{&../key_query}}{{viewer_hash}}">Viewer</a>
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#if serving_rendered}} {{#if serving_rendered}}
<a class="btn" href="/styles/{{@key}}/?{{&../key_query_part}}raster{{viewer_hash}}">Raster</a> <a class="btn" href="{{public_url}}styles/{{@key}}/?{{&../key_query_part}}raster{{viewer_hash}}">Raster</a>
{{/if}} {{/if}}
{{#if serving_data}} {{#if serving_data}}
<a class="btn" href="/styles/{{@key}}/?{{&../key_query_part}}vector{{viewer_hash}}">Vector</a> <a class="btn" href="{{public_url}}styles/{{@key}}/?{{&../key_query_part}}vector{{viewer_hash}}">Vector</a>
{{/if}} {{/if}}
</div> </div>
</div> </div>
@ -72,15 +72,15 @@
{{#each data}} {{#each data}}
<div class="item"> <div class="item">
{{#if thumbnail}} {{#if thumbnail}}
<img src="/data/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" /> <img src="{{public_url}}data/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
{{else}} {{else}}
<img src="/images/placeholder.png" alt="{{name}} preview" /> <img src="{{public_url}}images/placeholder.png" alt="{{name}} preview" />
{{/if}} {{/if}}
<div class="details"> <div class="details">
<h3>{{name}}</h3> <h3>{{name}}</h3>
<p class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}} | type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data</p> <p class="identifier">identifier: {{@key}}{{#if formatted_filesize}} | size: {{formatted_filesize}}{{/if}} | type: {{#is_vector}}vector{{/is_vector}}{{^is_vector}}raster{{/is_vector}} data</p>
<p class="services"> <p class="services">
services: <a href="/data/{{@key}}.json{{&../key_query}}">TileJSON</a> services: <a href="{{public_url}}data/{{@key}}.json{{&../key_query}}">TileJSON</a>
{{#if wmts_link}} {{#if wmts_link}}
| <a href="{{&wmts_link}}">WMTS</a> | <a href="{{&wmts_link}}">WMTS</a>
{{/if}} {{/if}}
@ -92,10 +92,10 @@
</div> </div>
<div class="viewers"> <div class="viewers">
{{#is_vector}} {{#is_vector}}
<a class="btn" href="/data/{{@key}}/{{&../key_query}}{{viewer_hash}}">Inspect</a> <a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">Inspect</a>
{{/is_vector}} {{/is_vector}}
{{^is_vector}} {{^is_vector}}
<a class="btn" href="/data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a> <a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
{{/is_vector}} {{/is_vector}}
</div> </div>
</div> </div>
@ -104,7 +104,7 @@
{{/if}} {{/if}}
</section> </section>
<footer> <footer>
<a href="https://www.klokantech.com/" target="_blank"><img src="/images/klokantech.png" /></a> <a href="https://www.klokantech.com/" target="_blank"><img src="{{public_url}}images/klokantech.png" /></a>
<p> <p>
<a href="https://github.com/klokantech/tileserver-gl" target="_blank">Powered by TileServer GL ({{server_version}})</a> <a href="https://www.klokantech.com/" target="_blank">an open-source project from Klokan Technologies GmbH.</a> <img src="https://t.klokantech.com/8073932/19" class="t" /> <a href="https://github.com/klokantech/tileserver-gl" target="_blank">Powered by TileServer GL ({{server_version}})</a> <a href="https://www.klokantech.com/" target="_blank">an open-source project from Klokan Technologies GmbH.</a> <img src="https://t.klokantech.com/8073932/19" class="t" />
</p> </p>

View file

@ -4,11 +4,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{name}} - TileServer GL</title> <title>{{name}} - TileServer GL</title>
<link rel="stylesheet" type="text/css" href="/mapbox-gl.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox-gl.css{{&key_query}}" />
<script src="/mapbox-gl.js{{&key_query}}"></script> <script src="{{public_url}}mapbox-gl.js{{&key_query}}"></script>
<link rel="stylesheet" type="text/css" href="/mapbox.css{{&key_query}}" /> <link rel="stylesheet" type="text/css" href="{{public_url}}mapbox.css{{&key_query}}" />
<script src="/mapbox.js{{&key_query}}"></script> <script src="{{public_url}}mapbox.js{{&key_query}}"></script>
<script src="/leaflet-hash.js{{&key_query}}"></script> <script src="{{public_url}}leaflet-hash.js{{&key_query}}"></script>
<style> <style>
body { margin:0; padding:0; } body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; } #map { position:absolute; top:0; bottom:0; width:100%; }

View file

@ -36,6 +36,11 @@ var opts = require('nomnom')
default: true, default: true,
help: 'Enable Cross-origin resource sharing headers' help: 'Enable Cross-origin resource sharing headers'
}) })
.option('public_url', {
abbr: 'u',
default: '',
help: 'Enable exposing the server on subpaths, not necessarily the root of the domain'
})
.option('verbose', { .option('verbose', {
abbr: 'V', abbr: 'V',
flag: true, flag: true,
@ -54,12 +59,17 @@ var opts = require('nomnom')
console.log('Starting ' + packageJson.name + ' v' + packageJson.version); console.log('Starting ' + packageJson.name + ' v' + packageJson.version);
var startServer = function(configPath, config) { var startServer = function(configPath, config) {
var publicUrl = opts.public_url;
if (publicUrl && publicUrl.lastIndexOf('/') !== publicUrl.length - 1) {
publicUrl += '/';
}
return require('./server')({ return require('./server')({
configPath: configPath, configPath: configPath,
config: config, config: config,
bind: opts.bind, bind: opts.bind,
port: opts.port, port: opts.port,
cors: opts.cors cors: opts.cors,
publicUrl: publicUrl
}); });
}; };

View file

@ -6,8 +6,9 @@ var path = require('path'),
var clone = require('clone'), var clone = require('clone'),
express = require('express'); express = require('express');
var utils = require('./utils');
module.exports = function(options, repo, params, id, reportTiles, reportFont) { module.exports = function(options, repo, params, id, publicUrl, reportTiles, reportFont) {
var app = express().disable('x-powered-by'); var app = express().disable('x-powered-by');
var styleFile = path.resolve(options.paths.styles, params.style); var styleFile = path.resolve(options.paths.styles, params.style);
@ -79,7 +80,7 @@ module.exports = function(options, repo, params, id, reportTiles, reportFont) {
query = '?' + queryParams.join('&'); query = '?' + queryParams.join('&');
} }
return url.replace( return url.replace(
'local://', req.protocol + '://' + req.headers.host + '/') + query; 'local://', utils.getPublicUrl(publicUrl, req)) + query;
}; };
var styleJSON_ = clone(styleJSON); var styleJSON_ = clone(styleJSON);

View file

@ -109,7 +109,7 @@ function start(opts) {
} }
if (item.serve_data !== false) { if (item.serve_data !== false) {
startupPromises.push(serve_style(options, serving.styles, item, id, startupPromises.push(serve_style(options, serving.styles, item, id, opts.publicUrl,
function(mbtiles, fromData) { function(mbtiles, fromData) {
var dataItemId; var dataItemId;
Object.keys(data).forEach(function(id) { Object.keys(data).forEach(function(id) {
@ -194,7 +194,7 @@ function start(opts) {
version: styleJSON.version, version: styleJSON.version,
name: styleJSON.name, name: styleJSON.name,
id: id, id: id,
url: req.protocol + '://' + req.headers.host + url: utils.getPublicUrl(opts.publicUrl, req) +
'/styles/' + id + '/style.json' + query '/styles/' + id + '/style.json' + query
}); });
}); });
@ -260,6 +260,7 @@ function start(opts) {
} }
} }
data['server_version'] = packageJson.name + ' v' + packageJson.version; data['server_version'] = packageJson.name + ' v' + packageJson.version;
data['public_url'] = opts.publicUrl || '/';
data['is_light'] = isLight; data['is_light'] = isLight;
data['key_query_part'] = data['key_query_part'] =
req.query.key ? 'key=' + req.query.key + '&amp;' : ''; req.query.key ? 'key=' + req.query.key + '&amp;' : '';
@ -293,8 +294,8 @@ function start(opts) {
var query = req.query.key ? ('?key=' + req.query.key) : ''; var query = req.query.key ? ('?key=' + req.query.key) : '';
style.wmts_link = 'http://wmts.maptiler.com/' + style.wmts_link = 'http://wmts.maptiler.com/' +
base64url('http://' + req.headers.host + base64url(utils.getPublicUrl(opts.publicUrl, req) +
'/styles/' + id + '.json' + query) + '/wmts'; 'styles/' + id + '.json' + query) + '/wmts';
var tiles = utils.getTileUrls( var tiles = utils.getTileUrls(
req, style.serving_rendered.tiles, req, style.serving_rendered.tiles,
@ -322,8 +323,8 @@ function start(opts) {
var query = req.query.key ? ('?key=' + req.query.key) : ''; var query = req.query.key ? ('?key=' + req.query.key) : '';
data_.wmts_link = 'http://wmts.maptiler.com/' + data_.wmts_link = 'http://wmts.maptiler.com/' +
base64url('http://' + req.headers.host + base64url(utils.getPublicUrl(opts.publicUrl, req) +
'/data/' + id + '.json' + query) + '/wmts'; 'data/' + id + '.json' + query) + '/wmts';
var tiles = utils.getTileUrls( var tiles = utils.getTileUrls(
req, data_.tiles, 'data/' + id, data_.format, { req, data_.tiles, 'data/' + id, data_.format, {

View file

@ -6,6 +6,11 @@ var path = require('path'),
var clone = require('clone'), var clone = require('clone'),
glyphCompose = require('glyph-pbf-composite'); glyphCompose = require('glyph-pbf-composite');
module.exports.getPublicUrl = function(publicUrl, req) {
return publicUrl || (req.protocol + '://' + req.headers.host + '/')
}
module.exports.getTileUrls = function(req, domains, path, format, aliases) { module.exports.getTileUrls = function(req, domains, path, format, aliases) {
if (domains) { if (domains) {

View file

@ -8,7 +8,8 @@ before(function() {
process.chdir('test_data'); process.chdir('test_data');
var running = require('../src/server')({ var running = require('../src/server')({
configPath: 'config.json', configPath: 'config.json',
port: 8888 port: 8888,
publicUrl: '/test/'
}); });
global.app = running.app; global.app = running.app;
global.server = running.server; global.server = running.server;

View file

@ -23,6 +23,7 @@ describe('Styles', function() {
res.body.sources.should.be.Object(); res.body.sources.should.be.Object();
res.body.glyphs.should.be.String(); res.body.glyphs.should.be.String();
res.body.sprite.should.be.String(); res.body.sprite.should.be.String();
res.body.sprite.should.equal('/test/styles/test-style/sprite');
res.body.layers.should.be.Array(); res.body.layers.should.be.Array();
}).end(done); }).end(done);
}); });