Allow specifying a custom app name, calculate base URL for opensearch.xml

pull/256/head
Candid Dauth 2024-02-17 04:57:21 +01:00
rodzic 485aa98e2b
commit aa4b82a69f
21 zmienionych plików z 114 dodań i 31 usunięć

Wyświetl plik

@ -1,6 +1,15 @@
# HTTP requests made by the backend will send this User-Agent header. Please adapt to your URL and e-mail address. # HTTP requests made by the backend will send this User-Agent header. Please adapt to your URL and e-mail address.
#USER_AGENT=FacilMap (https://facilmap.org/, cdauth@cdauth.eu) #USER_AGENT=FacilMap (https://facilmap.org/, cdauth@cdauth.eu)
# Whether to trust the X-Forwarded-* headers. Can be "true" or a comma-separated list of IP subnets.
# See https://expressjs.com/en/guide/behind-proxies.html for details.
#TRUST_PROXY=true
# Alternatively, manually set the base URL where FacilMap will be publicly reachable.
#BASE_URL=https://facilmap.org/
# The name of the app that should be displayed throughout the UI.
#APP_NAME=FacilMap
# On which IP the HTTP server will listen. Leave empty to listen to all IPs. # On which IP the HTTP server will listen. Leave empty to listen to all IPs.
#HOST= #HOST=
# On which port the HTTP server will listen. # On which port the HTTP server will listen.

Wyświetl plik

