From 978d447b3da1c876764bd9759a7dc6f301a3c5ba Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Mon, 6 Apr 2026 21:30:17 -0400 Subject: [PATCH] fix: add CORS headers for Chrome extension + fix Save & Connect button - Add CORS middleware to server.js allowing chrome-extension:// origins so the popup can make authenticated API requests without browser blocking - Fix popup.js saveSettings(): require password on save, call login() directly instead of tryConnect() to avoid password-not-found loop - Fix init(): open settings panel automatically if no saved token, so users know they need to enter credentials after first install or session expiry - Don't persist password to chrome.storage (security), use remove('token') instead of set({token:null}) to properly clear the old session Co-Authored-By: Claude Sonnet 4.6 --- chrome-extension/popup.js | 28 +++++++++++++++++++++------- server.js | 13 +++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/chrome-extension/popup.js b/chrome-extension/popup.js index 42d3dab..5fa9996 100644 --- a/chrome-extension/popup.js +++ b/chrome-extension/popup.js @@ -12,7 +12,7 @@ let connected = false; (async function init() { const stored = await chrome.storage.local.get(['config', 'token', 'mode']); if (stored.config) config = { ...config, ...stored.config }; - if (stored.token) authToken = stored.token; + if (stored.token && stored.token !== null) authToken = stored.token; if (stored.mode) uploadMode = stored.mode; document.getElementById('cfg-server').value = config.serverUrl; @@ -20,7 +20,15 @@ let connected = false; document.getElementById('conn-server').textContent = config.serverUrl.replace(/https?:\/\//, ''); setMode(uploadMode, false); - await tryConnect(); + + // If we have a saved token, try using it; otherwise open settings + if (authToken) { + await tryConnect(); + } else { + // No saved session — open settings panel so user can enter password + document.getElementById('settings-panel').classList.add('open'); + setConnStatus('grey', 'Enter credentials in settings below'); + } })(); document.getElementById('settings-toggle').addEventListener('click', () => { @@ -84,16 +92,22 @@ async function saveSettings() { const username = document.getElementById('cfg-user').value.trim(); const password = document.getElementById('cfg-pass').value; if (!serverUrl) { showSettingsStatus('Server URL required', 'error'); return; } + if (!username) { showSettingsStatus('Username required', 'error'); return; } + if (!password) { showSettingsStatus('Password required', 'error'); return; } - config = { serverUrl, username, password: password || config.password }; + config = { serverUrl, username, password }; authToken = null; - await chrome.storage.local.set({ config, token: null }); + connected = false; + // Clear old token and save new config (don't save password to storage for security) + await chrome.storage.local.remove('token'); + await chrome.storage.local.set({ config: { serverUrl, username } }); document.getElementById('conn-server').textContent = serverUrl.replace(/https?:\/\//, ''); showSettingsStatus('Saved — connecting…', 'loading'); - connected = false; - await tryConnect(); - showSettingsStatus('Settings saved', 'success'); + await login(); + if (connected) { + showSettingsStatus('✅ Connected successfully!', 'success'); + } } function showSettingsStatus(msg, type) { diff --git a/server.js b/server.js index c96e8eb..efaa605 100644 --- a/server.js +++ b/server.js @@ -231,6 +231,19 @@ const upload = multer({ limits: { fileSize: 50 * 1024 * 1024 * 1024 }, }); +// CORS — allow Chrome extensions and browser clients +app.use((req, res, next) => { + const origin = req.headers.origin || ""; + // Allow Chrome extensions, localhost, and the configured host + const allowed = origin.startsWith("chrome-extension://") || origin.startsWith("http://localhost") || origin.startsWith("https://localhost"); + res.setHeader("Access-Control-Allow-Origin", allowed ? origin : "*"); + res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type,x-auth-token,Authorization"); + res.setHeader("Access-Control-Allow-Credentials", "true"); + if (req.method === "OPTIONS") return res.sendStatus(204); + next(); +}); + app.use((req, res, next) => { if (req.path === "/" || req.path.endsWith(".html") || req.path.endsWith(".png") || req.path.endsWith(".svg")) { res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");