From 290b29854409d00a082241d5568a273c62c98e76 Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Thu, 21 May 2026 13:58:52 +0200 Subject: [PATCH] fix(sync): harden Dropbox loading --- src/App.tsx | 99 ++++++++++++++++++++++---------------------------- vite.config.ts | 3 +- 2 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8104e86..9a988f9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -192,68 +192,55 @@ export default function App() { setDefaultLoadError(null); try { - const isDev = import.meta.env.DEV; - let rows: any[][]; let allData: any[][]; let fileMeta = { rev: '', size: 0 }; - - if (isDev) { - const cacheBuster = `t_${Date.now()}`; - const fileUrl = `/dropbox-file/scl/fi/usa8me7ywgylrij2bt6hj/Data-Matrix.xlsx?rlkey=tsec8csrhye54u1fdvk15ped1&st=qbxxs4cn&dl=0&${cacheBuster}=${Date.now()}`; - console.log('Fetching Data-Matrix.xlsx from Dropbox (hard refresh)...'); + + const loadWorkbookFromUrl = async (url: string) => { + const response = await fetch(url, { cache: 'no-store' }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const contentType = response.headers.get('content-type'); + if (contentType && contentType.includes('text/html')) { + throw new Error('Dropbox returned an HTML page instead of the Excel file.'); + } + + const arrayBuffer = await response.arrayBuffer(); + if (arrayBuffer.byteLength < 100) throw new Error('File too small'); + + const wb = XLSX.read(arrayBuffer, { type: 'array' }); + const wsname = wb.SheetNames[0]; + const ws = wb.Sheets[wsname]; + allData = XLSX.utils.sheet_to_json(ws, { header: 1 }); + rows = allData.slice(1); + return arrayBuffer.byteLength; + }; + + const dropboxSources = import.meta.env.DEV + ? [ + '/api/dropbox-proxy', + `/dropbox-file/scl/fi/usa8me7ywgylrij2bt6hj/Data-Matrix.xlsx?rlkey=tsec8csrhye54u1fdvk15ped1&st=qbxxs4cn&dl=0&t=${Date.now()}` + ] + : [ + '/api/dropbox-proxy' + ]; + + let lastError: unknown = null; + for (const source of dropboxSources) { try { - const response = await fetch(fileUrl, { cache: 'no-store' }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - - const contentType = response.headers.get('content-type'); - if (contentType && contentType.includes('text/html')) { - throw new Error('Dropbox returned an HTML page instead of the Excel file.'); - } - - const arrayBuffer = await response.arrayBuffer(); - if (arrayBuffer.byteLength < 100) throw new Error('File too small'); - - const wb = XLSX.read(arrayBuffer, { type: 'array' }); - const wsname = wb.SheetNames[0]; - const ws = wb.Sheets[wsname]; - allData = XLSX.utils.sheet_to_json(ws, { header: 1 }); - rows = allData.slice(1); - fileMeta = { rev: 'dev', size: arrayBuffer.byteLength }; - } catch (devErr) { - console.error('Dropbox dev load failed:', devErr); - throw devErr; + console.log(`Fetching Data-Matrix.xlsx from ${source.includes('/api/') ? 'proxy' : 'Dropbox'}...`); + const size = await loadWorkbookFromUrl(source); + fileMeta = { rev: source, size }; + lastError = null; + break; + } catch (err) { + lastError = err; + console.warn(`Dropbox source failed for ${source}:`, err); } - } else { - console.log('Fetching file info from Dropbox...'); - const infoRes = await fetch('/api/dropbox-proxy?info=1'); - if (infoRes.ok) { - fileMeta = await infoRes.json(); - console.log('File meta:', fileMeta); - } - - console.log('Fetching Data-Matrix.xlsx from proxy (hard refresh)...'); - try { - const response = await fetch('/api/dropbox-proxy', { cache: 'no-store' }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); + } - const contentType = response.headers.get('content-type'); - if (contentType && contentType.includes('text/html')) { - throw new Error('Dropbox returned an HTML page instead of the Excel file.'); - } - - const arrayBuffer = await response.arrayBuffer(); - if (arrayBuffer.byteLength < 100) throw new Error('File too small'); - - const wb = XLSX.read(arrayBuffer, { type: 'array' }); - const wsname = wb.SheetNames[0]; - const ws = wb.Sheets[wsname]; - allData = XLSX.utils.sheet_to_json(ws, { header: 1 }); - rows = allData.slice(1); - } catch (prodErr) { - console.error('Dropbox prod load failed:', prodErr); - throw prodErr; - } + if (lastError) { + throw lastError instanceof Error ? lastError : new Error(String(lastError)); } if (rows.length > 0) { diff --git a/vite.config.ts b/vite.config.ts index 40d2a47..c5fbff4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -31,6 +31,7 @@ async function readJsonBody(req: any): Promise { export default defineConfig(({mode}) => { const env = loadEnv(mode, '.', ''); + const prodEnv = loadEnv('production', '.', ''); return { plugins: [ react(), @@ -44,7 +45,7 @@ export default defineConfig(({mode}) => { const config = getBcConfig(env); // Populate process.env with loaded env variables for serverless handlers - Object.assign(process.env, env); + Object.assign(process.env, prodEnv, env); // Helper to adapt Node.js req/res to Vercel signature const adaptVercelHandler = async (handler: any) => {