mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 12:01:11 +00:00
[AZ-497] [AZ-498] [AZ-499] Cycle 2 New Task: epic, stories, contract draft
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>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# 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) |
|
||||
Reference in New Issue
Block a user