fix(geoportal-v2): proxy OpenFreeMap planet TileJSON to bypass origin block

Root cause traced today: tiles.openfreemap.org/planet (the openmaptiles
source's TileJSON ref inside the liberty + dark styles) returns 403 to
ANY request that carries a browser Origin header. Cloudflare hot-link
rule, presumably; bare curl (no Origin) gets 200 fine. Verified live
with `curl -H 'Origin: https://tools.beletage.ro' …/planet` → 403, and
with Playwright loading a minimal MapLibre test against openfreemap →
"CORS policy: No 'Access-Control-Allow-Origin' header is present".

Effect on the V2 panel: MapLibre fetches the liberty style fine, but the
openmaptiles vector source is defined as { url: ".../planet" } and
relies on a follow-up TileJSON fetch to learn the actual versioned tile
URL (e.g. /planet/20260513_001001_pt/{z}/{x}/{y}.pbf). That fetch dies
in the browser. No labels, no roads, no buildings — only the
natural_earth raster background renders and the page looks like an
empty cream sheet plus our PMTiles UAT outlines. That's exactly the
"harta nu se mai vede bine" complaint.

Other openfreemap endpoints (the style itself, sprites, glyphs, the
individual versioned tile PBFs) all work fine with an Origin header —
only /planet is blocked, so we only need to bypass that one.

Fix: GET /api/basemap-style/[id] fetches the style + every source
defined with `url:` server-side (no Origin → 200), inlines the resolved
`tiles[]`/zoom range into the source, and returns a self-contained
style. Browser only ever talks to tile endpoints directly afterwards,
which work. Liberty + dark basemaps in the V2 map-viewer now route
through this proxy. Cache-Control: 1h public + 1d SWR so we pick up
new openfreemap versions promptly without hammering on every map load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude VM
2026-05-23 22:52:57 +03:00
parent d70442e26f
commit a2581de599
2 changed files with 133 additions and 2 deletions
+9 -2
View File
@@ -43,8 +43,15 @@ type BasemapDef =
| { type: "raster"; tiles: string[]; attribution: string; tileSize: number; maxzoom?: number };
const BASEMAPS: Record<BasemapId, BasemapDef> = {
liberty: { type: "style", url: "https://tiles.openfreemap.org/styles/liberty" },
dark: { type: "style", url: "https://tiles.openfreemap.org/styles/dark" },
// OpenFreeMap styles routed through our own /api/basemap-style/* proxy
// because tiles.openfreemap.org/planet (the openmaptiles source's
// TileJSON) returns 403 to any request carrying a browser Origin
// header. Without the proxy, MapLibre can't resolve the planet tile
// URL and the map ends up cream-blank with only the natural_earth
// raster + our PMTiles overlay. The proxy fetches /planet server-
// side (no Origin → 200) and inlines the resolved `tiles` array.
liberty: { type: "style", url: "/api/basemap-style/liberty" },
dark: { type: "style", url: "/api/basemap-style/dark" },
satellite: {
type: "raster",
tiles: [