Make Leaflet tile URL configurable via MAP_TILE_URL env

The flights-map tile URL was hardcoded as the same-origin path
'/map/api/tile/{z}/{x}/{y}.jpeg' (matching Angular's environment.ts).
On deployments where the ingress routes /map/api/** to the upstream
tile service (prod, flights.test.aeroflot.ru) this works. On
deployments without that rule (e.g. flights-ui.devwebzavod.ru) the
Modern.js SSR catch-all answers every tile URL with the SPA index
page, so Leaflet renders the marker + controls but never paints the
raster layer.

Expose the URL through MAP_TILE_URL env with the same-origin path as
the default, read it on the server route (where process.env is
available), and pass the resolved URL to FlightsMapStartPage as a
prop so the client bundle uses whatever the operator configured.
Prod and same-origin deployments stay unchanged; dev clusters can
point at an absolute URL like https://flights.test.aeroflot.ru/map/api/tile/...
instead.
This commit is contained in:
2026-04-18 22:34:41 +03:00
parent 4aa0bbe5e6
commit 6813bf902e
3 changed files with 32 additions and 8 deletions
+8
View File
@@ -28,6 +28,12 @@ const EnvSchema = z.object({
ANALYTICS_VARIOCUBE: boolish.default("false"),
ANALYTICS_DYNATRACE: boolish.default("false"),
FEATURE_FLIGHTS_MAP: boolish.default("true"),
// Leaflet tile URL template. Defaults to the same-origin path used by
// Angular (environment.ts: mapApiUrl) so production deployments that
// route /map/api/** through the ingress stay unchanged. Deployments
// without that rule (e.g. flights-ui.devwebzavod.ru) can override
// with a fully-qualified URL to an upstream tile service.
MAP_TILE_URL: z.string().default("/map/api/tile/{z}/{x}/{y}.jpeg"),
VERSION: z.string().min(1).default("dev"),
});
@@ -44,6 +50,7 @@ export interface Env {
LOGS_ENDPOINT?: string;
ANALYTICS_ENABLED: AnalyticsProviders;
FEATURE_FLIGHTS_MAP: boolean;
MAP_TILE_URL: string;
VERSION: string;
}
@@ -84,6 +91,7 @@ export function getEnv(): Env {
dynatrace: raw.ANALYTICS_DYNATRACE,
},
FEATURE_FLIGHTS_MAP: raw.FEATURE_FLIGHTS_MAP,
MAP_TILE_URL: raw.MAP_TILE_URL,
VERSION: raw.VERSION,
};
if (raw.OTEL_EXPORTER_OTLP_ENDPOINT !== undefined) {
@@ -66,7 +66,20 @@ function addMonthsYyyymmdd(base: string, months: number): string {
// Component
// ---------------------------------------------------------------------------
export const FlightsMapStartPage: FC = () => {
export interface FlightsMapStartPageProps {
/**
* Leaflet tile URL template (MAP_TILE_URL env). Defaults to same-origin
* so tests and stand-alone renders keep working without wiring env. In
* production the wrapping route page resolves it from env and passes it
* in; absolute URLs let deployments without a /map/api/** ingress rule
* fetch tiles from an upstream tile service (e.g. flights.test.aeroflot.ru).
*/
tileUrl?: string;
}
export const FlightsMapStartPage: FC<FlightsMapStartPageProps> = ({
tileUrl: tileUrlProp,
}) => {
const { t } = useTranslation();
const routeParams = useParams<{ lang: string }>();
const lang = routeParams.lang ?? "ru";
@@ -266,11 +279,10 @@ export const FlightsMapStartPage: FC = () => {
t,
]);
// Tile URL — same-origin path served by the reverse proxy upstream of the
// SSR app. Matches Angular's environment.mapApiUrl. Must NOT be prefixed
// with API_BASE_URL because tiles come from a different backend route
// than the JSON API, and the path is absolute at the origin.
const tileUrl = "/map/api/tile/{z}/{x}/{y}.jpeg";
// Tile URL comes from the route page (reads MAP_TILE_URL env on the
// server). Fallback matches Angular's environment.ts mapApiUrl so
// stand-alone renders + tests keep working without threading env.
const tileUrl = tileUrlProp ?? "/map/api/tile/{z}/{x}/{y}.jpeg";
return (
<div className="flights-map-start-page" data-testid="flights-map-start">
+6 -2
View File
@@ -26,7 +26,11 @@ export default function FlightsMapPage(): JSX.Element {
const { t } = useTranslation();
const routeParams = useParams<{ lang: string }>();
const locale = routeParams.lang ?? "ru";
const canonicalOrigin = getEnv().PROD_ORIGIN;
const env = getEnv();
const canonicalOrigin = env.PROD_ORIGIN;
// Tile URL read on the server (where process.env is available) and passed
// down as a prop — the client bundle can't read MAP_TILE_URL itself.
const tileUrl = env.MAP_TILE_URL;
const isEnabled = useFeatureFlag("flightsMap");
if (!isEnabled) {
@@ -44,7 +48,7 @@ export default function FlightsMapPage(): JSX.Element {
<>
<SeoHead {...seoProps} jsonLd={jsonLd} />
<Suspense fallback={<div aria-busy="true">{t("SHARED.LOADING")}</div>}>
<FlightsMapStartPage />
<FlightsMapStartPage tileUrl={tileUrl} />
</Suspense>
</>
);