From 5a06194962495dc0023224f7080106688c0a0b44 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 29 Jul 2026 17:25:07 +0300 Subject: [PATCH] fix(map): complete live Cesium provider path --- ..._MISSION_CORE_MAP_PAGE_INTEGRATION_PLAN.md | 40 ++++++++++++------- src/k1link/web/map_api.py | 7 +++- tests/test_map_api.py | 35 ++++++++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/docs/21_MISSION_CORE_MAP_PAGE_INTEGRATION_PLAN.md b/docs/21_MISSION_CORE_MAP_PAGE_INTEGRATION_PLAN.md index fb0114b..2bd0eda 100644 --- a/docs/21_MISSION_CORE_MAP_PAGE_INTEGRATION_PLAN.md +++ b/docs/21_MISSION_CORE_MAP_PAGE_INTEGRATION_PLAN.md @@ -1,6 +1,7 @@ # Mission Core Map Page + Cesium integration plan -Status: proposed for product-owner acceptance, 2026-07-29 +Status: M0–M3 implemented for the local operator contour; operational +acceptance in progress, 2026-07-29 ## Objective @@ -144,9 +145,9 @@ browser cache, per-application TileCache, Docker volume or a second Gateway. ### Shared renderer gate -`@nodedc/page-patterns` already publishes the runtime-neutral Map Page -definition, but the Cesium adapter is not a published package. Production reuse -requires a shared reviewed package for: +`@nodedc/page-patterns` publishes the runtime-neutral Map Page definition and +`@nodedc/map-cesium-react@0.1.0` now owns the shared Cesium adapter consumed by +Mission Core. The package boundary covers: - provider startup and independent imagery/terrain/buildings readiness; - Gateway-proxied Cesium resource creation; @@ -155,16 +156,17 @@ requires a shared reviewed package for: - elevated target and label presentation; - camera and cache-intent application. -Foundry and Mission Core must consume the same package revision. Copying the -Foundry application renderer into Mission Core is rejected. Cesium remains a -lazy chunk loaded only when the map workspace opens. +Foundry and Mission Core must remain on the same reviewed package revision. +Copying the Foundry application renderer into Mission Core remains rejected. +Cesium is a lazy chunk loaded only when the map workspace opens. ## Credential and control-plane decision Mission Core does not need to receive a Cesium token to render the map. The existing platform token remains owned by Platform Map Gateway, and Mission Core -uses it only after Gateway reports a configured/verified state. The current -network check did not independently verify that state. +uses it only after Gateway reports a configured/verified state. The connected +local Gateway reports `ionConfigured=true` and serves the canonical assets +without exposing credentials. The proposed `API и ключи` settings section is a write-only Platform control-plane client: @@ -335,15 +337,23 @@ unbounded reconnect loops. ## Current blockers and evidence -1. The Cesium adapter is not currently available through a published shared - package; exact code reuse is blocked until that boundary exists. -2. Mission Core lacks authenticated admin authority, so platform token rotation +1. The shared Cesium adapter, Mission Core BFF and real map-first workspace are + admitted. Browser acceptance on `127.0.0.1:8000` reports `Карта готова`; + imagery, terrain and 3D buildings each report `Готов`, with no blocking map + error. +2. The connected Gateway is configured with assets `1`, `2` and `96188`. + Rendering the acceptance viewport increased the persistent live-cache count + from 3270 to 3282 without a browser credential or direct provider route. +3. Mission Core lacks authenticated admin authority, so platform token rotation cannot be exposed safely yet. -3. K1 currently has no admitted geodetic anchor, so live scanner targets are a +4. K1 currently has no admitted geodetic anchor, so live scanner targets are a later data-contract slice. -4. From the current Mac network, direct safe probes to the expected private +5. From the current Mac network, direct safe probes to the expected private Synology Map Gateway/Platform/AMD health endpoints timed out. End-to-end - acceptance requires the intended LAN route; no address will be hard-coded to + NAS/cache-only acceptance still requires the intended LAN route. The + currently connected development Gateway uses a persistent local Docker + volume, so it proves the browser/BFF/Gateway/provider contract but not the + final Synology failover path. No address is hard-coded in frontend state to hide this condition. None of these blockers requires a new product window, Engine embedding, diff --git a/src/k1link/web/map_api.py b/src/k1link/web/map_api.py index 15097f1..1a48f24 100644 --- a/src/k1link/web/map_api.py +++ b/src/k1link/web/map_api.py @@ -22,6 +22,7 @@ ALLOWED_ION_ASSET_IDS: Final = frozenset({1, 2, 96188}) SAFE_ERROR_CODE = re.compile(r"^[a-z][a-z0-9_]{0,95}$") MAX_JSON_RESPONSE_BYTES: Final = 1024 * 1024 MAX_CACHE_TARGET_LENGTH: Final = 8192 +BING_GATEWAY_KEY_MARKER: Final = "nodedc-gateway" REQUEST_HEADER_ALLOWLIST: Final = frozenset( { @@ -325,7 +326,11 @@ def _validate_cache_target(target_url: str) -> None: ) if any( key.lower() in CREDENTIAL_QUERY_KEYS - for key, _ in parse_qsl(parsed.query, keep_blank_values=True) + and not ( + key.lower() == "key" + and value == BING_GATEWAY_KEY_MARKER + ) + for key, value in parse_qsl(parsed.query, keep_blank_values=True) ): raise HTTPException( status_code=422, diff --git a/tests/test_map_api.py b/tests/test_map_api.py index 4f1d649..09e187d 100644 --- a/tests/test_map_api.py +++ b/tests/test_map_api.py @@ -250,6 +250,41 @@ def test_cache_rejects_credentials_non_https_and_non_loopback_clients() -> None: assert not requests +def test_cache_accepts_only_the_public_bing_gateway_key_marker() -> None: + service, requests = _service( + lambda request: httpx.Response( + 200, + headers={"content-type": "application/json"}, + content=b"{}", + ) + ) + + accepted = asyncio.run( + service.proxy_cache( + _request("/api/v1/map/gateway/cache"), + target_url=( + "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial" + "?key=nodedc-gateway" + ), + ) + ) + + assert isinstance(accepted, StreamingResponse) + assert requests[0].url.params["url"].endswith("key=nodedc-gateway") + + with pytest.raises(HTTPException) as exc_info: + asyncio.run( + service.proxy_cache( + _request("/api/v1/map/gateway/cache"), + target_url=( + "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial" + "?key=browser-secret" + ), + ) + ) + assert exc_info.value.status_code == 422 + + def test_asset_route_is_a_fixed_allowlist() -> None: service, requests = _service( lambda request: httpx.Response(