diff --git a/assets/js/map.ts b/assets/js/map.ts index d957844..afe172b 100644 --- a/assets/js/map.ts +++ b/assets/js/map.ts @@ -204,13 +204,61 @@ let MapAPRSMap = { } } - // Add OpenStreetMap tile layer + // Add OpenStreetMap tile layer with improved settings try { - const tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { - attribution: - '© OpenStreetMap contributors | APRS.me', + // Tile provider options + const tileProviders = { + osm: { + url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + attribution: '© OpenStreetMap contributors | APRS.me', + subdomains: ['a', 'b', 'c'] + }, + osmDE: { + url: "https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png", + attribution: '© OpenStreetMap contributors | APRS.me', + subdomains: ['a', 'b', 'c'] + }, + carto: { + url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", + attribution: '© OpenStreetMap contributors © CARTO | APRS.me', + subdomains: ['a', 'b', 'c', 'd'] + } + }; + + // Use OSM by default, but could be configured + const provider = tileProviders.osm; + + const tileLayer = L.tileLayer(provider.url, { + attribution: provider.attribution, maxZoom: 19, + subdomains: provider.subdomains, + tileSize: 256, + keepBuffer: 2, + updateWhenZooming: false, + updateInterval: 200, + errorTileUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' }); + + // Add event handler for tile errors with retry logic + let retryCount = new Map(); + tileLayer.on('tileerror', function(error: any) { + const src = error.tile.src; + const count = retryCount.get(src) || 0; + + if (count < 3) { + console.warn(`Tile load error (attempt ${count + 1}/3):`, src); + retryCount.set(src, count + 1); + + // Exponential backoff + setTimeout(() => { + error.tile.src = src + (src.includes('?') ? '&' : '?') + '_retry=' + Date.now(); + }, Math.pow(2, count) * 500); + } else { + console.error('Tile failed after 3 attempts:', src); + retryCount.delete(src); + } + }); + tileLayer.addTo(self.map); } catch (error) { self.errors!.push("Tile layer failed: " + (error instanceof Error ? error.message : error)); diff --git a/lib/aprsme_web/router.ex b/lib/aprsme_web/router.ex index 053e151..2f1e4b2 100644 --- a/lib/aprsme_web/router.ex +++ b/lib/aprsme_web/router.ex @@ -17,7 +17,7 @@ defmodule AprsmeWeb.Router do plug :put_secure_browser_headers, %{ "content-security-policy" => - "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.sentry-cdn.com https://unpkg.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https: blob:; font-src 'self' data:; connect-src 'self' wss: https://*.ingest.sentry.io https://*.sentry.io https://nominatim.openstreetmap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org; media-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; frame-src 'self'; manifest-src 'self'; worker-src 'self' blob:" + "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.sentry-cdn.com https://unpkg.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https: http: blob:; font-src 'self' data:; connect-src 'self' wss: https://*.ingest.sentry.io https://*.sentry.io https://nominatim.openstreetmap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org https://*.tile.openstreetmap.de https://*.basemaps.cartocdn.com; media-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; frame-src 'self'; manifest-src 'self'; worker-src 'self' blob:" } plug :fetch_current_user