diff --git a/src/env/index.ts b/src/env/index.ts index a968ee7d..07e3d20e 100644 --- a/src/env/index.ts +++ b/src/env/index.ts @@ -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) { diff --git a/src/features/flights-map/components/FlightsMapStartPage.tsx b/src/features/flights-map/components/FlightsMapStartPage.tsx index e249c17b..f13b506a 100644 --- a/src/features/flights-map/components/FlightsMapStartPage.tsx +++ b/src/features/flights-map/components/FlightsMapStartPage.tsx @@ -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 = ({ + 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 (
diff --git a/src/routes/[lang]/flights-map/page.tsx b/src/routes/[lang]/flights-map/page.tsx index 707bf8d5..c6b73e16 100644 --- a/src/routes/[lang]/flights-map/page.tsx +++ b/src/routes/[lang]/flights-map/page.tsx @@ -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 { <> {t("SHARED.LOADING")}
}> - + );