ef85ae6ea1
http://flights-ui.devwebzavod.ru/ru/flights-map was still hitting the same-origin tile path after adding the k8s env: Modern.js renders the <Suspense> fallback on the server (i18n isn't preloaded), so the route component that reads getEnv() never actually runs during SSR. The page hydrates client-side, where process.env is Rspack's empty stub and MAP_TILE_URL is never set — getEnv() falls back to the default. Move the value into window.__ENV__ instead: - modern.config.ts: inline a <script> into html.tags that sets window.__ENV__ = { MAP_TILE_URL: <value> } at SSR-server startup. The snippet is authored once at server boot, so the HTML template baked into dist/standalone/html/main/index.html always carries the pod's tile URL. - src/env/index.ts: merge window.__ENV__ on top of process.env so the browser prefers the injected value (process.env only has NODE_ENV after Rspack's polyfill). - Dockerfile.react: accept MAP_TILE_URL as a build ARG and expose it as ENV before `pnpm build:standalone`, so Modern.js picks it up when building the HTML template. k8s env still flows into the Node SSR process; the build-arg path guarantees correctness even when the runtime env is stripped. - deployment/build-docker.sh: forward MAP_TILE_URL through as a build-arg (default keeps the same-origin path). CI on the devwebzavod cluster can export MAP_TILE_URL=https://flights.test.aeroflot.ru/map/api/tile/{z}/{x}/{y}.jpeg before running build-docker.sh and the resulting image will serve tiles from the upstream the real Aeroflot ingress terminates.
102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { appTools, defineConfig } from "@modern-js/app-tools";
|
|
import { moduleFederationPlugin } from "@module-federation/modern-js";
|
|
|
|
const buildTarget = process.env["BUILD_TARGET"];
|
|
const isRemote = buildTarget === "remote";
|
|
|
|
// Runtime env values that must reach the client bundle. Rspack resolves
|
|
// `process.env` at BUILD time to an empty-ish polyfill in the browser, so
|
|
// any value we want per-deployment (e.g. MAP_TILE_URL pointing at a tile
|
|
// service that the local ingress doesn't proxy) has to be injected into
|
|
// the HTML as a window global. getEnv() picks up window.__ENV__ on the
|
|
// client. Evaluated once at Modern.js server start → pod env → HTML.
|
|
const PUBLIC_ENV_KEYS = ["MAP_TILE_URL"] as const;
|
|
const PUBLIC_ENV: Record<string, string> = {};
|
|
for (const k of PUBLIC_ENV_KEYS) {
|
|
const v = process.env[k];
|
|
if (typeof v === "string" && v.length > 0) PUBLIC_ENV[k] = v;
|
|
}
|
|
const PUBLIC_ENV_SCRIPT =
|
|
`window.__ENV__ = Object.assign(window.__ENV__ || {}, ${JSON.stringify(PUBLIC_ENV)});`;
|
|
|
|
export default defineConfig({
|
|
plugins: [appTools({ bundler: "rspack" }), moduleFederationPlugin()],
|
|
source: {
|
|
entriesDir: "./src",
|
|
},
|
|
html: {
|
|
favicon: "./config/public/favicon.ico",
|
|
tags: [
|
|
// Inline script runs before the main bundle so client-side getEnv()
|
|
// sees the pod's env (read when the SSR server starts).
|
|
{
|
|
tag: "script",
|
|
head: true,
|
|
append: false,
|
|
children: PUBLIC_ENV_SCRIPT,
|
|
},
|
|
{
|
|
tag: "link",
|
|
attrs: {
|
|
rel: "icon",
|
|
type: "image/png",
|
|
sizes: "32x32",
|
|
href: "/assets/img/favicon-32x32.png",
|
|
},
|
|
},
|
|
{
|
|
tag: "link",
|
|
attrs: {
|
|
rel: "icon",
|
|
type: "image/png",
|
|
sizes: "16x16",
|
|
href: "/assets/img/favicon-16x16.png",
|
|
},
|
|
},
|
|
{
|
|
tag: "link",
|
|
attrs: {
|
|
rel: "apple-touch-icon",
|
|
href: "/assets/img/favicon-touch.png",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
runtime: {
|
|
router: {
|
|
future: {
|
|
v7_startTransition: true,
|
|
v7_relativeSplatPath: true,
|
|
v7_fetcherPersist: true,
|
|
v7_normalizeFormMethod: true,
|
|
v7_partialHydration: true,
|
|
v7_skipActionErrorRevalidation: true,
|
|
},
|
|
},
|
|
},
|
|
server: {
|
|
ssr: {
|
|
mode: "stream",
|
|
},
|
|
},
|
|
tools: {
|
|
cssLoader: {
|
|
url: false,
|
|
},
|
|
// Explicit dev-server CORS headers. Silences the MF modern-js warning
|
|
// about empty devServer.headers (auto-assigning "*"). Production is
|
|
// unaffected — the real reverse proxy manages CORS.
|
|
devServer: {
|
|
headers: {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers":
|
|
"Content-Type, Authorization, Accept, Accept-Language",
|
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
},
|
|
},
|
|
},
|
|
output: {
|
|
distPath: { root: isRemote ? "dist/remote" : "dist/standalone" },
|
|
},
|
|
});
|