"use strict"; // Dragon Wind Chrome Extension — Popup Script let config = { serverUrl: 'http://localhost:3000', username: 'Admin', password: '' }; let authToken = null; let uploadMode = 'http'; let selectedFiles = []; let folderList = []; let connected = false; // ==================== INIT ==================== (async function init() { const stored = await chrome.storage.local.get(['config', 'token', 'mode']); if (stored.config) config = { ...config, ...stored.config }; if (stored.token && stored.token !== null) authToken = stored.token; if (stored.mode) uploadMode = stored.mode; // Default to HTTP if no mode saved if (!stored.mode) uploadMode = 'http'; document.getElementById('cfg-server').value = config.serverUrl; document.getElementById('cfg-user').value = config.username; document.getElementById('conn-server').textContent = config.serverUrl.replace(/https?:\/\//, ''); setMode(uploadMode, false); // 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'); } })(); // ==================== EVENT LISTENERS ==================== document.getElementById('settings-toggle').addEventListener('click', () => { document.getElementById('settings-panel').classList.toggle('open'); }); document.getElementById('save-btn').addEventListener('click', saveSettings); document.getElementById('upload-btn').addEventListener('click', startUpload); document.getElementById('btn-http').addEventListener('click', () => setMode('http')); document.getElementById('btn-udp').addEventListener('click', () => setMode('udp')); const dropZone = document.getElementById('drop-zone'); dropZone.addEventListener('click', () => document.getElementById('file-input').click()); dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('over'); }); dropZone.addEventListener('dragleave', () => dropZone.classList.remove('over')); dropZone.addEventListener('drop', onDrop); document.getElementById('file-input').addEventListener('change', onFileInputChange); // ==================== CONNECTION ==================== async function tryConnect() { setConnStatus('grey', 'Connecting…'); if (!authToken) { if (!config.username || !config.password) { setConnStatus('red', 'Set credentials in settings'); return; } await login(); return; } try { const r = await apiFetch('GET', '/api/health'); if (r.status === 'ok') { setConnStatus('green', `Connected — ${r.s3Configured ? 'S3 ✓' : 'S3 not configured'}`); connected = true; await loadFolders(); } else { authToken = null; await chrome.storage.local.remove('token'); await login(); } } catch (e) { setConnStatus('red', `Cannot reach server`); } } async function login() { try { const r = await apiFetch('POST', '/api/login', { username: config.username, password: config.password }); if (r.success) { authToken = r.token; await chrome.storage.local.set({ token: authToken }); setConnStatus('green', `Connected as ${r.user}`); connected = true; await loadFolders(); } else { setConnStatus('red', r.error || 'Auth failed'); showSettingsStatus(`❌ ${r.error || 'Auth failed'}`, 'error'); } } catch (e) { setConnStatus('red', 'Connection failed'); showSettingsStatus(`❌ ${e.message || 'Connection failed'}`, 'error'); } } function setConnStatus(color, text) { const dot = document.getElementById('conn-dot'); dot.className = `conn-dot ${color}`; document.getElementById('conn-label').textContent = text; } // ==================== SETTINGS ==================== async function saveSettings() { const serverUrl = document.getElementById('cfg-server').value.trim().replace(/\/$/, ''); 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 }; authToken = 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'); try { await login(); if (connected) { showSettingsStatus('✅ Connected successfully!', 'success'); } } catch (e) { showSettingsStatus(`❌ ${e.message || 'Unknown error'}`, 'error'); setConnStatus('red', e.message || 'Connection failed'); } } function showSettingsStatus(msg, type) { const el = document.getElementById('settings-status'); el.className = `status-bar ${type}`; el.textContent = msg; } // ==================== FOLDERS ==================== async function loadFolders() { try { const d = await apiFetch('GET', '/api/folders'); folderList = []; flattenTree(d.tree || [], '', folderList); const sel = document.getElementById('folder-select'); const current = sel.value; sel.innerHTML = ''; folderList.forEach(f => { const opt = document.createElement('option'); opt.value = f.path; opt.textContent = f.display; sel.appendChild(opt); }); if (current) sel.value = current; } catch (_) {} } function flattenTree(nodes, prefix, out) { nodes.forEach(n => { const path = prefix ? `${prefix}/${n.name}` : n.name; const display = '\u00a0'.repeat(prefix.split('/').filter(Boolean).length * 2) + (prefix ? '↳ ' : '') + n.name; out.push({ path, display }); if (n.children.length) flattenTree(n.children, path, out); }); } // ==================== MODE ==================== function setMode(mode, save = true) { uploadMode = mode; document.getElementById('btn-http').className = `mode-btn${mode === 'http' ? ' active-http' : ''}`; document.getElementById('btn-udp').className = `mode-btn${mode === 'udp' ? ' active-udp' : ''}`; const btn = document.getElementById('upload-btn'); if (mode === 'http') { btn.className = 'upload-btn http'; btn.textContent = 'Upload Files'; } else { btn.className = 'upload-btn'; btn.textContent = '⚡ UDP Upload'; } if (save) chrome.storage.local.set({ mode }); } // ==================== FILES ==================== function onDrop(e) { e.preventDefault(); document.getElementById('drop-zone').classList.remove('over'); addFiles(Array.from(e.dataTransfer.files)); } function onFileInputChange(e) { addFiles(Array.from(e.target.files)); e.target.value = ''; } function addFiles(files) { files.forEach(f => { if (!selectedFiles.find(x => x.name === f.name && x.size === f.size)) { selectedFiles.push({ file: f, name: f.name, size: f.size, status: 'pending' }); } }); renderFileList(); updateBtn(); } function renderFileList() { const list = document.getElementById('file-list'); list.innerHTML = ''; selectedFiles.forEach((item, i) => { const el = document.createElement('div'); el.className = 'file-item'; el.innerHTML = `