diff --git a/web/static/css/style.css b/web/static/css/style.css index b87d5e1..792a945 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -1047,60 +1047,3 @@ code { color: var(--muted); } -.privacy-sanitize { - padding: 10px 12px; - border-bottom: 1px solid var(--border); -} - -.privacy-sanitize.hidden, -.privacy-sanitize-preview.hidden { - display: none; -} - -#privacy-sanitize-btn, -.privacy-sanitize-dl { - font-size: 0.85em; - padding: 5px 12px; - cursor: pointer; -} - -.privacy-sanitize-preview { - margin-top: 10px; - font-size: 0.88em; -} - -.privacy-sanitize-preview table { - margin: 6px 0; -} - -.privacy-sanitize-dl { - margin-top: 8px; - font-weight: 600; -} - -.privacy-sanitize-warn { - margin-top: 8px; - padding: 6px 8px; - background: #fff8f0; - border: 1px solid #f0e0c0; - border-radius: 4px; -} - -.privacy-sanitize-warn ul { - margin: 4px 0 0; - padding-left: 18px; - font-family: monospace; - font-size: 0.82em; -} - -.privacy-sanitize-err { - color: var(--crit-fg); -} - -.privacy-sanitized-note { - padding: 6px 8px; - background: #eef7ee; - border: 1px solid #cfe6cf; - border-radius: 4px; - font-size: 0.88em; -} diff --git a/web/static/js/app.js b/web/static/js/app.js index 854928e..7204411 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -1407,7 +1407,6 @@ let auditViewerNonce = 0; async function loadData(vendor, filename) { document.getElementById('upload-section').classList.add('hidden'); document.getElementById('data-section').classList.remove('hidden'); - document.getElementById('clear-btn').classList.remove('hidden'); document.getElementById('header-raw-btn').classList.remove('hidden'); document.getElementById('header-reanimator-btn').classList.remove('hidden'); document.getElementById('header-logs-csv-btn').classList.remove('hidden'); @@ -1425,6 +1424,8 @@ async function loadPrivacyScan() { const customer = document.getElementById('privacy-customer'); if (!section || !rows) return; + const clearBtn = document.getElementById('clear-btn'); + let data; try { const resp = await fetch('/api/privacy-scan'); @@ -1436,6 +1437,7 @@ async function loadPrivacyScan() { if (!data || data.loaded === false) { section.classList.add('hidden'); + if (clearBtn) clearBtn.classList.add('hidden'); return; } @@ -1443,6 +1445,13 @@ async function loadPrivacyScan() { const customers = Array.isArray(data.customers) ? data.customers : []; const san = data.sanitized || null; const alreadySanitized = !!(san && san.detected); + + if (clearBtn) { + clearBtn.classList.toggle('hidden', !data.sanitizable); + clearBtn.disabled = alreadySanitized; + clearBtn.textContent = alreadySanitized ? 'Personal data removed' : 'Remove personal data'; + } + if (findings.length === 0 && customers.length === 0 && !alreadySanitized) { section.classList.add('hidden'); return; @@ -1500,67 +1509,30 @@ async function loadPrivacyScan() { if (findingsBox) findingsBox.style.display = privacyCollapsed ? 'none' : ''; } - const sanBox = document.getElementById('privacy-sanitize'); - if (sanBox) { - sanBox.classList.toggle('hidden', !data.sanitizable && !alreadySanitized); - const prev = document.getElementById('privacy-sanitize-preview'); - const btn = document.getElementById('privacy-sanitize-btn'); - if (alreadySanitized) { - if (btn) btn.classList.add('hidden'); - if (prev) { - prev.innerHTML = `
Файл выглядит уже обезличенным: ` + - `${san.markers} маркер(ов) в ${san.files} файл(ах).` + - ((san.evidence && san.evidence.length) - ? `` - : '') + `
`; - prev.classList.remove('hidden'); - } - } else { - if (btn) { btn.classList.remove('hidden'); btn.disabled = false; btn.textContent = 'Обезличить и скачать копию'; } - if (prev) { prev.classList.add('hidden'); prev.innerHTML = ''; } - } - } - section.classList.remove('hidden'); } -async function sanitizePreview() { - const btn = document.getElementById('privacy-sanitize-btn'); - const prev = document.getElementById('privacy-sanitize-preview'); - if (!btn || !prev) return; +// Header "Remove personal data": sanitize the uploaded file and download the +// clean copy in one click - no preview, no confirmation. +async function removePersonalData() { + const btn = document.getElementById('clear-btn'); + if (!btn || btn.disabled) return; + const label = btn.textContent; btn.disabled = true; - btn.textContent = 'Обработка…'; - let data; + btn.textContent = 'Working…'; try { const resp = await fetch('/api/sanitize', { method: 'POST' }); - data = await resp.json(); - if (!resp.ok) throw new Error(data && data.error ? data.error : 'sanitize failed'); + if (!resp.ok) { + const d = await resp.json().catch(() => ({})); + throw new Error(d.error || ('HTTP ' + resp.status)); + } + window.location = '/api/sanitize/download'; + setTimeout(() => { btn.disabled = false; btn.textContent = label; }, 2000); } catch (e) { - prev.innerHTML = `
${escapeHtml(String(e.message || e))}
`; - prev.classList.remove('hidden'); - btn.disabled = false; - btn.textContent = 'Обезличить и скачать копию'; - return; + console.error('remove personal data failed:', e); + btn.textContent = 'Failed'; + setTimeout(() => { btn.disabled = false; btn.textContent = label; }, 2500); } - - const changes = Array.isArray(data.changes) ? data.changes : []; - const skipped = Array.isArray(data.skipped_binary) ? data.skipped_binary : []; - let html = `

