mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 15:31:11 +00:00
20a39d3d8a
Closes autodev existing-code Step 9 for cycle 2.
- Epic AZ-497 (Self-Hosted Satellite Tiles - SPA Integration) added
to _docs/02_tasks/_dependencies_table.md as the cycle-2 umbrella.
- AZ-498 (5 pts): self-hosted satellite tiles + drop map-type toggle.
Cross-workspace prereq: satellite-provider must add cookie-auth on
GET /tiles/{z}/{x}/{y} before merge (user files separately).
- AZ-499 (2 pts): mission-planner OWM env-var hardening + closes the
AZ-482 source-scan gap with a new owm_key_in_source banned-deps kind.
- Contract _docs/02_document/contracts/satellite-provider/tiles.md
v1.0.0 (draft): slippy-tile XYZ shape both sides commit to.
- _docs/_autodev_state.md: Step 9 closure note + advances pointer to
Step 10 (Implement) for cycle 2.
Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
6.6 KiB
Markdown
118 lines
6.6 KiB
Markdown
# Contract: satellite-provider tile serving
|
|
|
|
**Component**: satellite-provider
|
|
**Producer task**: TBD — separate AZAION ticket on `satellite-provider` workspace (user-filed)
|
|
**Consumer tasks**: AZ-498 — `_docs/02_tasks/todo/AZ-498_satellite_tile_swap.md` (suite/ui, cycle 2, epic AZ-497)
|
|
**Version**: 1.0.0
|
|
**Status**: draft
|
|
**Last Updated**: 2026-05-12
|
|
|
|
## Purpose
|
|
|
|
Describe the slippy-tile HTTP interface that the suite UI consumes to render
|
|
satellite imagery in `FlightMap` / `MiniMap`. Replaces the prior external-tile
|
|
dependencies (OpenStreetMap, Esri ArcGIS World Imagery). The endpoint is
|
|
served by `SatelliteProvider.Api` and backed by an on-disk + Google-Maps
|
|
download cache.
|
|
|
|
Frozen post-migration: SPA authentication for this endpoint MUST be **cookie-based**
|
|
(JWT delivered via `HttpOnly; Secure; SameSite=Lax` cookie on the same origin)
|
|
because Leaflet's `<TileLayer>` issues plain `<img>` requests and cannot attach
|
|
`Authorization: Bearer …` headers.
|
|
|
|
## Shape
|
|
|
|
### HTTP / RPC endpoints
|
|
|
|
| Method | Path | Request body | Response | Status codes |
|
|
|--------|-------------------------------|--------------|-------------------|---------------------|
|
|
| `GET` | `/tiles/{z}/{x}/{y}` | — | image bytes | 200, 401, 404, 503 |
|
|
|
|
**Path parameters**
|
|
|
|
| Name | Type | Required | Range / Constraint |
|
|
|------|---------|----------|--------------------------------------------------------|
|
|
| `z` | `int` | yes | `0 ≤ z ≤ 20` (slippy-tile zoom) |
|
|
| `x` | `int` | yes | `0 ≤ x < 2^z` (slippy-tile column) |
|
|
| `y` | `int` | yes | `0 ≤ y < 2^z` (slippy-tile row, TMS-y convention NO) |
|
|
|
|
Coordinates follow the Google Maps / OSM XYZ tiling scheme (NOT the inverted TMS
|
|
y-axis). Out-of-range coordinates SHOULD return 404.
|
|
|
|
**Response headers (on 200)**
|
|
|
|
| Header | Value |
|
|
|------------------|---------------------------------------------------------------|
|
|
| `Content-Type` | `image/jpeg` (image bytes from the `TileService`) |
|
|
| `Cache-Control` | `public, max-age=N` where N is set by `TileService` |
|
|
| `ETag` | strong ETag tied to the cached tile's content hash |
|
|
|
|
**Authentication**
|
|
|
|
- **Required**: yes (the endpoint is NOT public).
|
|
- **Mechanism (post-migration)**: cookie-based JWT.
|
|
- Cookie name: `satellite_auth` (TBD — defined by producer task).
|
|
- Attributes: `HttpOnly; Secure; SameSite=Lax` in production; `SameSite=Lax`
|
|
permitted over `http://localhost` for dev only.
|
|
- **Cross-origin behavior**: same-origin only. The SPA reaches this endpoint via
|
|
the suite ingress (nginx) on the SPA's origin; cross-origin direct calls from
|
|
`http://localhost:5173 → http://localhost:5100` will NOT carry the cookie and
|
|
will receive 401 in dev unless the developer disables auth locally.
|
|
|
|
**Status codes**
|
|
|
|
| Code | Meaning |
|
|
|------|-------------------------------------------------------------------|
|
|
| 200 | Cached or freshly downloaded tile; body = image bytes |
|
|
| 304 | (Optional) ETag match — body empty. UI MUST tolerate either 200 or 304. |
|
|
| 401 | Missing/invalid cookie — UI MUST treat as "user signed out" |
|
|
| 404 | Tile coordinates out of range OR upstream had no tile |
|
|
| 503 | Upstream (Google Maps) unavailable; UI MUST render placeholder |
|
|
|
|
## Invariants
|
|
|
|
- The endpoint URL pattern is `/tiles/{z}/{x}/{y}` exactly — never `/tiles/{z}/{y}/{x}`
|
|
(Esri-style) nor `/api/satellite/tiles/{z}/{x}/{y}`. This invariant survives
|
|
refactors and is asserted by both producer's integration tests and consumer's
|
|
blackbox tests.
|
|
- Image format is JPEG (Content-Type `image/jpeg`). Switching to PNG/WEBP is a
|
|
major-version change.
|
|
- The endpoint MUST honor `Cache-Control` and `ETag` headers on every 200; clients
|
|
rely on them to avoid re-fetching unchanged tiles during pan/zoom.
|
|
- Authentication failure MUST return 401, not 200 with an HTML body — Leaflet
|
|
would otherwise display a broken-image placeholder silently.
|
|
|
|
## Non-Goals
|
|
|
|
- Not covered: tile vector formats (`.pbf` / Mapbox Vector Tiles). This contract
|
|
is raster-only.
|
|
- Not covered: tile prewarming. Pre-warm uses the separate `POST /api/satellite/request`
|
|
endpoint (different contract, not consumed by the UI's `FlightMap`).
|
|
- Not covered: MGRS tile retrieval (returns 501 today; out of UI scope).
|
|
|
|
## Versioning Rules
|
|
|
|
- **Breaking** (major bump): change the path template, change the path-parameter
|
|
semantics (e.g., TMS-y), change `Content-Type`, remove a status code from the
|
|
set above, change the auth mechanism away from cookies.
|
|
- **Non-breaking** (minor bump): add a new optional query parameter, broaden the
|
|
zoom range, add a new status code in the 4xx/5xx space that consumers can
|
|
tolerate.
|
|
|
|
## Test Cases
|
|
|
|
| Case | Input | Expected | Notes |
|
|
|----------------------------|----------------------------------------|-----------------------------------------------------------|----------------------------------|
|
|
| valid-tile | `GET /tiles/15/9876/5432` w/ cookie | 200 + JPEG bytes + `Cache-Control` + `ETag` | producer + consumer cover |
|
|
| missing-cookie | `GET /tiles/15/9876/5432` w/o cookie | 401 | consumer must NOT retry |
|
|
| out-of-range-coord | `GET /tiles/3/8/0` (x ≥ 2^z) | 404 | consumer renders placeholder |
|
|
| etag-match | `GET /tiles/15/9876/5432` + `If-None-Match` | 304 OR 200 (server-policy dependent) | consumer tolerates both |
|
|
| upstream-503 | upstream Google Maps down | 503 | consumer renders placeholder |
|
|
| zoom-extreme | `GET /tiles/20/x/y` valid coords | 200 (or 404 if not cached and no on-demand) | consumer caps zoom at 20 |
|
|
|
|
## Change Log
|
|
|
|
| Version | Date | Change | Author |
|
|
|---------|------------|------------------------------------------------------------------------------|--------|
|
|
| 1.0.0 | 2026-05-12 | Initial draft; freezes the post-migration shape (cookie auth, XYZ scheme). | autodev (cycle 2 — suite/ui) |
|