From d3747469128b988ad25f849e8a46858ca5b31ac6 Mon Sep 17 00:00:00 2001 From: bbrauzzi Date: Thu, 4 Jun 2026 19:51:13 +0200 Subject: [PATCH 1/3] fix: route prod quicklook thumbnails to zipper to avoid CORS-less redirect Thumbnail hrefs point at datahub.creodias.eu, which 301-redirects to the identical path on zipper.creodias.eu. Only the final zipper response carries CORS headers; the 301 from datahub does not. Since the browser enforces CORS on every hop of a redirect chain, the maplibre WebGL texture fetch (crossOrigin 'anonymous') was blocked at the redirect before reaching the CORS-* image, breaking thumbnails on GitHub Pages. In prod, thumbForMap now rewrites the datahub host straight to zipper, skipping the redirect entirely. Dev still proxies via /thumb (followRedirects). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/MapView.test.tsx | 4 ++-- src/components/MapView.tsx | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/MapView.test.tsx b/src/components/MapView.test.tsx index e15e62a..4319aa4 100644 --- a/src/components/MapView.test.tsx +++ b/src/components/MapView.test.tsx @@ -331,11 +331,11 @@ describe('MapView quicklook overlays', () => { ) }) - it('loads creodias quicklook URLs directly in prod', () => { + it('rewrites datahub quicklook URLs to zipper in prod (skips the CORS-less redirect)', () => { vi.stubEnv('DEV', false) renderMapView({ selectedIds: new Set(['S2_A']) }) expect(screen.getByTestId('source-quicklook-S2_A').getAttribute('data-url')).toBe( - 'https://datahub.creodias.eu/odata/v1/preview.png', + 'https://zipper.creodias.eu/odata/v1/preview.png', ) }) }) diff --git a/src/components/MapView.tsx b/src/components/MapView.tsx index 6448911..77d5b64 100644 --- a/src/components/MapView.tsx +++ b/src/components/MapView.tsx @@ -33,14 +33,21 @@ interface Props { type Corner = { lng: number; lat: number } -// In dev, route creodias quicklook URLs through the Vite /thumb proxy so the -// WebGL texture loads same-origin (no CORS / redirect quirks). In prod the -// final image response is CORS `*`, so we load it directly. +// Quicklook URLs point at datahub.creodias.eu, which 301-redirects to the same +// path on zipper.creodias.eu. The redirect response carries no CORS header, so a +// cross-origin WebGL texture fetch fails on the redirect hop even though the final +// zipper response is CORS `*`. In dev we proxy via /thumb (followRedirects); in prod +// we rewrite straight to zipper to skip the redirect. function thumbForMap(href: string): string { - if (!import.meta.env.DEV) return href try { const u = new URL(href, window.location.origin) - if (u.hostname.endsWith('creodias.eu')) return `/thumb${u.pathname}${u.search}` + if (u.hostname.endsWith('creodias.eu')) { + if (import.meta.env.DEV) return `/thumb${u.pathname}${u.search}` + if (u.hostname === 'datahub.creodias.eu') { + u.hostname = 'zipper.creodias.eu' + return u.toString() + } + } return href } catch { return href From c8b7d8787a13273de385d23955bdcef812073b16 Mon Sep 17 00:00:00 2001 From: bbrauzzi Date: Thu, 4 Jun 2026 20:02:01 +0200 Subject: [PATCH 2/3] docs: add MIT license, live demo link, badges; correct CORS note - Add LICENSE (MIT) and "license"/"homepage"/"repository" fields in package.json. - Add a License section and a live demo link to the GitHub Pages deployment. - Add CI, demo, license, and stack badges to the README header. - Correct the CORS note: the STAC API sends Access-Control-Allow-Origin: *, so no proxy is needed for data; document the datahub->zipper thumbnail redirect and the host-rewrite fix instead. Co-Authored-By: Claude Opus 4.8 (1M context) --- LICENSE | 21 +++++++++++++++++++++ README.md | 31 ++++++++++++++++++++++++++----- package.json | 6 ++++++ 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0462994 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 bbrauzzi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d0c4eac..d0c921f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,18 @@ # MapExplorer — ESA data search portal via STAC +[![CI](https://github.com/bbrauzzi/map-explorer/actions/workflows/ci.yml/badge.svg)](https://github.com/bbrauzzi/map-explorer/actions/workflows/ci.yml) +[![Live demo](https://img.shields.io/badge/demo-GitHub%20Pages-2ea44f)](https://bbrauzzi.github.io/map-explorer/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![React](https://img.shields.io/badge/React-18-61dafb?logo=react&logoColor=white)](https://react.dev) +[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org) +[![Vite](https://img.shields.io/badge/Vite-5-646cff?logo=vite&logoColor=white)](https://vite.dev) + Web app for simple, filtered exploration of the **Copernicus Data Space Ecosystem STAC catalog** (ESA data: Sentinel-1/2/3/5P/6, Copernicus Contributing Missions, CLMS, ...). +**🌍 Live demo:** + ![MapExplorer preview](docs/preview.png) ## Features @@ -37,12 +46,20 @@ authentication (out of scope for this portal). ### Note on CORS -In **development**, requests go through a Vite proxy (`/stac` → real endpoint), -configured in `vite.config.ts`, to avoid CORS issues. +In **development**, requests go through Vite proxies (`/stac` → STAC API, `/thumb` → +quicklook host), configured in `vite.config.ts`, so everything loads same-origin. + +In **production** (e.g. GitHub Pages) no proxy is needed for data: the STAC API at +`https://stac.dataspace.copernicus.eu/v1` sends `Access-Control-Allow-Origin: *`, so the +app calls the absolute endpoint directly (see `src/config.ts`). -In **production**, the app calls the absolute endpoint directly (see `src/config.ts`). -If the API does not send CORS headers, you will need a small reverse proxy in front -(e.g. the same rule from `vite.config.ts` on Nginx/Cloudflare Worker). +The one catch is **quicklook thumbnails**: their hrefs point at `datahub.creodias.eu`, +which 301-redirects to the same path on `zipper.creodias.eu`. Only the final response +carries CORS headers — the redirect itself does not — so a cross-origin WebGL texture +fetch is blocked on the redirect hop. In prod the app rewrites the host straight to +`zipper.creodias.eu` to skip the redirect (`thumbForMap` in `src/components/MapView.tsx`). +If CreoDIAS ever changes that mapping or gates `zipper` behind auth, fall back to a small +reverse proxy (e.g. the `/thumb` rule from `vite.config.ts` on Nginx/Cloudflare Worker). ## Structure @@ -56,3 +73,7 @@ src/ components/ # FilterPanel, MapView, ResultList, ItemDetail, SavedSearchesPanel App.tsx # 3-zone layout and shared state ``` + +## License + +Released under the [MIT License](LICENSE). diff --git a/package.json b/package.json index 4431fed..8db0a86 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,12 @@ "version": "1.0.0", "type": "module", "description": "Portale di ricerca per dati ESA via STAC (Copernicus Data Space)", + "license": "MIT", + "homepage": "https://bbrauzzi.github.io/map-explorer/", + "repository": { + "type": "git", + "url": "git+https://github.com/bbrauzzi/map-explorer.git" + }, "scripts": { "dev": "vite", "build": "tsc && vite build", From 8d65b0ab89f1acae95ede181356da3565ccd5ed5 Mon Sep 17 00:00:00 2001 From: bbrauzzi Date: Thu, 4 Jun 2026 20:06:09 +0200 Subject: [PATCH 3/3] chore: bump version to 1.0.1 and translate package description to English Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1fb3ef..bee02b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "map-explorer", - "version": "0.1.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "map-explorer", - "version": "0.1.0", + "version": "1.0.1", "dependencies": { "maplibre-gl": "^4.7.1", "react": "^18.3.1", diff --git a/package.json b/package.json index 8db0a86..2f0f847 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "map-explorer", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", - "description": "Portale di ricerca per dati ESA via STAC (Copernicus Data Space)", + "description": "Search portal for ESA data via STAC (Copernicus Data Space Ecosystem)", "license": "MIT", "homepage": "https://bbrauzzi.github.io/map-explorer/", "repository": {