// ================================================ // SGE — B2 Client // Proxy: /b2proxy (Flask pe HuggingFace) // ================================================ const PROXY = '/b2proxy'; async function proxyCall(action, path, extraHeaders = {}, body = null) { const opts = { method: 'POST', headers: { 'X-Action': action, 'X-Path': path, ...extraHeaders } }; if (body) opts.body = body; const res = await fetch(PROXY, opts); if (!res.ok) { const txt = await res.text(); throw new Error(txt || `err-027 — HTTP ${res.status}`); } return res.json(); } // ── UPLOAD (prin proxy Flask — fara CORS issues) ── async function b2Upload(file, path, onProgress) { // Citeste fisierul const buf = await file.arrayBuffer(); // SHA1 pentru integritate const hash = await crypto.subtle.digest('SHA-1', buf); const sha1 = Array.from(new Uint8Array(hash)) .map(b => b.toString(16).padStart(2, '0')).join(''); // Upload prin proxy (Flask il trimite mai departe la B2) // Folosim XMLHttpRequest pentru progress return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', PROXY); xhr.setRequestHeader('X-Action', 'upload'); xhr.setRequestHeader('X-Path', path); xhr.setRequestHeader('X-Content-Type', file.type || 'application/octet-stream'); xhr.setRequestHeader('X-Sha1', sha1); xhr.setRequestHeader('Content-Type', 'application/octet-stream'); xhr.upload.onprogress = e => { if (e.lengthComputable && onProgress) onProgress(Math.round(e.loaded / e.total * 100)); }; xhr.onload = () => { if (xhr.status === 200) { resolve(path); } else { reject(new Error(`err-009 — Upload esuat: ${xhr.status}`)); } }; xhr.onerror = () => reject(new Error('err-027 — Eroare retea la upload')); xhr.send(buf); }); } // ── LIST ── async function b2List(prefix) { try { const data = await proxyCall('list', prefix); return data.files || []; } catch(e) { console.error('b2List:', e.message); return []; } } // ── DELETE ── async function b2Delete(key) { try { await proxyCall('delete', key); } catch(e) { console.error('b2Delete:', e.message); } }