diff --git a/docker-compose.yml b/docker-compose.yml
index afc87f66..9dda7d8a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -64,9 +64,9 @@ services:
UPLOAD_FOLDER: /app/uploads
UPLOAD_USER:
UPLOAD_PASSWORD:
- PUBLIC_OVERPASS_API_URL: https://overpass-api.de
- PUBLIC_VALHALLA_URL: https://valhalla1.openstreetmap.de
- PUBLIC_NOMINATIM_URL: https://nominatim.openstreetmap.org
+ OVERPASS_API_URL: https://overpass-api.de
+ VALHALLA_URL: https://valhalla1.openstreetmap.de
+ NOMINATIM_URL: https://nominatim.openstreetmap.org
volumes:
- ./data/uploads:/app/uploads
# - ./data/about.md:/app/build/client/md/about.md
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index c6c78ce6..5b821175 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -52,9 +52,9 @@ services:
UPLOAD_FOLDER: /app/uploads
UPLOAD_USER:
UPLOAD_PASSWORD:
- PUBLIC_OVERPASS_API_URL: https://overpass-api.de
- PUBLIC_VALHALLA_URL: https://valhalla1.openstreetmap.de
- PUBLIC_NOMINATIM_URL: https://nominatim.openstreetmap.org
+ OVERPASS_API_URL: https://overpass-api.de
+ VALHALLA_URL: https://valhalla1.openstreetmap.de
+ NOMINATIM_URL: https://nominatim.openstreetmap.org
volumes:
- uploads:/app/uploads
# - ./data/about.md:/app/build/client/md/about.md
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index f89161e6..fc398ca4 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -52,9 +52,9 @@ services:
UPLOAD_FOLDER: /app/uploads
UPLOAD_USER:
UPLOAD_PASSWORD:
- PUBLIC_OVERPASS_API_URL: https://overpass-api.de
- PUBLIC_VALHALLA_URL: https://valhalla1.openstreetmap.de
- PUBLIC_NOMINATIM_URL: https://nominatim.openstreetmap.org
+ OVERPASS_API_URL: https://overpass-api.de
+ VALHALLA_URL: https://valhalla1.openstreetmap.de
+ NOMINATIM_URL: https://nominatim.openstreetmap.org
volumes:
- uploads:/app/uploads
# - ./data/about.md:/app/build/client/md/about.md
diff --git a/docs/src/content/docs/develop/local-development.md b/docs/src/content/docs/develop/local-development.md
index 247fa442..9e09fc5a 100644
--- a/docs/src/content/docs/develop/local-development.md
+++ b/docs/src/content/docs/develop/local-development.md
@@ -12,7 +12,7 @@ export ORIGIN=http://localhost:5173
export MEILI_URL=http://127.0.0.1:7700
export MEILI_MASTER_KEY=p2gYZAWODOrwTPr4AYoahCZ9CI8y9bUd0yQLGk-E3m8
export PUBLIC_POCKETBASE_URL=http://127.0.0.1:8090
-export PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de
+export VALHALLA_URL=https://valhalla1.openstreetmap.de
export POCKETBASE_ENCRYPTION_KEY=9ada3c93163812101e50e2bf49e880bc
cd search && ./meilisearch --master-key $MEILI_MASTER_KEY &
diff --git a/docs/src/content/docs/run/environment-configuration.md b/docs/src/content/docs/run/environment-configuration.md
index cd14daab..ee0e10b0 100644
--- a/docs/src/content/docs/run/environment-configuration.md
+++ b/docs/src/content/docs/run/environment-configuration.md
@@ -42,10 +42,38 @@ Since we use an unmodified installation of meilisearch you can use all variables
| BODY_SIZE_LIMIT | Maximum allowed upload size | Infinity |
| PUBLIC_POCKETBASE_URL | IP or hostname (including the port) of your pocketbase instance | http://db:8090 |
| PUBLIC_DISABLE_SIGNUP | Disables signup option for new users | false |
-| PUBLIC_VALHALLA_URL | Public IP or hostname (including the port) of a valhalla instance | https://valhalla1.openstreetmap.de |
-| PUBLIC_NOMINATIM_URL | Public IP or hostname (including the port) of a nominatim instance | https://nominatim.openstreetmap.org |
| PUBLIC_PRIVATE_INSTANCE | Setting this to true will block visitors from viewing content without an account | false |
| UPLOAD_FOLDER | Folder from which wanderer auto-uploads trails | /app/uploads |
| UPLOAD_USER | Username for the account with which wanderer auto-uploads trails | |
| UPLOAD_PASSWORD | Password for the account with which wanderer auto-uploads trails | |
-| PUBLIC_OVERPASS_API_URL | Overpass API URL used for map points of interest | https://overpass-api.de |
+
+## Geocoding & Routing
+
+These variables configure server-side requests to Valhalla, Nominatim and Overpass.
+
+| Environment Variable | Description | Default |
+| ------------------------ | --------------------------------------------------------------------------- | ----------------------------------- |
+| VALHALLA_URL | Valhalla API URL used for auto-routing and elevation data | https://valhalla1.openstreetmap.de |
+| NOMINATIM_URL | Nominatim API URL used for (reverse) geocoding | https://nominatim.openstreetmap.org |
+| OVERPASS_API_URL | Overpass API URL used for map points of interest | https://overpass-api.de |
+
+When `*_URL` is unset, the backend falls back to legacy `PUBLIC_*_URL`.
+
+## Custom CA certificates
+
+If your API endpoints use certificates signed by a private CA, add the CA bundle and set `NODE_EXTRA_CA_CERTS` for the `web` service.
+
+| Environment Variable | Description | Default |
+| -------------------- | ------------------------------------------------------------------------ | ------- |
+| NODE_EXTRA_CA_CERTS | Path to an additional CA bundle used by Node.js TLS connections | |
+
+Example (`docker-compose.yml`):
+
+```yaml
+services:
+ web:
+ environment:
+ NODE_EXTRA_CA_CERTS: /etc/ssl/private-ca/ca.pem
+ volumes:
+ - ./certs/ca.pem:/etc/ssl/private-ca/ca.pem:ro
+```
diff --git a/docs/src/content/docs/run/installation/docker.mdx b/docs/src/content/docs/run/installation/docker.mdx
index 8212030e..24894099 100644
--- a/docs/src/content/docs/run/installation/docker.mdx
+++ b/docs/src/content/docs/run/installation/docker.mdx
@@ -111,11 +111,14 @@ services:
UPLOAD_FOLDER: /app/uploads
UPLOAD_USER:
UPLOAD_PASSWORD:
- PUBLIC_OVERPASS_API_URL: https://overpass-api.de
- PUBLIC_VALHALLA_URL: https://valhalla1.openstreetmap.de
- PUBLIC_NOMINATIM_URL: https://nominatim.openstreetmap.org
+ OVERPASS_API_URL: https://overpass-api.de
+ VALHALLA_URL: https://valhalla1.openstreetmap.de
+ NOMINATIM_URL: https://nominatim.openstreetmap.org
+ # Optional: trust private CAs for server-side API calls
+ # NODE_EXTRA_CA_CERTS: /etc/ssl/private-ca/ca.pem
volumes:
- ./data/uploads:/app/uploads
+ # - ./certs/ca.pem:/etc/ssl/private-ca/ca.pem:ro
# - ./data/about.md:/app/build/client/md/about.md
ports:
- "3000:3000"
diff --git a/docs/src/content/docs/run/installation/from-source.mdx b/docs/src/content/docs/run/installation/from-source.mdx
index f9249347..1b6819cf 100644
--- a/docs/src/content/docs/run/installation/from-source.mdx
+++ b/docs/src/content/docs/run/installation/from-source.mdx
@@ -66,7 +66,7 @@ export ORIGIN=http://localhost:3000
export MEILI_URL=http://127.0.0.1:7700
export MEILI_MASTER_KEY=YOU_SHOULD_DEFINITELY_CHANGE_ME
export PUBLIC_POCKETBASE_URL=http://127.0.0.1:8090
-export PUBLIC_VALHALLA_URL=https://valhalla1.openstreetmap.de
+export VALHALLA_URL=https://valhalla1.openstreetmap.de
export POCKETBASE_ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE
# Optional configuration
@@ -76,6 +76,7 @@ export POCKETBASE_ENCRYPTION_KEY=YOUR_ENCRYPTION_KEY_HERE
# export UPLOAD_FOLDER=/app/uploads
# export UPLOAD_USER=
# export UPLOAD_PASSWORD=
+# export NODE_EXTRA_CA_CERTS=/absolute/path/to/ca.pem
cd search && ./meilisearch --master-key $MEILI_MASTER_KEY &
cd db && ./pocketbase serve &
diff --git a/web/src/lib/server/http.ts b/web/src/lib/server/http.ts
new file mode 100644
index 00000000..7a683253
--- /dev/null
+++ b/web/src/lib/server/http.ts
@@ -0,0 +1,18 @@
+import { json } from "@sveltejs/kit";
+
+function safeJson(text: string): any {
+ try {
+ return JSON.parse(text);
+ } catch {
+ return { message: text };
+ }
+}
+
+export async function proxyJsonResponse(response: Response) {
+ const text = await response.text();
+ const payload = text.length ? safeJson(text) : {};
+ if (!response.ok) {
+ return json(payload, { status: response.status });
+ }
+ return json(payload);
+}
diff --git a/web/src/lib/server/nominatim.ts b/web/src/lib/server/nominatim.ts
new file mode 100644
index 00000000..7bc217d1
--- /dev/null
+++ b/web/src/lib/server/nominatim.ts
@@ -0,0 +1,63 @@
+import { version } from "$app/environment";
+import { resolveBaseUrl } from "$lib/server/url";
+import type { RequestEvent } from "@sveltejs/kit";
+
+const NOMINATIM_RATE_LIMIT_MS = 1000;
+const NOMINATIM_MAX_RETRIES = 2;
+let lastNominatimCall = 0;
+
+function getNominatimBaseUrl(): string {
+ return resolveBaseUrl("NOMINATIM_URL", "https://nominatim.openstreetmap.org");
+}
+
+function needsRateLimiting(baseUrl: string): boolean {
+ return baseUrl.includes("nominatim.openstreetmap.org");
+}
+
+const waitTimer = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+async function nominatimRateLimiter(baseUrl: string) {
+ if (!needsRateLimiting(baseUrl)) {
+ return;
+ }
+
+ const elapsedTimeMs = Date.now() - lastNominatimCall;
+ const waitTime = NOMINATIM_RATE_LIMIT_MS - elapsedTimeMs;
+ if (waitTime > 0) {
+ await waitTimer(waitTime);
+ }
+
+ lastNominatimCall = Date.now();
+}
+
+export async function fetchNominatim(event: RequestEvent, path: string, params: URLSearchParams): Promise {
+ const baseUrl = getNominatimBaseUrl();
+ const base = new URL(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
+ const cleanPath = path.replace(/^\/+/, "");
+ const url = new URL(cleanPath, base);
+ const query = params.toString();
+ if (query.length) {
+ url.search = query;
+ }
+
+ let attempt = 0;
+
+ while (true) {
+ await nominatimRateLimiter(baseUrl);
+
+ try {
+ return await event.fetch(url.toString(), {
+ method: "GET",
+ headers: {
+ "User-Agent": `wanderer/${version}`,
+ },
+ });
+ } catch (error) {
+ if (attempt < NOMINATIM_MAX_RETRIES) {
+ attempt++;
+ continue;
+ }
+ throw new Error(`Nominatim fetch failed for ${url.toString()}`, { cause: error });
+ }
+ }
+}
diff --git a/web/src/lib/server/overpass.ts b/web/src/lib/server/overpass.ts
new file mode 100644
index 00000000..0f8a96a7
--- /dev/null
+++ b/web/src/lib/server/overpass.ts
@@ -0,0 +1,34 @@
+import { resolveBaseUrl } from "$lib/server/url";
+import type { RequestEvent } from "@sveltejs/kit";
+
+const OVERPASS_MAX_RETRIES = 2;
+
+function getOverpassBaseUrl(): string {
+ return resolveBaseUrl("OVERPASS_API_URL", "https://overpass-api.de");
+}
+
+export async function fetchOverpass(event: RequestEvent, params: URLSearchParams): Promise {
+ const baseUrl = getOverpassBaseUrl();
+ const base = new URL(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
+ const url = new URL("api/interpreter", base);
+ const query = params.toString();
+ if (query.length) {
+ url.search = query;
+ }
+
+ let attempt = 0;
+
+ while (true) {
+ try {
+ return await event.fetch(url.toString(), {
+ method: "GET",
+ });
+ } catch (error) {
+ if (attempt < OVERPASS_MAX_RETRIES) {
+ attempt++;
+ continue;
+ }
+ throw error;
+ }
+ }
+}
diff --git a/web/src/lib/server/url.ts b/web/src/lib/server/url.ts
new file mode 100644
index 00000000..7f195439
--- /dev/null
+++ b/web/src/lib/server/url.ts
@@ -0,0 +1,24 @@
+import { env as privateEnv } from "$env/dynamic/private";
+import { env as publicEnv } from "$env/dynamic/public";
+
+export type ExternalServiceUrlKey = "VALHALLA_URL" | "NOMINATIM_URL" | "OVERPASS_API_URL";
+
+export function normalizeBaseUrl(url: string): string {
+ const trimmedUrl = url.trim();
+ if (!trimmedUrl) {
+ return "";
+ }
+ if (!/^https?:\/\//i.test(trimmedUrl)) {
+ return `https://${trimmedUrl}`;
+ }
+ return trimmedUrl;
+}
+
+export function resolveBaseUrl(
+ key: ExternalServiceUrlKey,
+ fallback: string = "",
+): string {
+ const publicKey = `PUBLIC_${key}` as `PUBLIC_${string}`;
+ const rawUrl = privateEnv[key] ?? publicEnv[publicKey] ?? fallback;
+ return normalizeBaseUrl(rawUrl);
+}
diff --git a/web/src/lib/server/valhalla.ts b/web/src/lib/server/valhalla.ts
new file mode 100644
index 00000000..98ff4294
--- /dev/null
+++ b/web/src/lib/server/valhalla.ts
@@ -0,0 +1,5 @@
+import { resolveBaseUrl } from "$lib/server/url";
+
+export function getValhallaBaseUrl(): string {
+ return resolveBaseUrl("VALHALLA_URL");
+}
diff --git a/web/src/lib/stores/search_store.ts b/web/src/lib/stores/search_store.ts
index 78ce7857..a25a1971 100644
--- a/web/src/lib/stores/search_store.ts
+++ b/web/src/lib/stores/search_store.ts
@@ -1,10 +1,8 @@
-import { env } from "$env/dynamic/public";
import type { Actor } from "$lib/models/activitypub/actor";
import { defaultTrailSearchAttributes, type TrailSearchResult } from "$lib/models/trail";
import { APIError } from "$lib/util/api_util";
import type { Hits, MultiSearchParams, MultiSearchResponse, MultiSearchResult, SearchParams, SearchResponse } from "meilisearch";
import type { ListResult } from "pocketbase";
-import { version } from "$app/environment";
export type LocationSearchResult = {
name: string;
@@ -104,14 +102,17 @@ export async function searchTrails(q: string, options: SearchParams): Promise> {
- const nominatimURL = env.PUBLIC_NOMINATIM_URL ?? "https://nominatim.openstreetmap.org"
- const r = await fetch(`${nominatimURL}/search?q=${q}&format=geojson&addressdetails=1${limit ? '&limit=' + limit : ''}`, {
- method: "GET",
- headers: new Headers({
- "User-Agent": "wanderer/" + version
- })
- });
+export async function searchLocations(q: string, limit?: number, f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch): Promise> {
+ if (!q.trim()) {
+ return [];
+ }
+
+ const params = new URLSearchParams({
+ q,
+ format: "geojson",
+ addressdetails: "1",
+ });
+ const r = await fetchGeocoding("search", params, f);
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
@@ -127,14 +128,20 @@ export async function searchLocations(q: string, limit?: number): Promise Promise = fetch): Promise {
+ const query = params.toString();
+ const url = query.length ? `/api/v1/geocoding/${path}?${query}` : `/api/v1/geocoding/${path}`;
+ return await f(url);
+}
+
+export async function searchLocationReverse(lat: number, lon: number, f: (url: RequestInfo | URL, config?: RequestInit) => Promise = fetch) {
+ const params = new URLSearchParams({
+ lat: String(lat),
+ lon: String(lon),
+ format: "geojson",
+ addressdetails: "1",
+ });
+ const r = await fetchGeocoding("reverse", params, f);
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
@@ -170,14 +177,17 @@ function getLocationDescription(address: Address) {
export async function searchMulti(options: MultiSearchParams): Promise[]> {
- const locationQuery = options.queries.find(q => q.indexUid === "locations");
- const locationQueryIndex = locationQuery ? options.queries.indexOf(locationQuery) : -1
- if (locationQueryIndex >= 0) {
- options.queries.splice(locationQueryIndex, 1)
- }
+ const locationQueryIndex = options.queries.findIndex(q => q.indexUid === "locations");
+ const locationQuery = locationQueryIndex >= 0 ? options.queries[locationQueryIndex] : undefined;
+ const queries = locationQueryIndex >= 0
+ ? options.queries.filter((_, index) => index !== locationQueryIndex)
+ : options.queries;
const r = await fetch("/api/v1/search/multi", {
method: "POST",
- body: JSON.stringify(options),
+ body: JSON.stringify({
+ ...options,
+ queries,
+ }),
});
if (!r.ok) {
diff --git a/web/src/lib/vendor/maplibre-layer-manager/overpass-layer.ts b/web/src/lib/vendor/maplibre-layer-manager/overpass-layer.ts
index 040490a5..d7e7e805 100644
--- a/web/src/lib/vendor/maplibre-layer-manager/overpass-layer.ts
+++ b/web/src/lib/vendor/maplibre-layer-manager/overpass-layer.ts
@@ -12,7 +12,7 @@ import type { OverpassResponse } from "./types";
import { env } from '$env/dynamic/public'
export class OverpassLayer implements BaseLayer {
- private overpassApiURL: string = (env.PUBLIC_OVERPASS_API_URL && env.PUBLIC_OVERPASS_API_URL.length > 0 ? env.PUBLIC_OVERPASS_API_URL : "https://overpass-api.de") + "/api/interpreter";
+ private overpassApiURL: string = "/api/v1/overpass/interpreter";
data: GeoJSON.FeatureCollection = ({ type: 'FeatureCollection', features: [] });
diff --git a/web/src/routes/api/v1/geocoding/reverse/+server.ts b/web/src/routes/api/v1/geocoding/reverse/+server.ts
new file mode 100644
index 00000000..0eb931bb
--- /dev/null
+++ b/web/src/routes/api/v1/geocoding/reverse/+server.ts
@@ -0,0 +1,36 @@
+import { json, type RequestEvent } from "@sveltejs/kit";
+import { proxyJsonResponse } from "$lib/server/http";
+import { fetchNominatim } from "$lib/server/nominatim";
+
+export async function GET(event: RequestEvent) {
+ const lat = event.url.searchParams.get("lat");
+ const lon = event.url.searchParams.get("lon");
+ if (!lat || !lon) {
+ return json({ message: "Missing query parameter: lat or lon" }, { status: 400 });
+ }
+
+ if (Number.isNaN(Number(lat)) || Number.isNaN(Number(lon))) {
+ return json({ message: "Invalid query parameter: lat or lon" }, { status: 400 });
+ }
+
+ const params = new URLSearchParams({
+ lat,
+ lon,
+ format: "geojson",
+ addressdetails: "1",
+ });
+
+ try {
+ const response = await fetchNominatim(event, "/reverse", params);
+ return await proxyJsonResponse(response);
+ } catch (error) {
+ const err = error instanceof Error ? error : new Error(String(error));
+ const detail = {
+ name: err.name,
+ message: err.message,
+ cause: err.cause instanceof Error ? err.cause.message : err.cause,
+ };
+ console.error("Nominatim reverse request failed", detail);
+ return json({ message: "Nominatim request failed", detail }, { status: 502 });
+ }
+}
diff --git a/web/src/routes/api/v1/geocoding/search/+server.ts b/web/src/routes/api/v1/geocoding/search/+server.ts
new file mode 100644
index 00000000..4fb16886
--- /dev/null
+++ b/web/src/routes/api/v1/geocoding/search/+server.ts
@@ -0,0 +1,38 @@
+import { json, type RequestEvent } from "@sveltejs/kit";
+import { proxyJsonResponse } from "$lib/server/http";
+import { fetchNominatim } from "$lib/server/nominatim";
+
+export async function GET(event: RequestEvent) {
+ const q = event.url.searchParams.get("q");
+ if (!q) {
+ return json({ message: "Missing query parameter: q" }, { status: 400 });
+ }
+
+ const limit = event.url.searchParams.get("limit");
+ if (limit !== null && Number.isNaN(Number(limit))) {
+ return json({ message: "Invalid query parameter: limit" }, { status: 400 });
+ }
+
+ const params = new URLSearchParams({
+ q,
+ format: "geojson",
+ addressdetails: "1",
+ });
+ if (limit) {
+ params.set("limit", limit);
+ }
+
+ try {
+ const response = await fetchNominatim(event, "/search", params);
+ return await proxyJsonResponse(response);
+ } catch (error) {
+ const err = error instanceof Error ? error : new Error(String(error));
+ const detail = {
+ name: err.name,
+ message: err.message,
+ cause: err.cause instanceof Error ? err.cause.message : err.cause,
+ };
+ console.error("Nominatim search request failed", detail);
+ return json({ message: "Nominatim request failed", detail }, { status: 502 });
+ }
+}
diff --git a/web/src/routes/api/v1/overpass/interpreter/+server.ts b/web/src/routes/api/v1/overpass/interpreter/+server.ts
new file mode 100644
index 00000000..f10c5eef
--- /dev/null
+++ b/web/src/routes/api/v1/overpass/interpreter/+server.ts
@@ -0,0 +1,21 @@
+import { json, type RequestEvent } from "@sveltejs/kit";
+import { proxyJsonResponse } from "$lib/server/http";
+import { fetchOverpass } from "$lib/server/overpass";
+
+export async function GET(event: RequestEvent) {
+ const data = event.url.searchParams.get("data");
+ if (!data) {
+ return json({ message: "Missing query parameter: data" }, { status: 400 });
+ }
+
+ const params = new URLSearchParams({
+ data,
+ });
+
+ try {
+ const response = await fetchOverpass(event, params);
+ return await proxyJsonResponse(response);
+ } catch (error) {
+ return json({ message: "Overpass request failed" }, { status: 502 });
+ }
+}
diff --git a/web/src/routes/api/v1/trail/upload/+server.ts b/web/src/routes/api/v1/trail/upload/+server.ts
index 16bdff88..b0986cd0 100644
--- a/web/src/routes/api/v1/trail/upload/+server.ts
+++ b/web/src/routes/api/v1/trail/upload/+server.ts
@@ -75,8 +75,12 @@ export async function PUT(event: RequestEvent) {
}
if (trail.lat && trail.lon) {
- const location = await searchLocationReverse(trail.lat, trail.lon)
- trail.location ??= location;
+ try {
+ const location = await searchLocationReverse(trail.lat, trail.lon, event.fetch)
+ trail.location ??= location;
+ } catch (e: any) {
+ console.warn("Reverse geocoding failed during upload", e);
+ }
}
trail.public = event.locals.settings.privacy?.trails == "public"
diff --git a/web/src/routes/api/v1/valhalla/height/+server.ts b/web/src/routes/api/v1/valhalla/height/+server.ts
index c17d5bee..4f6b7625 100644
--- a/web/src/routes/api/v1/valhalla/height/+server.ts
+++ b/web/src/routes/api/v1/valhalla/height/+server.ts
@@ -1,5 +1,6 @@
-import { env } from '$env/dynamic/public';
-import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit";
+import { getValhallaBaseUrl } from '$lib/server/valhalla';
+import { proxyJsonResponse } from '$lib/server/http';
+import { json, type RequestEvent } from "@sveltejs/kit";
/**
@@ -29,18 +30,15 @@ import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
+ const baseUrl = getValhallaBaseUrl();
const data = await event.request.json()
- if (!env.PUBLIC_VALHALLA_URL) {
- return error(400, "PUBLIC_VALHALLA_URL not set")
+ if (!baseUrl) {
+ return json({ message: "VALHALLA_URL not set" }, { status: 400 })
}
try {
- const r = await event.fetch(env.PUBLIC_VALHALLA_URL + '/height', { method: "POST", body: JSON.stringify(data) });
- const response = await r.json();
- if (!r.ok) {
- throw error(r.status as NumericRange<400,500>, response);
- }
- return json(response);
+ const response = await event.fetch(baseUrl + '/height', { method: "POST", body: JSON.stringify(data) });
+ return await proxyJsonResponse(response);
} catch (e: any) {
- throw error(e.status || 500, e)
+ return json({ message: "Valhalla request failed" }, { status: 502 })
}
-}
\ No newline at end of file
+}
diff --git a/web/src/routes/api/v1/valhalla/route/+server.ts b/web/src/routes/api/v1/valhalla/route/+server.ts
index 543d7097..26bba480 100644
--- a/web/src/routes/api/v1/valhalla/route/+server.ts
+++ b/web/src/routes/api/v1/valhalla/route/+server.ts
@@ -1,6 +1,10 @@
-import { env } from '$env/dynamic/public';
-import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit";
+import { getValhallaBaseUrl } from '$lib/server/valhalla';
+import { proxyJsonResponse } from '$lib/server/http';
+import { json, type RequestEvent } from "@sveltejs/kit";
+type RouteRequestBody = Record & {
+ include_elevation_profile?: boolean;
+};
/**
* @swagger
@@ -29,19 +33,19 @@ import { error, json, type NumericRange, type RequestEvent } from "@sveltejs/kit
* description: Internal Server Error
*/
export async function POST(event: RequestEvent) {
- const data = await event.request.json()
- if (!env.PUBLIC_VALHALLA_URL) {
- return json({ message: "PUBLIC_VALHALLA_URL not set" }, { status: 400 })
+ const baseUrl = getValhallaBaseUrl();
+ const data: RouteRequestBody = await event.request.json();
+ if (!baseUrl) {
+ return json({ message: "VALHALLA_URL not set" }, { status: 400 })
}
- try {
- const r = await event.fetch(env.PUBLIC_VALHALLA_URL + '/route', { method: "POST", body: JSON.stringify(data) });
- const response = await r.json();
- if (!r.ok) {
- return json({ message: response }, { status: r.status })
- }
- return json(response);
+ try {
+ const response = await event.fetch(baseUrl + '/route', {
+ method: "POST",
+ body: JSON.stringify(data)
+ });
+ return await proxyJsonResponse(response);
} catch (e: any) {
- return json({ message: e }, { status: 500 })
+ return json({ message: "Valhalla request failed" }, { status: 502 })
}
-}
\ No newline at end of file
+}
diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte
index e7009a6b..fc5977f2 100644
--- a/web/src/routes/trail/edit/[id]/+page.svelte
+++ b/web/src/routes/trail/edit/[id]/+page.svelte
@@ -1,5 +1,4 @@