feat(map): add cache-first Cesium gateway and AMD egress
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
|
||||
## Что делает
|
||||
|
||||
- хранит master `CESIUM_ION_TOKEN` только в process environment;
|
||||
- выдаёт браузеру только asset-scoped endpoint token для allowlisted ion assets;
|
||||
- хранит master Cesium Ion token только в private runtime storage: первоначально он может прийти из server environment, а после admin rotation — из закрытого файла в NAS volume;
|
||||
- никогда не выдаёт browser-у master, asset-scoped Cesium token или Bing key: endpoint response содержит только публичный provider URL, а Gateway подставляет credential в свой upstream request;
|
||||
- проксирует и кэширует tile/3D Tiles/terrain resources через `GET /api/map/cache?url=<https-url>`;
|
||||
- может отдать перенесённый локальный Engine snapshot через `MAP_GATEWAY_LEGACY_CACHE_HOSTS`, но никогда не догружает для него cache miss из сети;
|
||||
- работает с persistent volume, а не с Git или Docker image layer;
|
||||
- при upstream outage отдаёт ранее сохранённый cache object; offline режим включается только для providers, явно разрешённых их лицензией;
|
||||
- защищает proxy allowlist-ом upstream hosts, HTTPS-only правилом, лимитами размера и token-free cache key;
|
||||
- ведёт лёгкий persistent cache index для LRU eviction и health/stats.
|
||||
- ведёт лёгкий persistent cache index и health/stats без автоматического удаления уже записанных tiles.
|
||||
|
||||
## API
|
||||
|
||||
@@ -21,23 +21,31 @@ GET /api/map/ion/assets/:assetId/endpoint
|
||||
GET|HEAD /api/map/cache?url=<encoded-upstream-url>
|
||||
```
|
||||
|
||||
`/api/map/ion/assets/:assetId/endpoint` разрешает только `CESIUM_ION_ASSET_ALLOWLIST`. Ответ содержит runtime asset token, а не master token. Browser использует endpoint вместе с Cesium `DefaultProxy`, направляющим resource requests в `/api/map/cache`.
|
||||
Внутренний admin route `GET|PUT /api/map/admin/cesium-ion` не является Browser API: он доступен только из Foundry по HMAC. Ключ подписи создаётся и хранится root-owned deploy runner в `/volume1/docker/nodedc-platform/secrets/map-gateway-admin-secret`, а оба сервиса получают только read-only file mount. Он не является `.env`-значением, настройкой Foundry или Cesium credential. Перед записью `PUT` проверяет candidate token через Cesium asset endpoints `1` (terrain), `2` (imagery) и `96188` (3D Buildings). Не прошедшее проверку значение не заменяет рабочее. Успешный token записывается атомарно в `${MAP_CACHE_DIR}/secrets/cesium-ion-token` с mode `0600`; ответ содержит только факт конфигурации, safe verification state и audit metadata — никогда само значение.
|
||||
|
||||
Ion endpoint metadata также сохраняется в persistent volume с файловыми правами `0600`. Это нужно для cold start в offline: Cesium получает сохранённый asset URL и asset-scoped token, а proxy отвечает только из ранее записанного cache. В metadata никогда не записывается master token.
|
||||
`/api/map/ion/assets/:assetId/endpoint` разрешает только `CESIUM_ION_ASSET_ALLOWLIST`. Ответ не содержит credential: Browser использует публичный provider URL вместе с Cesium `DefaultProxy`, направляющим resource requests в `/api/map/cache`. Gateway валидирует scope URL, удаляет любые credential query parameters из browser request и добавляет соответствующий server-side credential только перед обращением к provider.
|
||||
|
||||
Ion endpoint metadata с asset credential сохраняется только в private persistent storage с файловыми правами `0600`. Это нужно для cold start и offline: Browser по-прежнему получает только sanitised URL, а Gateway отвечает из ранее записанного cache. В metadata никогда не записывается master token; NAS backup этого каталога считается service-sensitive.
|
||||
|
||||
## Cache modes
|
||||
|
||||
- `readwrite` — свежий cache hit отдаётся сразу; stale object обновляется online; при upstream failure возвращается stale copy;
|
||||
- `readonly` — использует cache, но не записывает новый;
|
||||
- `readwrite` — при включённом `Не перезаписывать cache` отдаёт уже записанный object прямо из общего TileCache; только miss идёт к официальному upstream и атомарно дописывается. Явный `refresh` viewport получает live response и заменяет объект;
|
||||
- `readonly` — отдаёт ранее записанный object из TileCache; новый object через Gateway может быть показан live, но не записывается;
|
||||
- `offline` — никогда не идёт наружу; cache miss возвращает `504`.
|
||||
|
||||
`nodedc_cache_refresh=1` — единственный путь заменить уже записанный объект. Без него существующий object всегда отдаётся прямо с NAS и вообще не обращается к Ion/AMD/VPN. Foundry создаёт refresh либо когда Application явно разрешил обновление cache, либо для одноразового действия «Обновить текущий viewport». При достижении `MAP_CACHE_MAX_MB` Gateway не удаляет LRU-объекты: новый miss продолжает live-маршрут без записи с `x-nodedc-map-cache: live-pass-through-cache-full`.
|
||||
|
||||
Первый cold miss начинает стримиться клиенту сразу после provider headers и одновременно записывается в private temp-файл. Только полностью полученный object публикуется через atomic rename и durable `index.json`; partial/error никогда не становится cache entry. Одинаковые параллельные misses делят один upstream download, а отмена одного browser request не прерывает server-owned fill для других инстансов. Warm hit не меняет index. Параллельные новые объекты объединяются в минимальное число полных index snapshots вместо одного O(N) rewrite на каждый tile.
|
||||
|
||||
`offline` дополнительно требует, чтобы hostname был явно указан в `MAP_GATEWAY_OFFLINE_PROVIDER_ALLOWLIST`. По умолчанию список пуст: Gateway не превращает Cesium ion или public OSM tile service в offline distribution. Это осознанная provider policy, а не техническая ошибка cache.
|
||||
|
||||
Range requests пока transparently проксируются и не записываются как partial cache object. Полные GET responses кэшируются. Это безопасная начальная граница для terrain/3D Tiles; отдельным следующим шагом добавляется range-aware chunk cache после замеров реальных GOS/3D Tiles assets.
|
||||
|
||||
## Persistent storage
|
||||
|
||||
Platform Compose использует два внешних named volume: mutable `nodedc-platform_map-live-tile-cache` монтируется в `/var/lib/nodedc-map-live-cache`, read-only snapshot `nodedc-platform_map-offline-snapshot` — в `/var/lib/nodedc-map-offline-snapshot`. Их содержимое намеренно исключено из Git. Внешний volume не удаляется через `docker compose down -v`; очистка требует явного `docker volume rm` при остановленном Gateway. При необходимости offline seed доставляется отдельным export/import artifact, а не коммитом runtime cache.
|
||||
Production Platform Compose использует два NAS host directories: mutable `/volume1/docker/nodedc-platform/map-gateway/live-tile-cache` монтируется в `/var/lib/nodedc-map-live-cache`, read-only `/volume1/docker/nodedc-platform/map-gateway/offline-snapshot` — в `/var/lib/nodedc-map-offline-snapshot`. Их создаёт root-owned deploy runner при первом Map Gateway rollout. Они намеренно исключены из Git и artifact; Compose teardown их не удаляет. При необходимости offline seed доставляется отдельным export/import artifact, а не коммитом runtime cache.
|
||||
|
||||
Внутри mutable volume имеется service-sensitive подкаталог `secrets/`: там лежит только текущий Cesium Ion master token и metadata последнего изменения. Это не TileCache, не часть offline export и не объект SMB-операций. Gateway создаёт его с directory mode `0700`, files `0600`; не включать его в backup, export или diagnostic archive без отдельной процедуры секретов.
|
||||
|
||||
Кэш не способен создать участок карты, который никогда не был скачан. Для целевых офлайн-регионов нужен отдельный licensed dataset/provider и его `seed/prefetch` job: он заранее проходит разрешённые assets, zoom-диапазоны и географические области, записывает их в тот же persistent volume и формирует проверяемый export/import artifact. Эта задача не смешивается с runtime gateway и не помещает binary tiles в историю репозитория.
|
||||
|
||||
@@ -66,9 +74,15 @@ npm run import:engine-cache -- /absolute/path/to/nodedc-data/api/cesium/manifest
|
||||
## Required environment
|
||||
|
||||
```text
|
||||
# Optional migration bootstrap; the private token file is preferred after an
|
||||
# admin has configured it through Foundry.
|
||||
CESIUM_ION_TOKEN=
|
||||
CESIUM_ION_ASSET_ALLOWLIST=1,96188
|
||||
MAP_GATEWAY_UPSTREAM_ALLOWLIST=api.cesium.com,assets.ion.cesium.com,tile.openstreetmap.org
|
||||
# Production: runner-owned NODEDC_MAP_GATEWAY_ADMIN_SECRET_FILE is mounted
|
||||
# automatically. Do not put the secret into an env file.
|
||||
CESIUM_ION_ASSET_ALLOWLIST=1,2,96188
|
||||
CESIUM_ION_ENDPOINT_TTL_SECONDS=300
|
||||
CESIUM_ION_ENDPOINT_REFRESH_AHEAD_SECONDS=60
|
||||
MAP_GATEWAY_UPSTREAM_ALLOWLIST=api.cesium.com,assets.ion.cesium.com,tile.openstreetmap.org,dev.virtualearth.net,ecn.t0.tiles.virtualearth.net,ecn.t1.tiles.virtualearth.net,ecn.t2.tiles.virtualearth.net,ecn.t3.tiles.virtualearth.net
|
||||
MAP_GATEWAY_LEGACY_CACHE_HOSTS=ecn.t0.tiles.virtualearth.net,ecn.t1.tiles.virtualearth.net,ecn.t2.tiles.virtualearth.net,ecn.t3.tiles.virtualearth.net
|
||||
MAP_GATEWAY_OFFLINE_PROVIDER_ALLOWLIST=
|
||||
MAP_CACHE_DIR=/var/lib/nodedc-map-cache
|
||||
@@ -76,12 +90,13 @@ MAP_CACHE_MODE=readwrite
|
||||
```
|
||||
# Cache topology
|
||||
|
||||
`MAP_CACHE_DIR` is the mutable **live** cache. In the Platform compose profile it
|
||||
is mounted as `map-live-tile-cache` and is the only store that can receive new
|
||||
upstream objects.
|
||||
`MAP_CACHE_DIR` is the mutable **live** cache. In the production Platform
|
||||
compose profile it is bind-mounted from the NAS path
|
||||
`/volume1/docker/nodedc-platform/map-gateway/live-tile-cache` and is the only
|
||||
store that can receive new upstream objects.
|
||||
|
||||
`MAP_OFFLINE_SNAPSHOT_DIR` is optional and mounted read-only as
|
||||
`map-offline-snapshot`. It is populated only by the controlled Engine cache
|
||||
import. Requests marked with the `offline` cache profile are served exclusively
|
||||
from this snapshot; a missing tile returns a cache miss and never reaches an
|
||||
upstream provider.
|
||||
`/volume1/docker/nodedc-platform/map-gateway/offline-snapshot`. It is
|
||||
populated only by the controlled Engine cache import. Requests marked with the
|
||||
`offline` cache profile are served exclusively from this snapshot; a missing
|
||||
tile returns a cache miss and never reaches an upstream provider.
|
||||
|
||||
Reference in New Issue
Block a user