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.
-➡️ **[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",