From a1bbe850632ef48ecd2fb57fafe987f0d30231ff Mon Sep 17 00:00:00 2001 From: Paul Faure Date: Fri, 20 Mar 2026 10:50:53 -0400 Subject: [PATCH 1/2] fix(photo-addon): add referrer policy for OSM map tiles OSM's tile servers require a Referer header, but oCIS may set a strict Referrer-Policy that strips it, causing intermittent 403 errors on map tiles. Fix: inject a meta referrer tag and set referrerPolicy on each tile image element. Failed tiles retry once after 500ms. Co-Authored-By: Claude Opus 4.6 --- .../src/components/PhotoMap.vue | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/web-app-photo-addon/src/components/PhotoMap.vue b/packages/web-app-photo-addon/src/components/PhotoMap.vue index d59c3f6d..df58cbff 100644 --- a/packages/web-app-photo-addon/src/components/PhotoMap.vue +++ b/packages/web-app-photo-addon/src/components/PhotoMap.vue @@ -473,8 +473,36 @@ function initMap() { prefetchVisibleClusters() }) - // Add OpenStreetMap tile layer (noWrap prevents world map repeating) - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + // OSM tile servers require a Referer header. Ensure referrer policy allows it. + let metaReferrer = document.querySelector('meta[name="referrer"]') as HTMLMetaElement | null + if (!metaReferrer) { + metaReferrer = document.createElement('meta') + metaReferrer.name = 'referrer' + metaReferrer.content = 'no-referrer-when-downgrade' + document.head.appendChild(metaReferrer) + } else if (metaReferrer.content === 'no-referrer' || metaReferrer.content === 'same-origin') { + // Temporarily relax for OSM tiles — restored on unmount + metaReferrer.dataset.originalContent = metaReferrer.content + metaReferrer.content = 'no-referrer-when-downgrade' + } + + // Tile layer with retry on 403 (transient referrer issues) + const OsmTileLayer = L.TileLayer.extend({ + createTile(coords: any, done: any) { + const tile = L.TileLayer.prototype.createTile.call(this, coords, done) as HTMLImageElement + tile.referrerPolicy = 'no-referrer-when-downgrade' + const originalSrc = tile.src + tile.addEventListener('error', () => { + // Retry once after a short delay + if (!tile.dataset.retried) { + tile.dataset.retried = '1' + setTimeout(() => { tile.src = originalSrc }, 500) + } + }, { once: false }) + return tile + } + }) + new OsmTileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19, keepBuffer: 2, @@ -780,6 +808,12 @@ onUnmounted(() => { activeTooltip.remove() activeTooltip = null } + // Restore original referrer policy if we changed it + const metaRef = document.querySelector('meta[name="referrer"]') as HTMLMetaElement | null + if (metaRef?.dataset.originalContent) { + metaRef.content = metaRef.dataset.originalContent + delete metaRef.dataset.originalContent + } // Clean up injected CSS (only if no other PhotoMap instances) // Note: We leave the CSS in place since it's idempotent and shared // Removing it could break other instances if multiple PhotoMaps exist From 4fe0d170c601ae13ec8115b8ec1bab24d606c3a1 Mon Sep 17 00:00:00 2001 From: Paul Faure Date: Tue, 24 Mar 2026 22:05:47 -0400 Subject: [PATCH 2/2] fix(photo-addon): fix TypeScript errors in OSM tile layer Replace L.TileLayer.extend() with event-based approach using tileloadstart/tileerror events. The previous approach accessed the protected createTile method, causing TypeScript errors in CI. Co-Authored-By: Claude Opus 4.6 --- .../src/components/PhotoMap.vue | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/web-app-photo-addon/src/components/PhotoMap.vue b/packages/web-app-photo-addon/src/components/PhotoMap.vue index df58cbff..c3597099 100644 --- a/packages/web-app-photo-addon/src/components/PhotoMap.vue +++ b/packages/web-app-photo-addon/src/components/PhotoMap.vue @@ -486,29 +486,28 @@ function initMap() { metaReferrer.content = 'no-referrer-when-downgrade' } - // Tile layer with retry on 403 (transient referrer issues) - const OsmTileLayer = L.TileLayer.extend({ - createTile(coords: any, done: any) { - const tile = L.TileLayer.prototype.createTile.call(this, coords, done) as HTMLImageElement - tile.referrerPolicy = 'no-referrer-when-downgrade' - const originalSrc = tile.src - tile.addEventListener('error', () => { - // Retry once after a short delay - if (!tile.dataset.retried) { - tile.dataset.retried = '1' - setTimeout(() => { tile.src = originalSrc }, 500) - } - }, { once: false }) - return tile - } - }) - new OsmTileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + // Tile layer with referrer policy fix and retry on 403 + const tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19, keepBuffer: 2, noWrap: true, }).addTo(map) + // Set referrer policy on each tile and retry on failure + tileLayer.on('tileloadstart', (event: L.TileEvent) => { + const tile = event.tile as HTMLImageElement + tile.referrerPolicy = 'no-referrer-when-downgrade' + }) + tileLayer.on('tileerror', (event: L.TileErrorEvent) => { + const tile = event.tile as HTMLImageElement + if (!tile.dataset.retried) { + tile.dataset.retried = '1' + const src = tile.src + setTimeout(() => { tile.src = src }, 500) + } + }) + // Add photo markers addPhotoMarkers()