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)
- ? `
${san.evidence.map(e => `- ${escapeHtml(e)}
`).join('')}
`
- : '') + `
`;
- 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 => `| ${escapeHtml(c.category)} | ${c.count} | ${escapeHtml(c.path)} |
`).join('') +
- '
';
- }
- if (skipped.length) {
- html += `Не удалось отредактировать (обезличьте вручную):` +
- skipped.map(s => `- ${escapeHtml(s)}
`).join('') + '
';
- }
- 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 @@