mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-05 00:09:32 -04:00
Support for custom URL prefix for landing page
Add a QGIS_SERVER_LANDING_PAGE_PREFIX server setting: Prefix of the path component of the landing page base URL, default is empty (since QGIS 3.20).
This commit is contained in:
parent
497197e316
commit
27bfb09fb8
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=it><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel=icon href=favicon.ico><title>app</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=css/chunk-06c8fa3c.09a55b1d.css rel=prefetch><link href=css/chunk-123bc409.8679d8ba.css rel=prefetch><link href=css/chunk-744799cf.0a230f6c.css rel=prefetch><link href=js/chunk-06c8fa3c.010c72ad.js rel=prefetch><link href=js/chunk-123bc409.ec41f71a.js rel=prefetch><link href=js/chunk-744799cf.70663d3c.js rel=prefetch><link href=css/app.ca3f5643.css rel=preload as=style><link href=css/chunk-vendors.a728f495.css rel=preload as=style><link href=js/app.333c53e0.js rel=preload as=script><link href=js/chunk-vendors.573fc8d0.js rel=preload as=script><link href=css/chunk-vendors.a728f495.css rel=stylesheet><link href=css/app.ca3f5643.css rel=stylesheet></head><body><noscript><strong>We're sorry but app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.573fc8d0.js></script><script src=js/app.333c53e0.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel=icon href=./favicon.ico><title>app</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=css/chunk-06c8fa3c.09a55b1d.css rel=prefetch><link href=css/chunk-123bc409.8679d8ba.css rel=prefetch><link href=css/chunk-a28d6c70.162a27d1.css rel=prefetch><link href=js/chunk-06c8fa3c.010c72ad.js rel=prefetch><link href=js/chunk-123bc409.ec41f71a.js rel=prefetch><link href=js/chunk-a28d6c70.7ed0c6db.js rel=prefetch><link href=css/app.ca3f5643.css rel=preload as=style><link href=css/chunk-vendors.a728f495.css rel=preload as=style><link href=js/app.3a5ac8de.js rel=preload as=script><link href=js/chunk-vendors.573fc8d0.js rel=preload as=script><link href=css/chunk-vendors.a728f495.css rel=stylesheet><link href=css/app.ca3f5643.css rel=stylesheet></head><body><noscript><strong>We're sorry but app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.573fc8d0.js></script><script src=js/app.3a5ac8de.js></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -7,7 +7,7 @@
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<link rel="icon" href="./favicon.ico" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
|
@ -181,7 +181,7 @@ export default {
|
||||
filter = `&${this.filterField.value}=${this.filterText}`;
|
||||
}
|
||||
fetch(
|
||||
`/project/${this.project.id}/wfs3/collections/${this.typename}/items.json?limit=5&offset=${offset}${sorting}${filter}`
|
||||
`./project/${this.project.id}/wfs3/collections/${this.typename}/items.json?limit=5&offset=${offset}${sorting}${filter}`
|
||||
)
|
||||
.then(response => {
|
||||
if (!response) {
|
||||
@ -241,4 +241,4 @@ export default {
|
||||
.btn-close {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -73,7 +73,7 @@ export default new Vuex.Store({
|
||||
actions: {
|
||||
async getCatalog({ commit }) {
|
||||
try {
|
||||
fetch(`/index.json`)
|
||||
fetch(`./index.json`)
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
throw Error(`Error fetching data from QGIS Server`)
|
||||
@ -100,8 +100,7 @@ export default new Vuex.Store({
|
||||
},
|
||||
async getProject({ commit }, projectId) {
|
||||
try {
|
||||
//console.log(`Inside getProject ${projectId}`)
|
||||
fetch(`/map/${projectId}.json`)
|
||||
fetch(`./map/${projectId}.json`)
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
throw Error(`Error fetching data from QGIS Server`)
|
||||
@ -130,7 +129,7 @@ export default new Vuex.Store({
|
||||
* Fetches the TOC style icons from GetLegendGraphics
|
||||
*/
|
||||
async getToc({ commit }, payload) {
|
||||
let toc_url = `/project/${payload.projectId}/?SERVICE=WMS&REQUEST=GetLegendGraphics&LAYERS=${payload.layers}&FORMAT=application/json`
|
||||
let toc_url = `./project/${payload.projectId}/?SERVICE=WMS&REQUEST=GetLegendGraphics&LAYERS=${payload.layers}&FORMAT=application/json`
|
||||
fetch(toc_url)
|
||||
.then(this.handleErrors)
|
||||
.then((response) => response.json())
|
||||
|
@ -313,7 +313,7 @@ export default {
|
||||
this.map.fitBounds(jl.getBounds());
|
||||
}
|
||||
let that = this;
|
||||
this.wms_source = WmsSource.source(`/project/` + project.id + `/?`, {
|
||||
this.wms_source = WmsSource.source(`./project/` + project.id + `/?`, {
|
||||
tileSize: 512,
|
||||
transparent: true,
|
||||
format: "image/png",
|
||||
@ -499,4 +499,4 @@ export default {
|
||||
.expanded-sidebar .leaflet-left {
|
||||
left: 300px !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,7 +1,5 @@
|
||||
module.exports = {
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? './'
|
||||
: '/',
|
||||
publicPath: './',
|
||||
assetsDir: './',
|
||||
configureWebpack: {
|
||||
devtool: "source-map",
|
||||
|
@ -84,18 +84,18 @@ void QgsServerOgcApi::executeRequest( const QgsServerApiContext &context ) const
|
||||
auto path { sanitizeUrl( context.request()->url() ).path() };
|
||||
// Find matching handler
|
||||
auto hasMatch { false };
|
||||
for ( const auto &h : mHandlers )
|
||||
for ( const auto &handler : mHandlers )
|
||||
{
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Checking API path %1 for %2 " ).arg( path, h->path().pattern() ), QStringLiteral( "Server" ), Qgis::Info );
|
||||
if ( h->path().match( path ).hasMatch() )
|
||||
QgsMessageLog::logMessage( QStringLiteral( "Checking API path %1 for %2 " ).arg( path, handler->path().pattern() ), QStringLiteral( "Server" ), Qgis::Info );
|
||||
if ( handler->path().match( path ).hasMatch() )
|
||||
{
|
||||
hasMatch = true;
|
||||
// Execute handler
|
||||
QgsMessageLog::logMessage( QStringLiteral( "API %1: found handler %2" ).arg( name(), QString::fromStdString( h->operationId() ) ), QStringLiteral( "Server" ), Qgis::Info );
|
||||
QgsMessageLog::logMessage( QStringLiteral( "API %1: found handler %2" ).arg( name(), QString::fromStdString( handler->operationId() ) ), QStringLiteral( "Server" ), Qgis::Info );
|
||||
// May throw QgsServerApiBadRequestException or JSON exceptions on serializing
|
||||
try
|
||||
{
|
||||
h->handleRequest( context );
|
||||
handler->handleRequest( context );
|
||||
}
|
||||
catch ( json::exception &ex )
|
||||
{
|
||||
|
@ -46,8 +46,8 @@ class QgsLandingPageApi: public QgsServerOgcApi
|
||||
{
|
||||
QString baseUrlPrefix{ serverIface()->serverSettings()->landingPageBaseUrlPrefix() };
|
||||
|
||||
// Make sure prefix always starts with /
|
||||
if ( ! baseUrlPrefix.startsWith( '/' ) )
|
||||
// Make sure non empty prefix always starts with /
|
||||
if ( ! baseUrlPrefix.isEmpty() && ! baseUrlPrefix.startsWith( '/' ) )
|
||||
{
|
||||
baseUrlPrefix.prepend( '/' );
|
||||
}
|
||||
@ -69,11 +69,11 @@ class QgsLandingPageApi: public QgsServerOgcApi
|
||||
return path.isEmpty()
|
||||
|| path == '/'
|
||||
|| path.startsWith( QLatin1String( "/map/" ) )
|
||||
|| path.startsWith( QLatin1String( "/index" ) )
|
||||
|| path.startsWith( QLatin1String( "/index." ) )
|
||||
// Static
|
||||
|| path.startsWith( QLatin1String( "/css/" ) )
|
||||
|| path.startsWith( QLatin1String( "/js/" ) )
|
||||
|| path == QLatin1String( "/favicon.ico" );
|
||||
|| path == QLatin1String( "favicon.ico" );
|
||||
}
|
||||
|
||||
};
|
||||
@ -100,7 +100,7 @@ class QgsProjectLoaderFilter: public QgsServerFilter
|
||||
{
|
||||
mEnvWasChanged = false;
|
||||
const auto handler { serverInterface()->requestHandler() };
|
||||
if ( handler->path().startsWith( QStringLiteral( "%1project/" ).arg( QgsLandingPageHandler::prefix( serverInterface()->serverSettings() ) ) ) )
|
||||
if ( handler->path().startsWith( QStringLiteral( "%1/project/" ).arg( QgsLandingPageHandler::prefix( serverInterface()->serverSettings() ) ) ) )
|
||||
{
|
||||
const QString projectPath { QgsLandingPageUtils::projectUriFromUrl( handler->url(), *serverInterface()->serverSettings() ) };
|
||||
if ( ! projectPath.isEmpty() )
|
||||
@ -153,7 +153,7 @@ class QgsLandingPageModule: public QgsServiceModule
|
||||
};
|
||||
// Register handlers
|
||||
landingPageApi->registerHandler<QgsServerStaticHandler>(
|
||||
QStringLiteral( "%1(?<staticFilePath>((css|js)/.*)|favicon.ico)$" )
|
||||
QStringLiteral( "%1/(?<staticFilePath>((css|js)/.*)|favicon.ico)$" )
|
||||
.arg( QgsLandingPageHandler::prefix( serverIface->serverSettings() ) ), QStringLiteral( "landingpage" ) );
|
||||
landingPageApi->registerHandler<QgsLandingPageHandler>( serverIface->serverSettings() );
|
||||
landingPageApi->registerHandler<QgsLandingPageMapHandler>( serverIface->serverSettings() );
|
||||
|
@ -35,12 +35,20 @@ QgsLandingPageHandler::QgsLandingPageHandler( const QgsServerSettings *settings
|
||||
|
||||
void QgsLandingPageHandler::handleRequest( const QgsServerApiContext &context ) const
|
||||
{
|
||||
if ( context.request()->url().path( ) == prefix( context.serverInterface()->serverSettings() ) )
|
||||
const QString requestPrefix { prefix( context.serverInterface()->serverSettings() ) };
|
||||
auto urlPath { context.request()->url().path( ) };
|
||||
|
||||
while ( urlPath.endsWith( '/' ) )
|
||||
{
|
||||
urlPath.chop( 1 );
|
||||
}
|
||||
|
||||
if ( urlPath == requestPrefix )
|
||||
{
|
||||
QUrl url { context.request()->url() };
|
||||
url.setPath( QStringLiteral( "%1index.%2" )
|
||||
.arg( prefix( context.serverInterface()->serverSettings() ) )
|
||||
.arg( QgsServerOgcApi::contentTypeToExtension( contentTypeFromRequest( context.request() ) ) ) );
|
||||
url.setPath( QStringLiteral( "%1/index.%2" )
|
||||
.arg( requestPrefix,
|
||||
QgsServerOgcApi::contentTypeToExtension( contentTypeFromRequest( context.request() ) ) ) );
|
||||
context.response()->setStatusCode( 302 );
|
||||
context.response()->setHeader( QStringLiteral( "Location" ), url.toString() );
|
||||
}
|
||||
@ -67,11 +75,13 @@ const QString QgsLandingPageHandler::templatePath( const QgsServerApiContext &co
|
||||
QString QgsLandingPageHandler::prefix( const QgsServerSettings *settings )
|
||||
{
|
||||
QString prefix { settings->landingPageBaseUrlPrefix() };
|
||||
if ( prefix.endsWith( '/' ) )
|
||||
|
||||
while ( prefix.endsWith( '/' ) )
|
||||
{
|
||||
prefix.remove( prefix.length() - 1 );
|
||||
prefix.chop( 1 );
|
||||
}
|
||||
if ( ! prefix.startsWith( '/' ) )
|
||||
|
||||
if ( ! prefix.isEmpty() && ! prefix.startsWith( '/' ) )
|
||||
{
|
||||
prefix.prepend( '/' );
|
||||
}
|
||||
@ -116,8 +126,13 @@ void QgsLandingPageMapHandler::handleRequest( const QgsServerApiContext &context
|
||||
write( data, context, {{ "pageTitle", linkTitle() }, { "navigation", json::array() }} );
|
||||
}
|
||||
|
||||
QRegularExpression QgsLandingPageMapHandler::path() const
|
||||
{
|
||||
return QRegularExpression( QStringLiteral( R"re(^%1/map/([a-f0-9]{32}).*$)re" ).arg( QgsLandingPageHandler::prefix( mSettings ) ) );
|
||||
}
|
||||
|
||||
|
||||
QRegularExpression QgsLandingPageHandler::path() const
|
||||
{
|
||||
return QRegularExpression( QStringLiteral( R"re(^%1(/index.html|/index.json)?$)re" ).arg( prefix( mSettings ) ) );
|
||||
return QRegularExpression( QStringLiteral( R"re(^%1(/index.html|/index.json|/)?$)re" ).arg( prefix( mSettings ) ) );
|
||||
}
|
||||
|
@ -54,8 +54,13 @@ class QgsLandingPageHandler: public QgsServerOgcApiHandler
|
||||
QgsServerOgcApi::Rel linkType() const override { return QgsServerOgcApi::Rel::self; }
|
||||
const QString templatePath( const QgsServerApiContext &context ) const override;
|
||||
|
||||
/**
|
||||
* Returns the path prefix, default is empty. Also makes sure that not-empty
|
||||
* prefix starts with "/" (ex: "/mylandingprefix"
|
||||
*/
|
||||
static QString prefix( const QgsServerSettings *settings );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@ -77,7 +82,7 @@ class QgsLandingPageMapHandler: public QgsServerOgcApiHandler
|
||||
void handleRequest( const QgsServerApiContext &context ) const override;
|
||||
|
||||
// QgsServerOgcApiHandler interface
|
||||
QRegularExpression path() const override { return QRegularExpression( R"re(^/map/([a-f0-9]{32}).*$)re" ); }
|
||||
QRegularExpression path() const override;
|
||||
std::string operationId() const override { return "getMap"; }
|
||||
QStringList tags() const override { return { QStringLiteral( "Catalog" ), QStringLiteral( "Map Viewer" ) }; }
|
||||
std::string summary() const override
|
||||
|
@ -67,13 +67,22 @@ class QgsServerLandingPageTest(QgsServerAPITestBase):
|
||||
"""Setup env"""
|
||||
|
||||
super().setUp()
|
||||
os.environ["QGIS_SERVER_DISABLED_APIS"] = ''
|
||||
try:
|
||||
del (os.environ["QGIS_SERVER_DISABLED_APIS"])
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
del (os.environ['QGIS_SERVER_LANDING_PAGE_PREFIX'])
|
||||
except:
|
||||
pass
|
||||
|
||||
os.environ['QGIS_SERVER_LANDING_PAGE_PROJECTS_DIRECTORIES'] = '||'.join(self.directories)
|
||||
|
||||
if not os.environ.get('TRAVIS', False):
|
||||
os.environ['QGIS_SERVER_LANDING_PAGE_PROJECTS_PG_CONNECTIONS'] = "postgresql://localhost:5432?sslmode=disable&dbname=landing_page_test&schema=public"
|
||||
|
||||
def test_landing_page_redirects(self):
|
||||
def ___test_landing_page_redirects(self):
|
||||
"""Test landing page redirects"""
|
||||
|
||||
request = QgsBufferServerRequest('http://server.qgis.org/')
|
||||
@ -82,6 +91,14 @@ class QgsServerLandingPageTest(QgsServerAPITestBase):
|
||||
self.server.handleRequest(request, response)
|
||||
self.assertEqual(response.headers()[
|
||||
'Location'], 'http://server.qgis.org/index.json')
|
||||
|
||||
request = QgsBufferServerRequest('http://server.qgis.org')
|
||||
request.setHeader('Accept', 'application/json')
|
||||
response = QgsBufferServerResponse()
|
||||
self.server.handleRequest(request, response)
|
||||
self.assertEqual(response.headers()[
|
||||
'Location'], 'http://server.qgis.org/index.json')
|
||||
|
||||
response = QgsBufferServerResponse()
|
||||
request.setHeader('Accept', 'text/html')
|
||||
self.server.handleRequest(request, response)
|
||||
@ -165,6 +182,38 @@ class QgsServerLandingPageTest(QgsServerAPITestBase):
|
||||
self.compareApi(
|
||||
request, None, 'test_landing_page_empty_index.json', subdir='landingpage')
|
||||
|
||||
def test_landing_page_prefix(self):
|
||||
|
||||
os.environ['QGIS_SERVER_LANDING_PAGE_PREFIX'] = '/mylanding'
|
||||
|
||||
def _test_error(uri):
|
||||
request = QgsBufferServerRequest(uri)
|
||||
request.setHeader('Accept', 'application/json')
|
||||
response = QgsBufferServerResponse()
|
||||
self.server.handleRequest(request, response)
|
||||
self.assertEqual(bytes(response.body()), b'<ServerException>Project file error. For OWS services: please provide a SERVICE and a MAP parameter pointing to a valid QGIS project file</ServerException>\n')
|
||||
|
||||
_test_error('http://server.qgis.org/index.json')
|
||||
_test_error('http://server.qgis.org/index.html')
|
||||
_test_error('http://server.qgis.org/')
|
||||
_test_error('http://server.qgis.org')
|
||||
|
||||
def _test_valid(uri):
|
||||
request = QgsBufferServerRequest(uri)
|
||||
request.setHeader('Accept', 'application/json')
|
||||
response = QgsBufferServerResponse()
|
||||
self.server.handleRequest(request, response)
|
||||
if 'index' not in uri:
|
||||
self.assertEqual(response.headers()[
|
||||
'Location'], 'http://server.qgis.org/mylanding/index.json')
|
||||
else:
|
||||
# Just check that it's valid json
|
||||
j = json.loads(bytes(response.body()))
|
||||
|
||||
_test_valid('http://server.qgis.org/mylanding')
|
||||
_test_valid('http://server.qgis.org/mylanding/')
|
||||
_test_valid('http://server.qgis.org/mylanding/index.json')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user