v1.0.176: XSS ×5 fix + syncDB await + escHtml(null)
This commit is contained in:
+23
-21
@@ -21,13 +21,15 @@ var batchId = crypto.randomUUID(); // классификация: привяз
|
||||
})();
|
||||
|
||||
// Синхронизация БД с таблицей — удалить всё, чего нет в fileQueue
|
||||
function syncDB() {
|
||||
async function syncDB() {
|
||||
var keepIds = fileQueue.map(function(f) { return f.doc_id; }).filter(Boolean);
|
||||
fetch(VM_API + '/api/sync', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({keep_ids: keepIds})
|
||||
}).catch(function(){});
|
||||
try {
|
||||
await fetch(VM_API + '/api/sync', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({keep_ids: keepIds})
|
||||
});
|
||||
} catch(e) { /* сервер недоступен — не критично */ }
|
||||
}
|
||||
// ── Pipeline stepper ─────────────────────────────────────────
|
||||
function stepDone(id) {
|
||||
@@ -58,7 +60,7 @@ function renderTable() {
|
||||
} else {
|
||||
fileTable.innerHTML = fileQueue.map(function(f, i) {
|
||||
return '<tr id="row_' + i + '">' +
|
||||
'<td class="name-cell">' + f.name + '</td>' +
|
||||
'<td class="name-cell">' + escHtml(f.name) + '</td>' +
|
||||
'<td style="font-size:12px;color:var(--muted);">' + formatDate(f.lastModified) + '</td>' +
|
||||
'<td class="num-cell" style="font-size:12px;color:var(--muted);">' + formatSize(f.size) + '</td>' +
|
||||
'<td class="status-cell">' + (f.status || '') + '</td>' +
|
||||
@@ -318,18 +320,18 @@ async function loadGroups() {
|
||||
var docsHtml = g.documents.map(function(d) {
|
||||
var reason = '';
|
||||
if (d.parent_number) {
|
||||
reason = ' — нет базового договора №' + d.parent_number;
|
||||
reason = ' — нет базового договора №' + escHtml(d.parent_number);
|
||||
} else if (d.classify_status === 'failed') {
|
||||
reason = ' — ошибка классификации: ' + (d.error_message || '?');
|
||||
reason = ' — ошибка классификации: ' + escHtml(d.error_message || '?');
|
||||
} else if (!d.doc_type || d.doc_type === 'other') {
|
||||
reason = ' — тип не определён';
|
||||
} else {
|
||||
reason = ' — нет matching-договора';
|
||||
}
|
||||
return '<div style="padding:2px 0;">' +
|
||||
'<span style="color:var(--muted);">[' + (d.doc_type || '?') + ']</span> ' +
|
||||
d.filename +
|
||||
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
||||
'<span style="color:var(--muted);">[' + escHtml(d.doc_type || '?') + ']</span> ' +
|
||||
escHtml(d.filename) +
|
||||
(d.doc_date ? ' <span style="color:var(--muted);">' + escHtml(d.doc_date) + '</span>' : '') +
|
||||
'<span style="color:var(--destructive);font-size:11px;">' + reason + '</span>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
@@ -337,14 +339,14 @@ async function loadGroups() {
|
||||
'<div style="font-size:12px;">' + docsHtml + '</div>' +
|
||||
'<div style="font-size:11px;color:var(--muted);margin-top:4px;">Загрузите недостающие базовые договоры для этих номеров.</div>';
|
||||
} else {
|
||||
card.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">📄 Договор №' + (g.contract_number || '?') + ' — ' + (g.counterparty || 'контрагент не определён') + '</div>' +
|
||||
card.innerHTML = '<div style="font-weight:600;margin-bottom:6px;">📄 Договор №' + escHtml(g.contract_number || '?') + ' — ' + escHtml(g.counterparty || 'контрагент не определён') + '</div>' +
|
||||
'<div style="font-size:12px;">' +
|
||||
g.documents.map(function(d) {
|
||||
var typeLabel = {contract: 'договор', supplement: 'допсоглашение', specification: 'спецификация'}[d.doc_type] || d.doc_type;
|
||||
return '<div style="padding:2px 0;">' +
|
||||
'<span style="color:var(--muted);">' + (typeLabel || '?') + '</span> ' +
|
||||
d.filename +
|
||||
(d.doc_date ? ' <span style="color:var(--muted);">' + d.doc_date + '</span>' : '') +
|
||||
'<span style="color:var(--muted);">' + escHtml(typeLabel || '?') + '</span> ' +
|
||||
escHtml(d.filename) +
|
||||
(d.doc_date ? ' <span style="color:var(--muted);">' + escHtml(d.doc_date) + '</span>' : '') +
|
||||
'</div>';
|
||||
}).join('') + '</div>';
|
||||
card.innerHTML += '<button class="btn btn-primary" style="margin-top:6px;font-size:12px;height:28px;" onclick="runCompareForGroup(' + gi + ')">▶ Сравнить эту группу</button>';
|
||||
@@ -723,11 +725,11 @@ async function showText(i) {
|
||||
// ── Результаты классификации ──────────────────────────
|
||||
if (qData.classify_status === 'classified' || qData.classify_raw) {
|
||||
rightHtml += '<div style="margin-top:12px;font-weight:600;font-size:12px;border-top:1px solid var(--brand-gray);padding-top:8px;">🏷️ Классификация LLM</div>';
|
||||
if (qData.doc_type) rightHtml += '<div class="kv"><span class="k">Тип</span><span class="v">' + qData.doc_type + '</span></div>';
|
||||
if (qData.own_number) rightHtml += '<div class="kv"><span class="k">Номер</span><span class="v">' + qData.own_number + '</span></div>';
|
||||
if (qData.parent_number) rightHtml += '<div class="kv"><span class="k">Родительский</span><span class="v">' + qData.parent_number + '</span></div>';
|
||||
if (qData.doc_date) rightHtml += '<div class="kv"><span class="k">Дата</span><span class="v">' + qData.doc_date + '</span></div>';
|
||||
if (qData.counterparty) rightHtml += '<div class="kv"><span class="k">Контрагент</span><span class="v">' + qData.counterparty + '</span></div>';
|
||||
if (qData.doc_type) rightHtml += '<div class="kv"><span class="k">Тип</span><span class="v">' + escHtml(qData.doc_type) + '</span></div>';
|
||||
if (qData.own_number) rightHtml += '<div class="kv"><span class="k">Номер</span><span class="v">' + escHtml(qData.own_number) + '</span></div>';
|
||||
if (qData.parent_number) rightHtml += '<div class="kv"><span class="k">Родительский</span><span class="v">' + escHtml(qData.parent_number) + '</span></div>';
|
||||
if (qData.doc_date) rightHtml += '<div class="kv"><span class="k">Дата</span><span class="v">' + escHtml(qData.doc_date) + '</span></div>';
|
||||
if (qData.counterparty) rightHtml += '<div class="kv"><span class="k">Контрагент</span><span class="v">' + escHtml(qData.counterparty) + '</span></div>';
|
||||
if (qData.classify_raw) {
|
||||
var rawId = 'raw_' + docId.replace(/-/g,'');
|
||||
rightHtml += '<details style="margin-top:8px;"><summary style="cursor:pointer;font-size:11px;color:var(--brand-primary);">Сырой ответ LLM</summary>';
|
||||
|
||||
Reference in New Issue
Block a user