diff --git a/ShadowBroker_v0.3.zip.REMOVED.git-id b/ShadowBroker_v0.3.zip.REMOVED.git-id deleted file mode 100644 index ee016b4..0000000 --- a/ShadowBroker_v0.3.zip.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -f5eaed9b0fba2bd73f0597d9659b616cae4291b5 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index de37148..097da07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,11 +19,13 @@ services: frontend: build: context: ./frontend + args: + # Optional: set this to your backend's external URL if using custom ports + # e.g. http://192.168.1.50:9096 — leave empty to auto-detect from browser + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-} container_name: shadowbroker-frontend ports: - "3000:3000" - environment: - - NEXT_PUBLIC_API_URL=http://localhost:8000 depends_on: - backend restart: unless-stopped diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 82bd2f7..2b3fd73 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -11,6 +11,10 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED 1 +# NEXT_PUBLIC_* vars must exist at build time for Next.js to inline them. +# Default empty = auto-detect from browser hostname at runtime. +ARG NEXT_PUBLIC_API_URL="" +ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL RUN npm run build FROM base AS runner diff --git a/frontend/src/components/MaplibreViewer.tsx b/frontend/src/components/MaplibreViewer.tsx index 155d605..f2b3c34 100644 --- a/frontend/src/components/MaplibreViewer.tsx +++ b/frontend/src/components/MaplibreViewer.tsx @@ -373,15 +373,37 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele const onMapLoad = useCallback((e: any) => { const map = e.target; + // Track which images are still loading so we can retry on styleimagemissing + const pendingImages: Record = {}; + const loadImg = (id: string, url: string) => { if (!map.hasImage(id)) { + pendingImages[id] = url; const img = new Image(); img.crossOrigin = "anonymous"; img.src = url; - img.onload = () => map.addImage(id, img); + img.onload = () => { + if (!map.hasImage(id)) map.addImage(id, img); + delete pendingImages[id]; + }; } }; + // Suppress "image not found" warnings — retry when the async load finishes + map.on('styleimagemissing', (ev: any) => { + const id = ev.id; + const url = pendingImages[id]; + if (url) { + const img = new Image(); + img.crossOrigin = "anonymous"; + img.src = url; + img.onload = () => { + if (!map.hasImage(id)) map.addImage(id, img); + delete pendingImages[id]; + }; + } + }); + // Legacy generic plane icons (still used as fallbacks) loadImg('svgPlaneCyan', svgPlaneCyan); loadImg('svgPlaneYellow', svgPlaneYellow); diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 4e45682..231264e 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1 +1,28 @@ -export const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; +// NEXT_PUBLIC_* vars are baked at build time in Next.js, so setting them +// in docker-compose `environment` has no effect at runtime. Instead we +// auto-detect: use the browser's current hostname with a configurable port +// so the dashboard works on localhost, LAN IPs, and custom Docker port maps +// without any code changes. +// +// Override order: +// 1. Build-time NEXT_PUBLIC_API_URL (for advanced users who rebuild the image) +// 2. Runtime auto-detect from window.location.hostname + port 8000 + +function resolveApiBase(): string { + // Build-time override (works when image is rebuilt with the env var) + if (process.env.NEXT_PUBLIC_API_URL) { + return process.env.NEXT_PUBLIC_API_URL; + } + + // Server-side rendering: fall back to localhost + if (typeof window === "undefined") { + return "http://localhost:8000"; + } + + // Client-side: use the same hostname the user is browsing on + const proto = window.location.protocol; + const host = window.location.hostname; + return `${proto}//${host}:8000`; +} + +export const API_BASE = resolveApiBase();