diff --git a/README.md b/README.md index 3d89d86..ed3e7ff 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Tabular data and processed image cutouts are available for download. Animated GIF of data download from the MinIO service

-➡️ **[MinIO Service Link](https://ruggedly-quaky-maricruz.ngrok-free.app/login)** +➡️ **[MinIO Service Link](https://3t611xfvhvp2.share.zrok.io)** **Instructions & Examples:** * Data access instructions: **[Data Access Notebook](./notebooks/how_to.ipynb)**. * Additional example notebooks (database exploration, proposal planning): `notebooks/` folder, including **[Proposal Planning Notebook](./notebooks/proposals/proposals.ipynb)**. diff --git a/dashboard/src/api.ts b/dashboard/src/api.ts index 1a0f6ea..1a18429 100644 --- a/dashboard/src/api.ts +++ b/dashboard/src/api.ts @@ -86,12 +86,20 @@ export const loadCutouts = (): Promise => fetchJson }).env?.VITE_MINIO_ENDPOINT; -const RAW_ENDPOINT: string | undefined = "nonarithmetically-undeliberating-janelle.ngrok-free.app"; +const RAW_ENDPOINT: string | undefined = "l5s5a0sibv6w.share.zrok.io"; const ENDPOINT_SCHEME: string = ((import.meta as { env?: Record }).env?.VITE_MINIO_SCHEME || 'https').replace(/:$/,''); export const buildCutoutUrl = (objectKey: string): string => { const cleaned = objectKey.trim().replace(/^\/+/, ''); - // Ensure Cutouts/ prefix once const path = cleaned.startsWith('Cutouts/') ? cleaned : `Cutouts/${cleaned}`; + + // In dev, go through local proxy to inject headers and avoid CORS + interstitial + if (import.meta.env && (import.meta as any).env.DEV) { + // Strip the prefix "Cutouts/" because the proxy rewrite adds it back (see vite proxy config) + const proxied = '/proxy-cutouts/' + path.replace(/^Cutouts\//, ''); + if((import.meta as { env?: Record }).env?.VITE_DEBUG_CUTOUTS) console.debug('[cutout-url-dev-proxy]', { objectKey, path, proxied }); + return proxied; + } + if(!RAW_ENDPOINT) return path; // fallback relative path if not configured let base = RAW_ENDPOINT.trim(); if(!/^https?:\/\//i.test(base)) { @@ -106,54 +114,10 @@ export const buildCutoutUrl = (objectKey: string): string => { // Simple async wrapper (keeps existing calling pattern with react-query) export const getCutoutObject = async (objectKey: string): Promise => { const url = buildCutoutUrl(objectKey); - - // For ngrok URLs, we need to add headers to bypass the browser warning - if (RAW_ENDPOINT && RAW_ENDPOINT.includes('ngrok')) { - try { - console.log('[DEBUG] Fetching image with ngrok headers:', url); - - // Create a new URL object to add ngrok bypass headers - const response = await fetch(url, { - method: 'GET', - headers: { - 'ngrok-skip-browser-warning': 'true', - 'User-Agent': 'LaStBeRu-Explorer/1.0 (Custom)' - }, - mode: 'cors', - credentials: 'omit' - }); - - console.log('[DEBUG] Response status:', response.status, response.statusText); - - if (response.ok) { - // Convert to blob URL for better browser handling - const blob = await response.blob(); - const blobUrl = URL.createObjectURL(blob); - console.log('[DEBUG] Created blob URL:', blobUrl); - return blobUrl; - } else { - console.warn('[DEBUG] Response not ok:', response.status, response.statusText); - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - } catch (error) { - console.warn('Failed to fetch image with headers:', error); - // Try creating an image element that can handle the ngrok redirect - return new Promise((resolve) => { - const img = new Image(); - img.crossOrigin = 'anonymous'; - img.onload = () => { - console.log('[DEBUG] Image loaded successfully via img element'); - resolve(url); - }; - img.onerror = () => { - console.warn('[DEBUG] Image failed to load via img element'); - resolve(url); // Still return URL, let the component handle the error - }; - img.src = url; - }); - } + // In dev with proxy we just return the proxied path; browser will request via Vite server + if ((import.meta as any).env?.DEV) { + return url; } - - console.log('[DEBUG] Using direct URL:', url); + // In production (no proxy) just return direct URL (CORS must be handled server-side) return url; }; diff --git a/dashboard/src/components/CutoutGrid.tsx b/dashboard/src/components/CutoutGrid.tsx index a9cf315..1c2eeb3 100644 --- a/dashboard/src/components/CutoutGrid.tsx +++ b/dashboard/src/components/CutoutGrid.tsx @@ -44,7 +44,7 @@ const CutoutCard: React.FC<{ record: CutoutRecord }> = memo(({ record }) => { src={data} loading="lazy" decoding="async" - crossOrigin="anonymous" + alt={record.band + ' cutout'} style={{ position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'contain', imageRendering:'auto' }} onError={(e)=>{ const img = e.currentTarget; @@ -54,7 +54,7 @@ const CutoutCard: React.FC<{ record: CutoutRecord }> = memo(({ record }) => { // For ngrok URLs, try adding the bypass parameters as query string if (img.src.includes('ngrok')) { const url = new URL(img.src); - url.searchParams.set('ngrok-skip-browser-warning', 'true'); + url.searchParams.set('skip_zrok_interstitial', 'true'); img.src = url.toString(); return; } diff --git a/dashboard/vite.config.ts b/dashboard/vite.config.ts index 840d9b6..c66c023 100644 --- a/dashboard/vite.config.ts +++ b/dashboard/vite.config.ts @@ -46,10 +46,29 @@ export default defineConfig({ server: { port: 5173, headers: { - // Add headers to help with CORS during development + // Basic permissive headers (still need target server cooperation for true CORS) 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization' + }, + // Dev-only proxy to inject the zrok interstitial bypass header and avoid CORS issues. + proxy: { + // Usage in code: fetch('/proxy-cutouts/') + '/proxy-cutouts': { + target: 'https://l5s5a0sibv6w.share.zrok.io', + changeOrigin: true, + // Rewrite /proxy-cutouts/Processed_Cutouts/... -> /slcomp/Cutouts/Processed_Cutouts/... + rewrite: (path) => path + .replace(/^\/proxy-cutouts\/?/, '/slcomp/Cutouts/') + .replace(/\/+/g,'/'), + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + // Header required by zrok to skip warning interstitial + proxyReq.setHeader('skip_zrok_interstitial', 'true'); + proxyReq.setHeader('User-Agent', 'LaStBeRu-Explorer/1.0 (DevProxy)'); + }); + } + } } } }); diff --git a/notebooks/footprints/footprints.ipynb b/notebooks/footprints/footprints.ipynb index d5250e3..5de343e 100644 --- a/notebooks/footprints/footprints.ipynb +++ b/notebooks/footprints/footprints.ipynb @@ -51,7 +51,7 @@ ")\n", "\n", "\n", - "MINIO_ENDPOINT_URL = \"nonarithmetically-undeliberating-janelle.ngrok-free.app\"\n", + "MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\"\n", "ACCESS_KEY = \"slcomp\"\n", "SECRET_KEY = \"slcomp@data\"\n", "client = Minio(\n", diff --git a/notebooks/how_to.ipynb b/notebooks/how_to.ipynb index a224abb..9e7a015 100644 --- a/notebooks/how_to.ipynb +++ b/notebooks/how_to.ipynb @@ -61,7 +61,7 @@ "metadata": {}, "outputs": [], "source": [ - "MINIO_ENDPOINT_URL = \"nonarithmetically-undeliberating-janelle.ngrok-free.app\"\n", + "MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\n", "ACCESS_KEY = \"slcomp\"\n", "SECRET_KEY = \"slcomp@data\"\n", "client = Minio(\n", @@ -74,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "id": "994a76e5-b83e-4e47-94b0-86b8636c5821", "metadata": {}, "outputs": [ @@ -84,7 +84,7 @@ "[Bucket('slcomp')]" ] }, - "execution_count": 3, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -3023,7 +3023,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "base", "language": "python", "name": "python3" }, @@ -3037,7 +3037,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.11" } }, "nbformat": 4, diff --git a/notebooks/modified-gravity-tests/01_LaStBeRu_cosmo_ground.ipynb b/notebooks/modified-gravity-tests/01_LaStBeRu_cosmo_ground.ipynb index fe1a55d..a9b55ce 100644 --- a/notebooks/modified-gravity-tests/01_LaStBeRu_cosmo_ground.ipynb +++ b/notebooks/modified-gravity-tests/01_LaStBeRu_cosmo_ground.ipynb @@ -82,7 +82,7 @@ "source": [ "from minio import Minio\n", "\n", - "MINIO_ENDPOINT_URL = \"nonarithmetically-undeliberating-janelle.ngrok-free.app\"\n", + "MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\"\n", "ACCESS_KEY = \"slcomp\"\n", "SECRET_KEY = \"slcomp@data\"\n", "client = Minio(\n", diff --git a/notebooks/proposals/proposals.ipynb b/notebooks/proposals/proposals.ipynb index 608e1a1..b3f7deb 100644 --- a/notebooks/proposals/proposals.ipynb +++ b/notebooks/proposals/proposals.ipynb @@ -35,7 +35,7 @@ " }\n", ")\n", "\n", - "MINIO_ENDPOINT_URL = \"nonarithmetically-undeliberating-janelle.ngrok-free.app\"\n", + "MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\"\n", "ACCESS_KEY = \"slcomp\"\n", "SECRET_KEY = \"slcomp@data\"\n", "client = Minio(\n",