Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Tabular data and processed image cutouts are available for download.
<img src=".figures/minio_service.gif" width="60%" alt="Animated GIF of data download from the MinIO service" />
</p>

➡️ **[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)**.
Expand Down
64 changes: 14 additions & 50 deletions dashboard/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,20 @@ export const loadCutouts = (): Promise<CutoutRecord[]> => fetchJson<CutoutRecord
// - Else we prepend scheme from VITE_MINIO_SCHEME (default 'https').
// - This allows using plain HTTP during local dev / tunnels without mixed content surprises.
// const RAW_ENDPOINT: string | undefined = (import.meta as { env?: Record<string, string> }).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<string, string> }).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<string, string> }).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)) {
Expand All @@ -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<string | null> => {
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;
};
4 changes: 2 additions & 2 deletions dashboard/src/components/CutoutGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const CutoutCard: React.FC<{ record: CutoutRecord }> = memo(({ record }) => {
src={data}
loading="lazy"
decoding="async"
crossOrigin="anonymous"
alt={record.band + ' cutout'}
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crossOrigin attribute was removed but this change appears unrelated to the zrok migration. The alt attribute addition is good for accessibility, but removing crossOrigin might cause CORS issues for direct image loading in production.

Suggested change
alt={record.band + ' cutout'}
alt={record.band + ' cutout'}
crossOrigin="anonymous"

Copilot uses AI. Check for mistakes.
style={{ position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'contain', imageRendering:'auto' }}
onError={(e)=>{
const img = e.currentTarget;
Expand All @@ -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;
}
Expand Down
21 changes: 20 additions & 1 deletion dashboard/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/<rest-of-cutouts-path>')
'/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)');
});
}
}
}
}
});
2 changes: 1 addition & 1 deletion notebooks/footprints/footprints.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 5 additions & 5 deletions notebooks/how_to.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing closing quote on the MINIO_ENDPOINT_URL string. The line should end with a closing double quote before the newline.

Suggested change
"MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\n",
"MINIO_ENDPOINT_URL = \"l5s5a0sibv6w.share.zrok.io\"\n",

Copilot uses AI. Check for mistakes.
"ACCESS_KEY = \"slcomp\"\n",
"SECRET_KEY = \"slcomp@data\"\n",
"client = Minio(\n",
Expand All @@ -74,7 +74,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 9,
"id": "994a76e5-b83e-4e47-94b0-86b8636c5821",
"metadata": {},
"outputs": [
Expand All @@ -84,7 +84,7 @@
"[Bucket('slcomp')]"
]
},
"execution_count": 3,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
Expand Down Expand Up @@ -3023,7 +3023,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "base",
"language": "python",
"name": "python3"
},
Expand All @@ -3037,7 +3037,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
"version": "3.12.11"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion notebooks/proposals/proposals.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down