@ -5,6 +5,9 @@ The config of the FacilMap server can be set either by using environment variabl
| Variable | Required | Default | Meaning | | Variable | Required | Default | Meaning |
|-----------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------| |-----------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------|
| `USER_AGENT` | * | | Will be used for all HTTP requests (search, routing, GPX/KML/OSM/GeoJSON files). You better provide your e-mail address in here. | | `USER_AGENT` | * | | Will be used for all HTTP requests (search, routing, GPX/KML/OSM/GeoJSON files). You better provide your e-mail address in here. |
| `APP_NAME` | | | If specified, will replace “FacilMap” as the name of the app throughout the UI. |
| `TRUST_PROXY` | | | Whether to trust the X-Forwarded-* headers. Can be `true` or a comma-separated list of IP subnets (see the [express documentation](https://expressjs.com/en/guide/behind-proxies.html)). Currently only used to calculate the base URL for the `opensearch.xml` file. |
| `BASE_URL` | | | If `TRUST_PROXY` does not work for your particular setup, you can manually specify the base URL where FacilMap can be publicly reached here. |
| `HOST` | | | The ip address to listen on (leave empty to listen on all addresses) | | `HOST` | | | The ip address to listen on (leave empty to listen on all addresses) |
| `PORT` | | `8080` | The port to listen on. | | `PORT` | | `8080` | The port to listen on. |
| `DB_TYPE` | | `mysql` | The type of database. Either `mysql`, `postgres`, `mariadb`, `sqlite`, or `mssql`. | | `DB_TYPE` | | `mysql` | The type of database. Either `mysql`, `postgres`, `mariadb`, `sqlite`, or `mssql`. |
@ -18,8 +21,10 @@ The config of the FacilMap server can be set either by using environment variabl
| `MAPZEN_TOKEN` | | | [Mapzen API key](https://mapzen.com/developers/sign_up). | | `MAPZEN_TOKEN` | | | [Mapzen API key](https://mapzen.com/developers/sign_up). |
| `MAXMIND_USER_ID` | | | [MaxMind user ID](https://www.maxmind.com/en/geolite2/signup). | | `MAXMIND_USER_ID` | | | [MaxMind user ID](https://www.maxmind.com/en/geolite2/signup). |
| `MAXMIND_LICENSE_KEY` | | | MaxMind license key. | | `MAXMIND_LICENSE_KEY` | | | MaxMind license key. |
| `LIMA_LABS_TOKEN` | | | [Lima Labs](https://maps.lima-labs.com/) API key
FacilMap makes use of several third-party services that require you to register (for free) and generate an API key: FacilMap makes use of several third-party services that require you to register (for free) and generate an API key:
* Mapbox and OpenRouteService are used for calculating routes. Mapbox is used for basic routes, OpenRouteService is used when custom route mode settings are made. If these API keys are not defined, calculating routes will fail. * Mapbox and OpenRouteService are used for calculating routes. Mapbox is used for basic routes, OpenRouteService is used when custom route mode settings are made. If these API keys are not defined, calculating routes will fail.
* Maxmind provides a free database that maps IP addresses to approximate locations. FacilMap downloads this database to decide the initial map view for users (IP addresses are looked up in FacilMaps copy of the database, on IP addresses are sent to Maxmind). This API key is optional, if it is not set, the default view will be the whole world. * Maxmind provides a free database that maps IP addresses to approximate locations. FacilMap downloads this database to decide the initial map view for users (IP addresses are looked up in FacilMaps copy of the database, on IP addresses are sent to Maxmind). This API key is optional, if it is not set, the default view will be the whole world.
* Mapzen is used to look up the elevation info for search results. The API key is optional, if it is not set, no elevation info will be available for search results. * Mapzen is used to look up the elevation info for search results. The API key is optional, if it is not set, no elevation info will be available for search results.
* Lima Labs is used for nicer and higher resolution map tiles than Mapnik. The API key is optional, if it is not set, Mapnik will be the default map style instead.

Wyświetl plik

@ -23,6 +23,7 @@ services:
- db - db
environment: environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org) USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org)
TRUST_PROXY: "true"
DB_TYPE: mysql DB_TYPE: mysql
DB_HOST: db DB_HOST: db
DB_NAME: facilmap DB_NAME: facilmap
@ -59,6 +60,7 @@ services:
- db - db
environment: environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org) USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org)
TRUST_PROXY: "true"
DB_TYPE: postgres DB_TYPE: postgres
DB_HOST: db DB_HOST: db
DB_NAME: facilmap DB_NAME: facilmap
@ -88,5 +90,5 @@ To manually create the necessary docker containers, use these commands:
```bash ```bash
docker create --name=facilmap_db -e MYSQL_DATABASE=facilmap -e MYSQL_USER=facilmap -e MYSQL_PASSWORD=password -e MYSQL_RANDOM_ROOT_PASSWORD=true --restart=unless-stopped mariadb --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci docker create --name=facilmap_db -e MYSQL_DATABASE=facilmap -e MYSQL_USER=facilmap -e MYSQL_PASSWORD=password -e MYSQL_RANDOM_ROOT_PASSWORD=true --restart=unless-stopped mariadb --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker create --link=facilmap_db -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e DB_TYPE=mysql -e DB_HOST=facilmap_db -e DB_NAME=facilmap -e DB_USER=facilmap -e DB_PASSWORD=facilmap -e ORS_TOKEN= -e MAPBOX_TOKEN= -e MAPZEN_TOKEN= -e MAXMIND_USER_ID= -e MAXMIND_LICENSE_KEY= -e LIMA_LABS_TOKEN= --restart=unless-stopped facilmap/facilmap docker create --link=facilmap_db -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e TRUST_PROXY=true -e DB_TYPE=mysql -e DB_HOST=facilmap_db -e DB_NAME=facilmap -e DB_USER=facilmap -e DB_PASSWORD=facilmap -e ORS_TOKEN= -e MAPBOX_TOKEN= -e MAPZEN_TOKEN= -e MAXMIND_USER_ID= -e MAXMIND_LICENSE_KEY= -e LIMA_LABS_TOKEN= --restart=unless-stopped facilmap/facilmap
``` ```

4
frontend/build.d.ts vendored
Wyświetl plik

@ -8,7 +8,9 @@ export const paths: {
mapEjs: string; mapEjs: string;
tableEntry: string; tableEntry: string;
tableEjs: string; tableEjs: string;
manifest: string; viteManifest: string;
pwaManifest: string;
opensearchXmlEjs: string;
}; };
export function serve(inlineConfig?: InlineConfig): Promise<ViteDevServer>; export function serve(inlineConfig?: InlineConfig): Promise<ViteDevServer>;

Wyświetl plik

@ -13,7 +13,9 @@ export const paths = {
mapEjs: `${root}/src/map/map.ejs`, mapEjs: `${root}/src/map/map.ejs`,
tableEntry: "src/table/table.ts", tableEntry: "src/table/table.ts",
tableEjs: `${root}/src/table/table.ejs`, tableEjs: `${root}/src/table/table.ejs`,
manifest: `${dist}/.vite/manifest.json`, viteManifest: `${dist}/.vite/manifest.json`,
pwaManifest: `${root}/src/manifest.json`,
opensearchXmlEjs: `${root}/src/opensearch.xml.ejs`,
}; };
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types

Wyświetl plik

