'use strict'; // ── Helpers ──────────────────────────────────────────────────────────────── function formatBytes(n) { if (!n) return 'unknown size'; if (n < 1024) return n + ' B'; if (n < 1024 ** 2) return (n / 1024).toFixed(1) + ' KB'; if (n < 1024 ** 3) return (n / 1024 ** 2).toFixed(1) + ' MB'; return (n / 1024 ** 3).toFixed(2) + ' GB'; } function formatExpiry(isoStr) { return new Date(isoStr).toLocaleString(); } function triggerDownload(blob, filename) { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename || 'download'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 10000); } // ── DOM refs ─────────────────────────────────────────────────────────────── const statusWrap = document.getElementById('statusWrap'); const statusMsg = document.getElementById('statusMsg'); const infoGrid = document.getElementById('infoGrid'); const infoType = document.getElementById('infoType'); const infoSize = document.getElementById('infoSize'); const infoExpiry = document.getElementById('infoExpiry'); const infoBurn = document.getElementById('infoBurn'); const alertBox = document.getElementById('alertBox'); const pwSection = document.getElementById('pwSection'); const pwInput = document.getElementById('pwInput'); const downloadBtn = document.getElementById('downloadBtn'); const textSection = document.getElementById('textSection'); const textOutput = document.getElementById('textOutput'); const copyTextBtn = document.getElementById('copyTextBtn'); // ── Init ─────────────────────────────────────────────────────────────────── const shareId = location.pathname.split('/s/')[1]; (async function init() { if (!shareId) { showError('Invalid share URL.'); return; } let meta; try { const res = await fetch(`/api/share/${shareId}/meta`); if (!res.ok) { showError(res.status === 404 ? 'This share does not exist or has expired.' : 'Failed to load share info.'); return; } meta = await res.json(); } catch { showError('Network error. Please check your connection.'); return; } if (meta.locked) { showError(`Share locked — too many failed attempts. Try again in ${meta.lock_remaining}s.`); return; } // Show info grid statusWrap.classList.add('hidden'); infoGrid.classList.remove('hidden'); infoType.textContent = meta.share_type === 'file' ? (meta.original_filename || 'File') : 'Text snippet'; infoSize.textContent = formatBytes(meta.filesize); infoExpiry.textContent = formatExpiry(meta.expires_at); infoBurn.textContent = meta.burn_after_read ? 'Yes — deleted after this download' : 'No'; if (meta.has_password) pwSection.classList.remove('hidden'); downloadBtn.classList.remove('hidden'); // Attach handlers now that we have meta downloadBtn.addEventListener('click', () => handleDownload(meta)); pwInput.addEventListener('keydown', e => { if (e.key === 'Enter') handleDownload(meta); }); })(); // ── Download ─────────────────────────────────────────────────────────────── async function handleDownload(meta) { clearAlert(); downloadBtn.disabled = true; downloadBtn.textContent = 'Downloading…'; try { const res = await fetch(`/api/share/${shareId}/download`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pwInput.value }), }); if (res.status === 401) { const body = await res.json().catch(() => ({})); showAlert(body.detail || 'Incorrect password.'); pwInput.value = ''; pwInput.focus(); return; } if (res.status === 429) { const body = await res.json().catch(() => ({})); showAlert(body.detail || 'Too many attempts. Please wait.'); return; } if (!res.ok) { showAlert('Download failed. The share may have expired.'); return; } if (meta.share_type === 'text') { const text = await res.text(); textSection.classList.remove('hidden'); textOutput.textContent = text; copyTextBtn.onclick = () => { navigator.clipboard.writeText(text); const orig = copyTextBtn.textContent; copyTextBtn.textContent = 'Copied!'; setTimeout(() => (copyTextBtn.textContent = orig), 1500); }; downloadBtn.textContent = 'Decrypted'; } else { const blob = await res.blob(); triggerDownload(blob, meta.original_filename); downloadBtn.textContent = 'Downloaded ✓'; } if (meta.burn_after_read) { showAlert('This share has been deleted — it can no longer be accessed.', 'info'); } } catch (err) { showAlert(err.message || 'An unexpected error occurred.'); } finally { if (downloadBtn.textContent === 'Downloading…') { downloadBtn.disabled = false; downloadBtn.textContent = 'Decrypt & Download'; } } } // ── UI helpers ───────────────────────────────────────────────────────────── function showError(msg) { statusWrap.classList.remove('hidden'); statusMsg.textContent = msg; statusMsg.className = 'alert alert-error'; downloadBtn.classList.add('hidden'); } function showAlert(msg, type = 'error') { alertBox.className = `alert alert-${type}`; alertBox.textContent = msg; alertBox.classList.remove('hidden'); downloadBtn.disabled = false; downloadBtn.textContent = 'Decrypt & Download'; } function clearAlert() { alertBox.classList.add('hidden'); }