${data.total_replaced} замен в ${changes.length} файл(ах). ` + - `Формат, имена и даты записей сохранены; правки той же длины.

`; - if (changes.length) { - html += '' + - changes.map(c => ``).join('') + - '
КатегорияКол-воФайл
${escapeHtml(c.category)}${c.count}${escapeHtml(c.path)}
'; - } - if (skipped.length) { - html += `
Не удалось отредактировать (обезличьте вручную):
'; - } - html += ``; - prev.innerHTML = html; - prev.classList.remove('hidden'); - btn.disabled = false; - btn.textContent = 'Пересобрать'; } // The header + customer summary stay visible; only the findings table collapses. @@ -1667,50 +1639,6 @@ function exportData(format) { window.location.href = `/api/export/${format}`; } -// Clear data -async function clearData() { - try { - await fetch('/api/clear', { method: 'DELETE' }); - document.getElementById('upload-section').classList.remove('hidden'); - document.getElementById('data-section').classList.add('hidden'); - document.getElementById('clear-btn').classList.add('hidden'); - document.getElementById('header-raw-btn').classList.add('hidden'); - document.getElementById('header-reanimator-btn').classList.add('hidden'); - document.getElementById('header-logs-csv-btn').classList.add('hidden'); - document.getElementById('header-log-meta').classList.add('hidden'); - document.getElementById('upload-status').textContent = ''; - const frame = document.getElementById('audit-viewer-frame'); - if (frame) { - frame.src = 'about:blank'; - } - const parseErrSection = document.getElementById('parse-errors-section'); - if (parseErrSection) parseErrSection.classList.add('hidden'); - const parseErrRows = document.getElementById('parse-errors-rows'); - if (parseErrRows) parseErrRows.innerHTML = ''; - parseErrorsCollapsed = false; - const parseErrBody = document.getElementById('parse-errors-body'); - if (parseErrBody) parseErrBody.style.display = ''; - const parseErrToggle = document.getElementById('parse-errors-toggle'); - if (parseErrToggle) parseErrToggle.textContent = '▲'; - const privacySection = document.getElementById('privacy-section'); - if (privacySection) privacySection.classList.add('hidden'); - const privacyRows = document.getElementById('privacy-rows'); - if (privacyRows) privacyRows.innerHTML = ''; - const privacyCustomer = document.getElementById('privacy-customer'); - if (privacyCustomer) privacyCustomer.innerHTML = ''; - privacyCollapsed = true; - const privacyFindings = document.getElementById('privacy-findings'); - if (privacyFindings) privacyFindings.style.display = 'none'; - const privacyToggle = document.getElementById('privacy-toggle'); - if (privacyToggle) { privacyToggle.textContent = '▼'; privacyToggle.style.visibility = ''; } - const privacySan = document.getElementById('privacy-sanitize'); - if (privacySan) privacySan.classList.add('hidden'); - const privacySanPrev = document.getElementById('privacy-sanitize-preview'); - if (privacySanPrev) { privacySanPrev.innerHTML = ''; privacySanPrev.classList.add('hidden'); } - } catch (err) { - console.error('Failed to clear data:', err); - } -} // Restart app (reload page) function restartApp() { diff --git a/web/templates/index.html b/web/templates/index.html index a193ea2..8d1fe1a 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -15,7 +15,7 @@