@ -36,10 +36,13 @@
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
const props = defineProps<{ const props = withDefaults(defineProps<{
baseUrl: string; baseUrl: string;
settings?: Partial<FacilMapSettings> appName?: string;
}>(); settings?: Partial<FacilMapSettings>;
}>(), {
appName: "FacilMap"
});
const isNarrow = useMaxBreakpoint("sm"); const isNarrow = useMaxBreakpoint("sm");
@ -62,6 +65,7 @@
const context: FacilMapContext = shallowReadonly(reactive({ const context: FacilMapContext = shallowReadonly(reactive({
id: idCounter++, id: idCounter++,
baseUrl: toRef(() => props.baseUrl), baseUrl: toRef(() => props.baseUrl),
appName: toRef(() => props.appName),
isNarrow, isNarrow,
settings: readonly(toRef(() => ({ settings: readonly(toRef(() => ({
toolbox: true, toolbox: true,

Wyświetl plik

@ -30,6 +30,7 @@ export interface FacilMapComponents {
export interface WritableFacilMapContext { export interface WritableFacilMapContext {
id: number; id: number;
baseUrl: string; baseUrl: string;
appName: string;
isNarrow: boolean; isNarrow: boolean;
settings: FacilMapSettings; settings: FacilMapSettings;
components: FacilMapComponents; components: FacilMapComponents;

Wyświetl plik

@ -21,6 +21,7 @@
baseUrl: string; baseUrl: string;
serverUrl: string; serverUrl: string;
padId: string | undefined; padId: string | undefined;
appName?: string;
settings?: Partial<FacilMapSettings>; settings?: Partial<FacilMapSettings>;
}>(); }>();
@ -61,6 +62,7 @@
<div class="fm-facilmap"> <div class="fm-facilmap">
<FacilMapContextProvider <FacilMapContextProvider
:baseUrl="props.baseUrl" :baseUrl="props.baseUrl"
:appName="props.appName"
:settings="props.settings" :settings="props.settings"
ref="contextRef" ref="contextRef"
> >

Wyświetl plik

@ -57,7 +57,7 @@
:href="selfUrl" :href="selfUrl"
target="_blank" target="_blank"
class="fm-open-external" class="fm-open-external"
v-tooltip.right="'Open FacilMap in full size'" v-tooltip.right="`Open ${context.appName} in full size`"
></a> ></a>
<div class="fm-logo"> <div class="fm-logo">
<img src="./logo.png"/> <img src="./logo.png"/>

Wyświetl plik

@ -76,7 +76,7 @@
function copyEmbedCode(): void { function copyEmbedCode(): void {
copyToClipboard(embedCode.value); copyToClipboard(embedCode.value);
toasts.showToast(undefined, "Embed code copied", "The code to embed FacilMap was copied to the clipboard.", { variant: "success", autoHide: true }); toasts.showToast(undefined, "Embed code copied", `The code to embed ${context.appName} was copied to the clipboard.`, { variant: "success", autoHide: true });
} }
</script> </script>
@ -185,7 +185,7 @@
<textarea class="form-control" :value="embedCode" readonly></textarea> <textarea class="form-control" :value="embedCode" readonly></textarea>
<button type="button" class="btn btn-secondary" @click="copyEmbedCode()">Copy</button> <button type="button" class="btn btn-secondary" @click="copyEmbedCode()">Copy</button>
</div> </div>
<p class="mt-2">Add this HTML code to a web page to embed FacilMap. <a href="https://docs.facilmap.org/developers/embed.html" target="_blank">Learn more</a></p> <p class="mt-2">Add this HTML code to a web page to embed {{context.appName}}. <a href="https://docs.facilmap.org/developers/embed.html" target="_blank">Learn more</a></p>
</template> </template>
</ModalDialog> </ModalDialog>
</template> </template>

Wyświetl plik

@ -2,6 +2,9 @@
import AboutDialog from "../about-dialog.vue"; import AboutDialog from "../about-dialog.vue";
import { ref } from "vue"; import { ref } from "vue";
import DropdownMenu from "../ui/dropdown-menu.vue"; import DropdownMenu from "../ui/dropdown-menu.vue";
import { injectContextRequired } from "../facil-map-context-provider/facil-map-context-provider.vue";
const context = injectContextRequired();
const emit = defineEmits<{ const emit = defineEmits<{
"hide-sidebar": []; "hide-sidebar": [];
@ -63,7 +66,7 @@
@click="dialog = 'about'; emit('hide-sidebar')" @click="dialog = 'about'; emit('hide-sidebar')"
href="javascript:" href="javascript:"
draggable="false" draggable="false"
>About FacilMap</a> >About {{context.appName}}</a>
</li> </li>
</DropdownMenu> </DropdownMenu>

Wyświetl plik

@ -1,13 +1,13 @@
{ {
"name": "FacilMap", "name": "%APP_NAME%",
"short_name": "FacilMap", "short_name": "%APP_NAME%",
"icons": [{ "icons": [{
"src": "./app-512.png", "src": "./static/app-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
}], }],
"background_color": "#ffffff", "background_color": "#ffffff",
"theme_color": "#ffffff", "theme_color": "#ffffff",
"display": "standalone", "display": "standalone",
"start_url": "../../" "start_url": "../"
} }

Wyświetl plik

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title><%= padData && padData.name ? `${padData.name} – ` : ''%>FacilMap</title> <title><%= padData && padData.name ? `${padData.name} – ` : ''%><%=appName%></title>
<meta name="description" content="<%= padData && padData.description || "A fully-featured OpenStreetMap-based map where markers and lines can be added with live collaboration." %>" /> <meta name="description" content="<%= padData && padData.description || "A fully-featured OpenStreetMap-based map where markers and lines can be added with live collaboration." %>" />
<% if(!padData || (isReadOnly && padData.searchEngines)) { -%> <% if(!padData || (isReadOnly && padData.searchEngines)) { -%>
<meta name="robots" content="index,nofollow" /> <meta name="robots" content="index,nofollow" />
@ -14,8 +14,8 @@
<link rel="icon" href="<%=paths.base%>static/favicon.svg"> <link rel="icon" href="<%=paths.base%>static/favicon.svg">
<link rel="mask-icon" href="<%=paths.base%>static/favicon.svg" color="#00272a"> <link rel="mask-icon" href="<%=paths.base%>static/favicon.svg" color="#00272a">
<link rel="apple-touch-icon" href="<%=paths.base%>static/app-180.png"> <link rel="apple-touch-icon" href="<%=paths.base%>static/app-180.png">
<link rel="manifest" href="<%=paths.base%>static/manifest.json"> <link rel="manifest" href="<%=paths.base%>manifest.json">
<link rel="search" type="application/opensearchdescription+xml" title="FacilMap" href="<%=paths.base%>static/opensearch.xml"> <link rel="search" type="application/opensearchdescription+xml" title="<%=appName%>" href="<%=paths.base%>opensearch.xml">
<style type="text/css"> <style type="text/css">
html, body, #app { html, body, #app {
margin: 0; margin: 0;
@ -73,7 +73,7 @@
<% } -%> <% } -%>
</head> </head>
<body> <body>
<noscript><p><strong>FacilMap requires JavaScript to work.</strong></p></noscript> <noscript><p><strong><%=appName%> requires JavaScript to work.</strong></p></noscript>
<div id="loading"> <div id="loading">
Loading... Loading...
<div id="spinner"></div> <div id="spinner"></div>
@ -88,7 +88,7 @@
var loading = document.getElementById("loading"); var loading = document.getElementById("loading");
if(loading) { if(loading) {
loading.className += " error"; loading.className += " error";
loading.innerHTML = "Could not load FacilMap!"; loading.innerHTML = "Could not load <%=appName%>!";
} }
}; };
})(); })();

Wyświetl plik

@ -59,7 +59,7 @@ const Root = defineComponent({
}); });
watch(padName, () => { watch(padName, () => {
const title = padName.value != null ? `${normalizePadName(padName.value)}FacilMap` : 'FacilMap'; const title = padName.value != null ? `${normalizePadName(padName.value)}${config.appName}` : config.appName;
// We have to call history.replaceState() in order for the new title to end up in the browser history // We have to call history.replaceState() in order for the new title to end up in the browser history
window.history && history.replaceState({ }, title); window.history && history.replaceState({ }, title);
@ -70,6 +70,7 @@ const Root = defineComponent({
baseUrl, baseUrl,
serverUrl: baseUrl, serverUrl: baseUrl,
padId: padId.value, padId: padId.value,
appName: config.appName,
settings: { settings: {
toolbox: toBoolean(queryParams.toolbox, true), toolbox: toBoolean(queryParams.toolbox, true),
search: toBoolean(queryParams.search, true), search: toBoolean(queryParams.search, true),

Wyświetl plik

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/"> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>FacilMap</ShortName> <ShortName><%=appName%></ShortName>
<Description>A privacy-friendly, open-source versatile online map that combines different services based on OpenStreetMap and makes it easy to find places, plan routes and create custom maps full of markers, lines and routes.</Description> <Description>A privacy-friendly, open-source versatile online map that combines different services based on OpenStreetMap and makes it easy to find places, plan routes and create custom maps full of markers, lines and routes.</Description>
<Image width="32" height="32" type="image/x-icon">./favicon.ico</Image> <Image width="32" height="32" type="image/x-icon">./favicon.ico</Image>
<Image width="64" height="64" type="image/png">./favicon-64.png</Image> <Image width="64" height="64" type="image/png">./favicon-64.png</Image>
<Url type="text/html" template="https://facilmap.org/#q={searchTerms}" /> <Url type="text/html" template="<%=baseUrl%>#q={searchTerms}" />
<moz:SearchForm>https://facilmap.org/</moz:SearchForm> <moz:SearchForm><%=baseUrl%></moz:SearchForm>
</OpenSearchDescription> </OpenSearchDescription>

Wyświetl plik

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title><%=normalizePadName(padData.name)%> – FacilMap</title> <title><%=normalizePadName(padData.name)%> – <%=appName%></title>
<base href="../" /> <base href="../" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<% <%
@ -36,7 +36,7 @@
</head> </head>
<body> <body>
<div class="container-fluid"> <div class="container-fluid">
<h1><%=normalizePadName(padData.name)%> – FacilMap</h1> <h1><%=normalizePadName(padData.name)%> – <%=appName%></h1>
<% <%
for(let type of Object.values(types)) { for(let type of Object.values(types)) {
-%> -%>

Wyświetl plik

@ -11,7 +11,10 @@ export interface DbConfig {
} }
export interface Config { export interface Config {
appName: string;
userAgent: string; userAgent: string;
trustProxy?: boolean | string | string[] | number | ((ip: string) => boolean);
baseUrl?: string;
host?: string; host?: string;
port: number; port: number;
db: DbConfig; db: DbConfig;
@ -24,7 +27,15 @@ export interface Config {
} }
const config: Config = { const config: Config = {
appName: process.env.APP_NAME || "FacilMap",
userAgent: process.env.USER_AGENT || 'FacilMap', userAgent: process.env.USER_AGENT || 'FacilMap',
trustProxy: (
!process.env.TRUST_PROXY ? undefined :
process.env.TRUST_PROXY === "true" ? true :
process.env.TRUST_PROXY.match(/^\d+$/) ? Number(process.env.TRUST_PROXY) :
process.env.TRUST_PROXY
),
baseUrl: process.env.BASE_URL ? (process.env.BASE_URL.endsWith("/") ? process.env.BASE_URL : `${process.env.BASE_URL}/`) : undefined,
host: process.env.HOST || undefined, host: process.env.HOST || undefined,
port: process.env.PORT ? Number(process.env.PORT) : 8080, port: process.env.PORT ? Number(process.env.PORT) : 8080,
db : { db : {

Wyświetl plik

@ -8,11 +8,12 @@ import { Router, type RequestHandler } from "express";
import { static as expressStatic } from "express"; import { static as expressStatic } from "express";
import { normalizeLineName, normalizeMarkerName, normalizePadName, type InjectedConfig } from "facilmap-utils"; import { normalizeLineName, normalizeMarkerName, normalizePadName, type InjectedConfig } from "facilmap-utils";
import config from "./config"; import config from "./config";
import { asyncIteratorToArray, jsonStream } from "./utils/streams";
export const isDevMode = !!process.env.FM_DEV; export const isDevMode = !!process.env.FM_DEV;
async function getManifest(): Promise<Manifest> { async function getViteManifest(): Promise<Manifest> {
const manifest = await readFile(paths.manifest); const manifest = await readFile(paths.viteManifest);
return JSON.parse(manifest.toString()); return JSON.parse(manifest.toString());
} }
@ -35,7 +36,7 @@ async function getScripts(entry: "mapEntry" | "tableEntry"): Promise<Scripts> {
styles: [] styles: []
}; };
} else { } else {
const manifest = await getManifest(); const manifest = await getViteManifest();
let referencedChunks = [paths[entry]]; let referencedChunks = [paths[entry]];
for (let i = 0; i < referencedChunks.length; i++) { for (let i = 0; i < referencedChunks.length; i++) {
@ -73,7 +74,9 @@ export async function renderMap(params: RenderMapParams): Promise<string> {
]); ]);
return ejs.render(template, { return ejs.render(template, {
appName: config.appName,
config: { config: {
appName: config.appName,
limaLabsToken: config.limaLabsToken limaLabsToken: config.limaLabsToken
} satisfies InjectedConfig, } satisfies InjectedConfig,
...injections, ...injections,
@ -94,6 +97,7 @@ export async function renderTable(params: {
return ejs.render(template, { return ejs.render(template, {
...injections, ...injections,
appName: config.appName,
paths, paths,
utils, utils,
normalizeMarkerName, normalizeMarkerName,
@ -123,3 +127,20 @@ export async function getStaticFrontendMiddleware(): Promise<RequestHandler> {
return router; return router;
} }
} }
export async function getPwaManifest(): Promise<string> {
const template = await readFile(paths.pwaManifest).then((t) => t.toString());
const chunks = await asyncIteratorToArray(jsonStream(JSON.parse(template), {
APP_NAME: config.appName
}));
return chunks.join("");
}
export async function getOpensearchXml(baseUrl: string): Promise<string> {
const template = await readFile(paths.opensearchXmlEjs).then((t) => t.toString());
return ejs.render(template, {
appName: config.appName,
baseUrl
});
}

Wyświetl plik

@ -1,6 +1,6 @@
import { ReadableStream, TransformStream } from "stream/web"; import { ReadableStream, TransformStream } from "stream/web";
export async function asyncIteratorToArray<T>(iterator: AsyncGenerator<T, any, void>): Promise<Array<T>> { export async function asyncIteratorToArray<T>(iterator: AsyncIterable<T>): Promise<Array<T>> {
const result: T[] = []; const result: T[] = [];
for await (const it of iterator) { for await (const it of iterator) {
result.push(it); result.push(it);

Wyświetl plik

@ -8,13 +8,19 @@ import { exportGeoJson } from "./export/geojson.js";
import { exportGpx } from "./export/gpx.js"; import { exportGpx } from "./export/gpx.js";
import domainMiddleware from "express-domain-middleware"; import domainMiddleware from "express-domain-middleware";
import { Readable, Writable } from "stream"; import { Readable, Writable } from "stream";
import { getStaticFrontendMiddleware, renderMap, type RenderMapParams } from "./frontend"; import { getOpensearchXml, getPwaManifest, getStaticFrontendMiddleware, renderMap, type RenderMapParams } from "./frontend";
import { normalizePadName } from "facilmap-utils"; import { normalizePadName } from "facilmap-utils";
import { paths } from "facilmap-frontend/build.js";
import config from "./config";
type PathParams = { type PathParams = {
padId: PadId padId: PadId
} }
function getBaseUrl(req: Request): string {
return config.baseUrl ?? `${req.protocol}://${req.host}/`;
}
export async function initWebserver(database: Database, port: number, host?: string): Promise<HttpServer> { export async function initWebserver(database: Database, port: number, host?: string): Promise<HttpServer> {
const padMiddleware = async (req: Request<PathParams>, res: Response<string>) => { const padMiddleware = async (req: Request<PathParams>, res: Response<string>) => {
let params: RenderMapParams; let params: RenderMapParams;
@ -52,11 +58,24 @@ export async function initWebserver(database: Database, port: number, host?: str
}; };
const app = express(); const app = express();
app.set("trust proxy", config.trustProxy ?? false);
app.use(domainMiddleware); app.use(domainMiddleware);
app.use(compression()); app.use(compression());
app.get("/", padMiddleware); app.get("/", padMiddleware);
app.get(`${paths.base}manifest.json`, async (req, res) => {
res.set("Content-type", "application/manifest+json");
res.send(await getPwaManifest());
});
app.get(`${paths.base}opensearch.xml`, async (req, res) => {
res.set("Content-type", "application/opensearchdescription+xml");
res.send(await getOpensearchXml(getBaseUrl(req)));
});
app.use(await getStaticFrontendMiddleware()); app.use(await getStaticFrontendMiddleware());
// If no file with this name has been found, we render a pad // If no file with this name has been found, we render a pad

Wyświetl plik

@ -8,5 +8,6 @@ export function isPromise(object: any): object is Promise<unknown> {
* The config that the backend injects into the EJS template to be read by the frontend. * The config that the backend injects into the EJS template to be read by the frontend.
*/ */
export interface InjectedConfig { export interface InjectedConfig {
appName: string;
limaLabsToken?: string; limaLabsToken?: string;
} }