fix(map): complete live Cesium provider path
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Mission Core Map Page + Cesium integration plan
|
# 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
|
## Objective
|
||||||
|
|
||||||
@@ -144,9 +145,9 @@ browser cache, per-application TileCache, Docker volume or a second Gateway.
|
|||||||
|
|
||||||
### Shared renderer gate
|
### Shared renderer gate
|
||||||
|
|
||||||
`@nodedc/page-patterns` already publishes the runtime-neutral Map Page
|
`@nodedc/page-patterns` publishes the runtime-neutral Map Page definition and
|
||||||
definition, but the Cesium adapter is not a published package. Production reuse
|
`@nodedc/map-cesium-react@0.1.0` now owns the shared Cesium adapter consumed by
|
||||||
requires a shared reviewed package for:
|
Mission Core. The package boundary covers:
|
||||||
|
|
||||||
- provider startup and independent imagery/terrain/buildings readiness;
|
- provider startup and independent imagery/terrain/buildings readiness;
|
||||||
- Gateway-proxied Cesium resource creation;
|
- Gateway-proxied Cesium resource creation;
|
||||||
@@ -155,16 +156,17 @@ requires a shared reviewed package for:
|
|||||||
- elevated target and label presentation;
|
- elevated target and label presentation;
|
||||||
- camera and cache-intent application.
|
- camera and cache-intent application.
|
||||||
|
|
||||||
Foundry and Mission Core must consume the same package revision. Copying the
|
Foundry and Mission Core must remain on the same reviewed package revision.
|
||||||
Foundry application renderer into Mission Core is rejected. Cesium remains a
|
Copying the Foundry application renderer into Mission Core remains rejected.
|
||||||
lazy chunk loaded only when the map workspace opens.
|
Cesium is a lazy chunk loaded only when the map workspace opens.
|
||||||
|
|
||||||
## Credential and control-plane decision
|
## Credential and control-plane decision
|
||||||
|
|
||||||
Mission Core does not need to receive a Cesium token to render the map. The
|
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
|
existing platform token remains owned by Platform Map Gateway, and Mission Core
|
||||||
uses it only after Gateway reports a configured/verified state. The current
|
uses it only after Gateway reports a configured/verified state. The connected
|
||||||
network check did not independently verify that state.
|
local Gateway reports `ionConfigured=true` and serves the canonical assets
|
||||||
|
without exposing credentials.
|
||||||
|
|
||||||
The proposed `API и ключи` settings section is a write-only Platform
|
The proposed `API и ключи` settings section is a write-only Platform
|
||||||
control-plane client:
|
control-plane client:
|
||||||
@@ -335,15 +337,23 @@ unbounded reconnect loops.
|
|||||||
|
|
||||||
## Current blockers and evidence
|
## Current blockers and evidence
|
||||||
|
|
||||||
1. The Cesium adapter is not currently available through a published shared
|
1. The shared Cesium adapter, Mission Core BFF and real map-first workspace are
|
||||||
package; exact code reuse is blocked until that boundary exists.
|
admitted. Browser acceptance on `127.0.0.1:8000` reports `Карта готова`;
|
||||||
2. Mission Core lacks authenticated admin authority, so platform token rotation
|
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.
|
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.
|
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
|
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.
|
hide this condition.
|
||||||
|
|
||||||
None of these blockers requires a new product window, Engine embedding,
|
None of these blockers requires a new product window, Engine embedding,
|
||||||
|
|||||||
@@ -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}$")
|
SAFE_ERROR_CODE = re.compile(r"^[a-z][a-z0-9_]{0,95}$")
|
||||||
MAX_JSON_RESPONSE_BYTES: Final = 1024 * 1024
|
MAX_JSON_RESPONSE_BYTES: Final = 1024 * 1024
|
||||||
MAX_CACHE_TARGET_LENGTH: Final = 8192
|
MAX_CACHE_TARGET_LENGTH: Final = 8192
|
||||||
|
BING_GATEWAY_KEY_MARKER: Final = "nodedc-gateway"
|
||||||
|
|
||||||
REQUEST_HEADER_ALLOWLIST: Final = frozenset(
|
REQUEST_HEADER_ALLOWLIST: Final = frozenset(
|
||||||
{
|
{
|
||||||
@@ -325,7 +326,11 @@ def _validate_cache_target(target_url: str) -> None:
|
|||||||
)
|
)
|
||||||
if any(
|
if any(
|
||||||
key.lower() in CREDENTIAL_QUERY_KEYS
|
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(
|
raise HTTPException(
|
||||||
status_code=422,
|
status_code=422,
|
||||||
|
|||||||
@@ -250,6 +250,41 @@ def test_cache_rejects_credentials_non_https_and_non_loopback_clients() -> None:
|
|||||||
assert not requests
|
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:
|
def test_asset_route_is_a_fixed_allowlist() -> None:
|
||||||
service, requests = _service(
|
service, requests = _service(
|
||||||
lambda request: httpx.Response(
|
lambda request: httpx.Response(
|
||||||
|
|||||||
Reference in New Issue
Block a user