add web UI placeholder (#1395)

Add an optional web UI interface for Martin, including docker-based cross-compilation support.  The UI itself is a placeholder with a logo, but will grow in subsequent PRs.

This was branched off of https://github.com/maplibre/martin/pull/1142 to
address the PR feedback from @nyurik .

---------

Co-authored-by: Yuri Astrakhan <yuriastrakhan@gmail.com>
Co-authored-by: Tomer Ronen <tomer207@gmail.com>
Co-authored-by: tomeronen <45331634+tomeronen@users.noreply.github.com>
This commit is contained in:
paigewilliams 2024-07-14 10:39:29 -07:00 committed by GitHub
parent adb9a4aa86
commit 7f18b6bbf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 3786 additions and 31 deletions

View File

@ -142,6 +142,9 @@ jobs:
for target in "aarch64-unknown-linux-musl" "x86_64-unknown-linux-musl"; do
echo -e "\n----------------------------------------------"
echo "Building $target"
# See https://github.com/cross-rs/cross/issues/1526
# TODO: Remove this once a version after cross 0.2.5 is released
export CROSS_BUILD_OPTS="--output=type=docker"
export "CARGO_TARGET_$(echo $target | tr 'a-z-' 'A-Z_')_RUSTFLAGS"='-C strip=debuginfo'
cross build --release --target $target --workspace
mkdir -p target_releases/$target

69
Cargo.lock generated
View File

@ -200,6 +200,18 @@ dependencies = [
"syn 2.0.71",
]
[[package]]
name = "actix-web-static-files"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adf6d1ef6d7a60e084f9e0595e2a5234abda14e76c105ecf8e2d0e8800c41a1f"
dependencies = [
"actix-web",
"derive_more",
"futures-util",
"static-files",
]
[[package]]
name = "addr2line"
version = "0.22.0"
@ -696,6 +708,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "change-detection"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "159fa412eae48a1d94d0b9ecdb85c97ce56eb2a347c62394d3fdbf221adabc1a"
dependencies = [
"path-matchers",
"path-slash",
]
[[package]]
name = "chrono"
version = "0.4.38"
@ -2344,6 +2366,7 @@ dependencies = [
"actix-http",
"actix-rt",
"actix-web",
"actix-web-static-files",
"async-trait",
"bit-set",
"brotli 6.0.0",
@ -2382,6 +2405,7 @@ dependencies = [
"serde_with",
"serde_yaml",
"spreet",
"static-files",
"subst",
"thiserror",
"tilejson",
@ -2489,6 +2513,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -2802,6 +2836,21 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "path-matchers"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36cd9b72a47679ec193a5f0229d9ab686b7bd45e1fbc59ccf953c9f3d83f7b2b"
dependencies = [
"glob",
]
[[package]]
name = "path-slash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696"
[[package]]
name = "pbf_font_tools"
version = "2.5.1"
@ -4270,6 +4319,17 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static-files"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8590e848e1c53be9258210bcd4a8f4118e08988f03a4e2d63b62e4ad9f7ced"
dependencies = [
"change-detection",
"mime_guess",
"path-slash",
]
[[package]]
name = "str_stack"
version = "0.1.0"
@ -4757,6 +4817,15 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.15"

View File

@ -29,6 +29,7 @@ actix-cors = "0.7"
actix-http = "3"
actix-rt = "2"
actix-web = "4"
actix-web-static-files = "4"
anyhow = "1.0"
approx = "0.5.1"
async-trait = "0.1"
@ -80,6 +81,7 @@ spreet = { version = "0.11", default-features = false }
sqlite-compressions = { version = "0.2.12", default-features = false, features = ["bsdiffraw", "gzip"] }
sqlite-hashes = { version = "0.7.3", default-features = false, features = ["md5", "aggregate", "hex"] }
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio"] }
static-files = "0.2"
subst = { version = "0.3", features = ["yaml"] }
thiserror = "1"
tile-grid = "0.6"

13
Cross.toml Normal file
View File

@ -0,0 +1,13 @@
[build]
pre-build = [
# install nodejs and npm to compile static web resources
# note that architecture could be the same as the container, not $CROSS_DEB_ARCH
# Need to use a fairly old Node.js version to support the old underlying docker image
"curl -fsSL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh",
"bash nodesource_setup.sh",
"apt-get install -y nodejs",
"node -v",
# Using old NPM, so must ensure correct access
"mkdir -p /.npm",
"chown -R 1001:127 /.npm",
]

View File

@ -38,6 +38,9 @@ cache_size_mb: 1024
# If the client accepts multiple compression formats, and the tile source is not pre-compressed, which compression should be used. `gzip` is faster, but `brotli` is smaller, and may be faster with caching. Default could be different depending on Martin version.
preferred_encoding: gzip
# Enable or disable Martin web UI. At the moment, only allows `enable-for-all` which enables the web UI for all connections. This may be undesirable in a production environment. [default: disable]
web_ui: disable
# Database configuration. This can also be a list of PG configs.
postgres:
# Database connection string. You can use env vars too, for example:

View File

@ -17,6 +17,9 @@ Options:
--save-config <SAVE_CONFIG>
Save resulting config to a file or use "-" to print to stdout. By default, only print if sources are auto-detected
-C, --cache-size <CACHE_SIZE>
Main cache size (in MB)
-s, --sprite <SPRITE>
Export a directory with SVG files as a sprite source. Can be specified multiple times
@ -28,6 +31,7 @@ Options:
-l, --listen-addresses <LISTEN_ADDRESSES>
The socket address to bind. [DEFAULT: 0.0.0.0:3000]
--base-path <BASE_PATH>
Set TileJSON URL path prefix, ignoring X-Rewrite-URL header. Must begin with a `/`. Examples: `/`, `/tiles`
@ -35,10 +39,17 @@ Options:
Number of web server workers
--preferred-encoding <PREFERRED_ENCODING>
Martin server preferred tile encoding. If the client accepts multiple compression formats, and the tile source is not pre-compressed, which compression should be used. `gzip` is faster, but `brotli` is smaller, and may be faster with caching. Default could be different depending on Martin version
Martin server preferred tile encoding. If the client accepts multiple compression formats, and the tile source is not pre-compressed, which compression should be used. `gzip` is faster, but `brotli` is smaller, and may be faster with caching. Defaults to gzip
[possible values: brotli, gzip]
-u, --webui <WEB_UI>
Control Martin web UI. Disabled by default
Possible values:
- disable: Disable Web UI interface. This is the default, but once implemented, the default will be enabled for localhost
- enable-for-all: Enable Web UI interface on all connections
-b, --auto-bounds <AUTO_BOUNDS>
Specify how bounds should be computed for the spatial PG tables. [DEFAULT: quick]

View File

@ -4,7 +4,7 @@ Martin data is available via the HTTP `GET` endpoints:
| URL | Description |
|-----------------------------------------|------------------------------------------------|
| `/` | Status text, that will eventually show web UI |
| `/` | Web UI |
| `/catalog` | [List of all sources](#catalog) |
| `/{sourceID}` | [Source TileJSON](#source-tilejson) |
| `/{sourceID}/{z}/{x}/{y}` | Map Tiles |

View File

@ -21,7 +21,7 @@ dockercompose := `if docker-compose --version &> /dev/null; then echo "docker-co
{{ just_executable() }} --list --unsorted
# Start Martin server
run *ARGS:
run *ARGS="--webui enable-for-all":
cargo run -p martin -- {{ ARGS }}
# Start Martin server
@ -50,9 +50,13 @@ pg_dump *ARGS:
pg_dump {{ ARGS }} {{ quote(DATABASE_URL) }}
# Perform cargo clean to delete all build files
clean: clean-test stop
clean: clean-test stop && clean-martin-ui
cargo clean
clean-martin-ui:
rm -rf martin-ui/dist martin-ui/node_modules
cargo clean -p static-files
# Delete test output files
[private]
clean-test:

18
martin-ui/.eslintrc.cjs Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

12
martin-ui/README.md Normal file
View File

@ -0,0 +1,12 @@
# Martin Web UI
A web interface for previewing tiles served by Martin.
### Run locally
To run just the web interface
```bash
npm i
npm run dev
```

24
martin-ui/index.html Normal file
View File

@ -0,0 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" type="image/x-icon" href="/_/assets/favicon.ico">
<meta property="og:url" content="https://maplibre.org"/>
<meta property="og:title" content="Martin — vector tiles server"/>
<meta property="og:description"
content="Blazing fast and lightweight PostGIS, MBtiles and PMtiles tile server, tile generation, and mbtiles tooling."/>
<meta property="og:image" content="/martin.png"/>
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/_/assets/favicons/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<title>Martin — vector tiles server</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

3383
martin-ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
martin-ui/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "martin-ui",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.8"
},
"devDependencies": {
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.0.8"
},
"engines": {
"node": ">=18.20.3",
"npm": ">=10.7.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

1
martin-ui/src/App.css Normal file
View File

@ -0,0 +1 @@
#root {}

18
martin-ui/src/App.tsx Normal file
View File

@ -0,0 +1,18 @@
import martinCover from './assets/logo.png';
import './App.css';
import styled from 'styled-components';
const CoverImage = styled.img`
width: 100%;
height: 100%;
`;
function App() {
return (
<div>
<CoverImage src={martinCover} />
</div>
);
}
export default App;

View File

@ -0,0 +1 @@
../../../logo.png

10
martin-ui/src/index.css Normal file
View File

@ -0,0 +1,10 @@
body {
margin: 0;
padding: 0;
}
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: dark light;
}

10
martin-ui/src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

1
martin-ui/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

25
martin-ui/tsconfig.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

12
martin-ui/vite.config.ts Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
// assets can also be the name of a tile source
// so we use /_/assets to avoid conflicts
assetsDir: '_/assets'
},
});

View File

@ -2,7 +2,7 @@ lints.workspace = true
[package]
name = "martin"
version = "0.14.2"
version = "0.15.0"
authors = ["Stepan Kuzmin <to.stepan.kuzmin@gmail.com>", "Yuri Astrakhan <YuriAstrakhan@gmail.com>", "MapLibre contributors"]
description = "Blazing fast and lightweight tile server with PostGIS, MBTiles, and PMTiles support"
keywords = ["maps", "tiles", "mbtiles", "pmtiles", "postgis"]
@ -59,7 +59,8 @@ name = "bench"
harness = false
[features]
default = ["fonts", "lambda", "mbtiles", "pmtiles", "postgres", "sprites"]
default = ["webui", "fonts", "lambda", "mbtiles", "pmtiles", "postgres", "sprites"]
webui = ["dep:actix-web-static-files", "dep:static-files"]
fonts = ["dep:bit-set", "dep:pbf_font_tools"]
lambda = ["dep:lambda-web"]
mbtiles = ["dep:mbtiles"]
@ -72,6 +73,7 @@ bless-tests = []
actix-cors.workspace = true
actix-http.workspace = true
actix-rt.workspace = true
actix-web-static-files = { workspace = true, optional = true }
actix-web.workspace = true
async-trait.workspace = true
bit-set = { workspace = true, optional = true }
@ -104,6 +106,7 @@ serde_json.workspace = true
serde_with.workspace = true
serde_yaml.workspace = true
spreet = { workspace = true, optional = true }
static-files = { workspace = true, optional = true }
subst.workspace = true
thiserror.workspace = true
tilejson.workspace = true
@ -111,6 +114,9 @@ tokio = { workspace = true, features = ["io-std"] }
tokio-postgres-rustls = { workspace = true, optional = true }
url.workspace = true
[build-dependencies]
static-files = { workspace = true, optional = true }
[dev-dependencies]
cargo-husky.workspace = true
criterion.workspace = true

13
martin/build.rs Normal file
View File

@ -0,0 +1,13 @@
fn main() -> std::io::Result<()> {
#[cfg(feature = "webui")]
{
static_files::NpmBuild::new("../martin-ui")
.install()?
.run("build")?
.target("../martin-ui/dist")
.change_detection()
.to_resource_dir()
.build()?;
}
Ok(())
}

View File

@ -13,4 +13,4 @@ mod root;
pub use root::{Args, ExtraArgs, MetaArgs};
mod srv;
pub use srv::{PreferredEncoding, SrvArgs};
pub use srv::{PreferredEncoding, SrvArgs, WebUiMode};

View File

@ -20,6 +20,25 @@ pub struct SrvArgs {
/// Martin server preferred tile encoding. If the client accepts multiple compression formats, and the tile source is not pre-compressed, which compression should be used. `gzip` is faster, but `brotli` is smaller, and may be faster with caching. Defaults to gzip.
#[arg(long)]
pub preferred_encoding: Option<PreferredEncoding>,
/// Control Martin web UI. Disabled by default.
#[arg(short = 'u', long = "webui")]
#[cfg(feature = "webui")]
pub web_ui: Option<WebUiMode>,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Default, Serialize, Deserialize, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum WebUiMode {
/// Disable Web UI interface. This is the default, but once implemented, the default will be enabled for localhost.
#[default]
#[serde(alias = "false")]
Disable,
// /// Enable Web UI interface on connections from the localhost
// #[default]
// #[serde(alias = "true")]
// Enable,
/// Enable Web UI interface on all connections
EnableForAll,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize, ValueEnum)]
@ -40,14 +59,18 @@ impl SrvArgs {
if self.listen_addresses.is_some() {
srv_config.listen_addresses = self.listen_addresses;
}
if self.base_path.is_some() {
srv_config.base_path = self.base_path;
}
if self.workers.is_some() {
srv_config.worker_processes = self.workers;
}
if self.preferred_encoding.is_some() {
srv_config.preferred_encoding = self.preferred_encoding;
}
if self.base_path.is_some() {
srv_config.base_path = self.base_path;
#[cfg(feature = "webui")]
if self.web_ui.is_some() {
srv_config.web_ui = self.web_ui;
}
}
}

View File

@ -29,9 +29,22 @@ async fn start(args: Args) -> MartinResult<()> {
info!("Use --save-config to save or print Martin configuration.");
}
#[cfg(feature = "webui")]
let web_ui_mode = config.srv.web_ui.unwrap_or_default();
let (server, listen_addresses) = new_server(config.srv, sources)?;
info!("Martin has been started on {listen_addresses}.");
info!("Use http://{listen_addresses}/catalog to get the list of available sources.");
#[cfg(feature = "webui")]
if web_ui_mode == martin::args::WebUiMode::EnableForAll {
log::warn!("Web UI is enabled for all connections at http://{listen_addresses}/");
} else {
info!(
"Web UI is disabled. Use `--webui enable-for-all` in CLI or a config value to enable it for all connections."
);
}
server.await
}

View File

@ -13,6 +13,8 @@ pub struct SrvConfig {
pub base_path: Option<String>,
pub worker_processes: Option<usize>,
pub preferred_encoding: Option<PreferredEncoding>,
#[cfg(feature = "webui")]
pub web_ui: Option<crate::args::WebUiMode>,
}
#[cfg(test)]
@ -35,8 +37,7 @@ mod tests {
keep_alive: Some(75),
listen_addresses: some("0.0.0.0:3000"),
worker_processes: Some(8),
preferred_encoding: None,
base_path: None,
..Default::default()
}
);
assert_eq!(
@ -52,7 +53,7 @@ mod tests {
listen_addresses: some("0.0.0.0:3000"),
worker_processes: Some(8),
preferred_encoding: Some(PreferredEncoding::Brotli),
base_path: None
..Default::default()
}
);
assert_eq!(
@ -68,7 +69,7 @@ mod tests {
listen_addresses: some("0.0.0.0:3000"),
worker_processes: Some(8),
preferred_encoding: Some(PreferredEncoding::Brotli),
base_path: None,
..Default::default()
}
);
}

View File

@ -3,6 +3,13 @@ use std::pin::Pin;
use std::string::ToString;
use std::time::Duration;
use crate::config::ServerState;
use crate::source::TileCatalog;
use crate::srv::config::{SrvConfig, KEEP_ALIVE_DEFAULT, LISTEN_ADDRESSES_DEFAULT};
use crate::srv::tiles::get_tile;
use crate::srv::tiles_info::get_source_info;
use crate::MartinError::BindingError;
use crate::MartinResult;
use actix_cors::Cors;
use actix_web::error::ErrorInternalServerError;
use actix_web::http::header::CACHE_CONTROL;
@ -15,13 +22,15 @@ use lambda_web::{is_running_on_lambda, run_actix_on_lambda};
use log::error;
use serde::{Deserialize, Serialize};
use crate::config::ServerState;
use crate::source::TileCatalog;
use crate::srv::config::{SrvConfig, KEEP_ALIVE_DEFAULT, LISTEN_ADDRESSES_DEFAULT};
use crate::srv::tiles::get_tile;
use crate::srv::tiles_info::get_source_info;
use crate::MartinError::BindingError;
use crate::MartinResult;
#[cfg(feature = "webui")]
use crate::args::WebUiMode;
#[cfg(feature = "webui")]
mod webui {
#![allow(clippy::unreadable_literal)]
#![allow(clippy::wildcard_imports)]
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
}
/// List of keywords that cannot be used as source IDs. Some of these are reserved for future use.
/// Reserved keywords must never end in a "dot number" (e.g. ".1").
@ -57,12 +66,23 @@ pub fn map_internal_error<T: std::fmt::Display>(e: T) -> actix_web::Error {
ErrorInternalServerError(e.to_string())
}
/// Root path will eventually have a web front. For now, just a stub.
/// Root path in case web front is disabled.
#[cfg(not(feature = "webui"))]
#[route("/", method = "GET", method = "HEAD")]
#[allow(clippy::unused_async)]
async fn get_index() -> &'static str {
// todo: once this becomes more substantial, add wrap = "middleware::Compress::default()"
"Martin server is running. Eventually this will be a nice web front.\n\n\
async fn get_index_no_ui() -> &'static str {
"Martin server is running. The WebUI feature was disabled at the compile time.\n\n\
A list of all available sources is at /catalog\n\n\
See documentation https://github.com/maplibre/martin"
}
/// Root path in case web front is disabled and the WebUI feature is enabled.
#[cfg(feature = "webui")]
#[route("/", method = "GET", method = "HEAD")]
#[allow(clippy::unused_async)]
async fn get_index_ui_disabled() -> &'static str {
"Martin server is running.\n\n
The WebUI feature can be enabled with the --webui enable-for-all CLI flag or in the config file, making it available to all users.\n\n
A list of all available sources is at /catalog\n\n\
See documentation https://github.com/maplibre/martin"
}
@ -87,9 +107,8 @@ async fn get_catalog(catalog: Data<Catalog>) -> impl Responder {
HttpResponse::Ok().json(catalog)
}
pub fn router(cfg: &mut web::ServiceConfig) {
pub fn router(cfg: &mut web::ServiceConfig, #[allow(unused_variables)] usr_cfg: &SrvConfig) {
cfg.service(get_health)
.service(get_index)
.service(get_catalog)
.service(get_source_info)
.service(get_tile);
@ -100,6 +119,23 @@ pub fn router(cfg: &mut web::ServiceConfig) {
#[cfg(feature = "fonts")]
cfg.service(crate::srv::fonts::get_font);
#[cfg(feature = "webui")]
{
// TODO: this can probably be simplified with a wrapping middleware,
// which would share usr_cfg from Data<> with all routes.
if usr_cfg.web_ui.unwrap_or_default() == WebUiMode::EnableForAll {
cfg.service(actix_web_static_files::ResourceFiles::new(
"/",
webui::generate(),
));
} else {
cfg.service(get_index_ui_disabled);
}
}
#[cfg(not(feature = "webui"))]
cfg.service(get_index_no_ui);
}
type Server = Pin<Box<dyn Future<Output = MartinResult<()>>>>;
@ -135,7 +171,7 @@ pub fn new_server(config: SrvConfig, state: ServerState) -> MartinResult<(Server
.wrap(cors_middleware)
.wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))
.wrap(middleware::Logger::default())
.configure(router)
.configure(|c| router(c, &config))
};
#[cfg(feature = "lambda")]

View File

@ -26,7 +26,7 @@ macro_rules! create_app {
.app_data(actix_web::web::Data::new(::martin::NO_MAIN_CACHE))
.app_data(actix_web::web::Data::new(state.tiles))
.app_data(actix_web::web::Data::new(SrvConfig::default()))
.configure(::martin::srv::router),
.configure(|c| ::martin::srv::router(c, &SrvConfig::default())),
)
.await
}};

View File

@ -30,7 +30,7 @@ macro_rules! create_app {
.app_data(actix_web::web::Data::new(::martin::NO_MAIN_CACHE))
.app_data(actix_web::web::Data::new(state.tiles))
.app_data(actix_web::web::Data::new(SrvConfig::default()))
.configure(::martin::srv::router),
.configure(|c| ::martin::srv::router(c, &SrvConfig::default())),
)
.await
}};
@ -1105,7 +1105,7 @@ tables:
.app_data(actix_web::web::Data::new(::martin::NO_MAIN_CACHE))
.app_data(actix_web::web::Data::new(state.tiles))
.app_data(actix_web::web::Data::new(SrvConfig::default()))
.configure(::martin::srv::router),
.configure(|c| ::martin::srv::router(c, &SrvConfig::default())),
)
.await;

View File

@ -26,7 +26,7 @@ macro_rules! create_app {
.app_data(actix_web::web::Data::new(::martin::NO_MAIN_CACHE))
.app_data(actix_web::web::Data::new(state.tiles))
.app_data(actix_web::web::Data::new(SrvConfig::default()))
.configure(::martin::srv::router),
.configure(|c| ::martin::srv::router(c, &SrvConfig::default())),
)
.await
}};