Add files via upload

This commit is contained in:
公明
2026-05-22 11:28:51 +08:00
committed by GitHub
parent 0358d3a67d
commit c446e22d0c
5 changed files with 358 additions and 193 deletions
+152 -77
View File
@@ -46,6 +46,7 @@ const getVulnerabilityPageSize = () => {
let currentVulnerabilityId = null;
let vulnerabilityFilters = {
q: '',
id: '',
conversation_id: '',
task_id: '',
@@ -65,11 +66,14 @@ const VULN_STAT_SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
let vulnerabilityStatCardsBound = false;
let vulnerabilityFilterPanelBound = false;
let vulnerabilityFilterOptionsCache = null;
const VULNERABILITY_ADVANCED_OPEN_KEY = 'vulnerabilityAdvancedFiltersOpen';
let vulnerabilityMoreFiltersPopoverOpen = false;
let vulnerabilityFilterDebounceTimer = null;
const VULNERABILITY_FILTER_DEBOUNCE_MS = 400;
const VULNERABILITY_DATALIST_MAX = 8;
const VULNERABILITY_DATALIST_MIN_QUERY = 2;
const VULN_FILTER_CHIP_FIELDS = [
{ key: 'q', labelKey: 'vulnerabilityPage.searchKeywordShort' },
{ key: 'id', labelKey: 'vulnerabilityPage.vulnId' },
{ key: 'status', labelKey: null, format: 'status' },
{ key: 'severity', labelKey: null, format: 'severity' },
@@ -94,10 +98,12 @@ function syncVulnerabilityFiltersFromLocationHash() {
const st = (params.get('status') || '').trim();
const convTag = (params.get('conversation_tag') || '').trim();
const taskTag = (params.get('task_tag') || '').trim();
if (!vid && !cid && !tid && !sev && !st && !convTag && !taskTag) {
const q = (params.get('q') || params.get('search') || '').trim();
if (!vid && !cid && !tid && !sev && !st && !convTag && !taskTag && !q) {
return;
}
vulnerabilityFilters.q = '';
vulnerabilityFilters.id = '';
vulnerabilityFilters.conversation_id = '';
vulnerabilityFilters.task_id = '';
@@ -105,14 +111,16 @@ function syncVulnerabilityFiltersFromLocationHash() {
vulnerabilityFilters.task_tag = '';
vulnerabilityFilters.severity = '';
vulnerabilityFilters.status = '';
const idEl = document.getElementById('vulnerability-id-filter');
const searchEl = document.getElementById('vulnerability-search-filter');
const exactIdEl = document.getElementById('vulnerability-exact-id-filter');
const convEl = document.getElementById('vulnerability-conversation-filter');
const taskEl = document.getElementById('vulnerability-task-filter');
const convTagEl = document.getElementById('vulnerability-conversation-tag-filter');
const taskTagEl = document.getElementById('vulnerability-task-tag-filter');
const sevEl = document.getElementById('vulnerability-severity-filter');
const stEl = document.getElementById('vulnerability-status-filter');
if (idEl) idEl.value = '';
if (searchEl) searchEl.value = '';
if (exactIdEl) exactIdEl.value = '';
if (convEl) convEl.value = '';
if (taskEl) taskEl.value = '';
if (convTagEl) convTagEl.value = '';
@@ -120,9 +128,13 @@ function syncVulnerabilityFiltersFromLocationHash() {
if (sevEl) sevEl.value = '';
if (stEl) stEl.value = '';
if (q) {
vulnerabilityFilters.q = q;
if (searchEl) searchEl.value = q;
}
if (vid) {
vulnerabilityFilters.id = vid;
if (idEl) idEl.value = vid;
if (exactIdEl) exactIdEl.value = vid;
}
if (cid) {
vulnerabilityFilters.conversation_id = cid;
@@ -149,9 +161,6 @@ function syncVulnerabilityFiltersFromLocationHash() {
if (stEl) stEl.value = st;
}
vulnerabilityPagination.currentPage = 1;
if (hasVulnerabilityAdvancedFiltersActive()) {
setVulnerabilityAdvancedFiltersOpen(true, false);
}
syncVulnerabilityStatCardActiveState();
updateVulnerabilityFilterPanelState();
renderVulnerabilityFilterChips();
@@ -213,7 +222,8 @@ function applyVulnerabilitySeverityFilter(severity) {
}
function readVulnerabilityFiltersFromForm() {
vulnerabilityFilters.id = (document.getElementById('vulnerability-id-filter')?.value || '').trim();
vulnerabilityFilters.q = (document.getElementById('vulnerability-search-filter')?.value || '').trim();
vulnerabilityFilters.id = (document.getElementById('vulnerability-exact-id-filter')?.value || '').trim();
vulnerabilityFilters.conversation_id = (document.getElementById('vulnerability-conversation-filter')?.value || '').trim();
vulnerabilityFilters.task_id = (document.getElementById('vulnerability-task-filter')?.value || '').trim();
vulnerabilityFilters.conversation_tag = (document.getElementById('vulnerability-conversation-tag-filter')?.value || '').trim();
@@ -225,13 +235,13 @@ function readVulnerabilityFiltersFromForm() {
function hasVulnerabilityAdvancedFiltersActive() {
const f = vulnerabilityFilters;
return Boolean(f.conversation_id || f.task_id || f.conversation_tag || f.task_tag);
return Boolean(f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag);
}
function hasAnyVulnerabilityFilterActive() {
const f = vulnerabilityFilters;
return Boolean(
f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag || f.severity || f.status
f.q || f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag || f.severity || f.status
);
}
@@ -253,6 +263,7 @@ function updateVulnerabilityLocationHashFromFilters() {
const params = new URLSearchParams(hashParts.length >= 2 ? hashParts.slice(1).join('?') : '');
const f = vulnerabilityFilters;
const pairs = [
['q', f.q],
['id', f.id],
['conversation_id', f.conversation_id],
['task_id', f.task_id],
@@ -279,17 +290,82 @@ function updateVulnerabilityLocationHashFromFilters() {
}
}
function toggleVulnerabilityAdvancedFilters(ev) {
function scheduleVulnerabilityFilterApply(immediate) {
if (vulnerabilityFilterDebounceTimer) {
clearTimeout(vulnerabilityFilterDebounceTimer);
vulnerabilityFilterDebounceTimer = null;
}
if (immediate) {
applyVulnerabilityFilters();
return;
}
vulnerabilityFilterDebounceTimer = setTimeout(function () {
vulnerabilityFilterDebounceTimer = null;
applyVulnerabilityFilters();
}, VULNERABILITY_FILTER_DEBOUNCE_MS);
}
function syncVulnerabilityAdvancedFieldsFromFilters() {
const map = {
id: 'vulnerability-exact-id-filter',
conversation_id: 'vulnerability-conversation-filter',
task_id: 'vulnerability-task-filter',
conversation_tag: 'vulnerability-conversation-tag-filter',
task_tag: 'vulnerability-task-tag-filter'
};
Object.keys(map).forEach(function (key) {
const el = document.getElementById(map[key]);
if (el) el.value = vulnerabilityFilters[key] || '';
});
}
function setVulnerabilityMoreFiltersPopoverOpen(open) {
const btn = document.getElementById('vulnerability-more-filters-btn');
const popover = document.getElementById('vulnerability-more-filters-popover');
if (!btn || !popover) return;
vulnerabilityMoreFiltersPopoverOpen = open;
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
btn.classList.toggle('is-active', open);
popover.hidden = !open;
}
function toggleVulnerabilityMoreFiltersPopover(ev) {
if (ev) {
ev.preventDefault();
ev.stopPropagation();
}
const toggleBtn = document.getElementById('vulnerability-advanced-toggle');
if (!toggleBtn) return;
const expanded = toggleBtn.getAttribute('aria-expanded') === 'true';
setVulnerabilityAdvancedFiltersOpen(!expanded, true);
const opening = !vulnerabilityMoreFiltersPopoverOpen;
if (opening) {
readVulnerabilityFiltersFromForm();
syncVulnerabilityAdvancedFieldsFromFilters();
}
setVulnerabilityMoreFiltersPopoverOpen(opening);
}
function closeVulnerabilityMoreFiltersPopover(revertDraft) {
if (revertDraft) syncVulnerabilityAdvancedFieldsFromFilters();
setVulnerabilityMoreFiltersPopoverOpen(false);
}
function clearVulnerabilityAdvancedFilterFields() {
['vulnerability-exact-id-filter', 'vulnerability-conversation-filter', 'vulnerability-task-filter',
'vulnerability-conversation-tag-filter', 'vulnerability-task-tag-filter'].forEach(function (id) {
const el = document.getElementById(id);
if (el) el.value = '';
});
}
function applyVulnerabilityMoreFiltersFromPopover() {
closeVulnerabilityMoreFiltersPopover(false);
scheduleVulnerabilityFilterApply(true);
}
function onVulnerabilityFilterDocumentPointerDown(ev) {
if (!vulnerabilityMoreFiltersPopoverOpen) return;
const anchor = document.querySelector('.vulnerability-more-filters-anchor');
if (anchor && anchor.contains(ev.target)) return;
closeVulnerabilityMoreFiltersPopover(true);
}
window.toggleVulnerabilityAdvancedFilters = toggleVulnerabilityAdvancedFilters;
function initVulnerabilityFilterPanel() {
const panel = document.getElementById('vulnerability-filter-panel');
@@ -301,55 +377,69 @@ function initVulnerabilityFilterPanel() {
}
vulnerabilityFilterPanelBound = true;
let savedOpen = false;
try {
savedOpen = localStorage.getItem(VULNERABILITY_ADVANCED_OPEN_KEY) === 'true';
} catch (e) { /* ignore */ }
setVulnerabilityAdvancedFiltersOpen(savedOpen, false);
const stEl = document.getElementById('vulnerability-status-filter');
if (stEl) stEl.addEventListener('change', applyVulnerabilityFilters);
if (stEl) stEl.addEventListener('change', function () { scheduleVulnerabilityFilterApply(true); });
const textIds = [
'vulnerability-id-filter',
const searchEl = document.getElementById('vulnerability-search-filter');
if (searchEl) {
searchEl.addEventListener('input', function () { scheduleVulnerabilityFilterApply(false); });
searchEl.addEventListener('keydown', function (ev) {
if (ev.key === 'Enter') {
ev.preventDefault();
scheduleVulnerabilityFilterApply(true);
}
});
searchEl.addEventListener('search', function () {
if (searchEl.value === '') scheduleVulnerabilityFilterApply(true);
});
}
const moreBtn = document.getElementById('vulnerability-more-filters-btn');
if (moreBtn) moreBtn.addEventListener('click', toggleVulnerabilityMoreFiltersPopover);
const applyBtn = document.getElementById('vulnerability-more-filters-apply');
if (applyBtn) applyBtn.addEventListener('click', applyVulnerabilityMoreFiltersFromPopover);
const resetBtn = document.getElementById('vulnerability-more-filters-reset');
if (resetBtn) {
resetBtn.addEventListener('click', function () {
clearVulnerabilityAdvancedFilterFields();
applyVulnerabilityMoreFiltersFromPopover();
});
}
const advancedTextIds = [
'vulnerability-exact-id-filter',
'vulnerability-conversation-filter',
'vulnerability-task-filter',
'vulnerability-conversation-tag-filter',
'vulnerability-task-tag-filter'
];
textIds.forEach(function (id) {
advancedTextIds.forEach(function (id) {
const el = document.getElementById(id);
if (!el) return;
el.addEventListener('keydown', function (ev) {
if (ev.key === 'Enter') {
ev.preventDefault();
applyVulnerabilityFilters();
applyVulnerabilityMoreFiltersFromPopover();
}
});
});
document.addEventListener('mousedown', onVulnerabilityFilterDocumentPointerDown);
document.addEventListener('keydown', function (ev) {
if (ev.key === 'Escape' && vulnerabilityMoreFiltersPopoverOpen) {
closeVulnerabilityMoreFiltersPopover(true);
}
});
bindVulnerabilityFilterTypeaheads();
}
function setVulnerabilityAdvancedFiltersOpen(open, persist) {
const toggleBtn = document.getElementById('vulnerability-advanced-toggle');
const advanced = document.getElementById('vulnerability-advanced-filters');
const wrap = document.querySelector('#vulnerability-filter-panel .vulnerability-filter-advanced-wrap');
if (!toggleBtn || !advanced) return;
toggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
advanced.hidden = !open;
advanced.classList.toggle('is-open', open);
if (wrap) wrap.classList.toggle('is-expanded', open);
if (persist) {
try {
localStorage.setItem(VULNERABILITY_ADVANCED_OPEN_KEY, open ? 'true' : 'false');
} catch (e) { /* ignore */ }
}
}
function countVulnerabilityAdvancedFiltersActive() {
const f = vulnerabilityFilters;
let n = 0;
if (f.id) n++;
if (f.conversation_id) n++;
if (f.task_id) n++;
if (f.conversation_tag) n++;
@@ -379,6 +469,8 @@ function updateVulnerabilityFilterPanelState() {
readVulnerabilityFiltersFromForm();
panel.classList.toggle('is-filtered', hasAnyVulnerabilityFilterActive());
updateVulnerabilityAdvancedBadge();
const clearBtn = document.getElementById('vulnerability-filter-clear-btn');
if (clearBtn) clearBtn.hidden = !hasAnyVulnerabilityFilterActive();
}
function formatVulnerabilityFilterChipValue(key, value) {
@@ -431,7 +523,8 @@ function renderVulnerabilityFilterChips() {
function removeVulnerabilityFilterByKey(key) {
const map = {
id: 'vulnerability-id-filter',
q: 'vulnerability-search-filter',
id: 'vulnerability-exact-id-filter',
conversation_id: 'vulnerability-conversation-filter',
task_id: 'vulnerability-task-filter',
conversation_tag: 'vulnerability-conversation-tag-filter',
@@ -609,13 +702,7 @@ async function loadVulnerabilityStats() {
throw new Error('apiFetch未定义');
}
const params = new URLSearchParams();
if (vulnerabilityFilters.conversation_id) {
params.append('conversation_id', vulnerabilityFilters.conversation_id);
}
if (vulnerabilityFilters.task_id) {
params.append('task_id', vulnerabilityFilters.task_id);
}
const params = buildVulnerabilityFilterParams();
const response = await apiFetch(`/api/vulnerabilities/stats?${params.toString()}`);
if (!response.ok) {
@@ -689,31 +776,9 @@ async function loadVulnerabilities(page = null) {
vulnerabilityPagination.currentPage = page;
}
const params = new URLSearchParams();
const params = buildVulnerabilityFilterParams();
params.append('page', vulnerabilityPagination.currentPage.toString());
params.append('limit', vulnerabilityPagination.pageSize.toString());
if (vulnerabilityFilters.id) {
params.append('id', vulnerabilityFilters.id);
}
if (vulnerabilityFilters.conversation_id) {
params.append('conversation_id', vulnerabilityFilters.conversation_id);
}
if (vulnerabilityFilters.task_id) {
params.append('task_id', vulnerabilityFilters.task_id);
}
if (vulnerabilityFilters.conversation_tag) {
params.append('conversation_tag', vulnerabilityFilters.conversation_tag);
}
if (vulnerabilityFilters.task_tag) {
params.append('task_tag', vulnerabilityFilters.task_tag);
}
if (vulnerabilityFilters.severity) {
params.append('severity', vulnerabilityFilters.severity);
}
if (vulnerabilityFilters.status) {
params.append('status', vulnerabilityFilters.status);
}
const response = await apiFetch(`/api/vulnerabilities?${params.toString()}`);
if (!response.ok) {
@@ -1090,8 +1155,14 @@ function filterVulnerabilities() {
// 清除筛选
function clearVulnerabilityFilters() {
closeVulnerabilityMoreFiltersPopover(false);
if (vulnerabilityFilterDebounceTimer) {
clearTimeout(vulnerabilityFilterDebounceTimer);
vulnerabilityFilterDebounceTimer = null;
}
const fields = [
'vulnerability-id-filter',
'vulnerability-search-filter',
'vulnerability-exact-id-filter',
'vulnerability-conversation-filter',
'vulnerability-task-filter',
'vulnerability-conversation-tag-filter',
@@ -1105,6 +1176,7 @@ function clearVulnerabilityFilters() {
});
vulnerabilityFilters = {
q: '',
id: '',
conversation_id: '',
task_id: '',
@@ -1277,8 +1349,11 @@ function formatVulnerabilityAsMarkdown(vuln) {
function buildVulnerabilityFilterParams() {
const params = new URLSearchParams();
if (vulnerabilityFilters.q) {
params.append('q', vulnerabilityFilters.q);
}
const keys = ['id', 'conversation_id', 'task_id', 'conversation_tag', 'task_tag', 'severity', 'status'];
keys.forEach((k) => {
keys.forEach(function (k) {
if (vulnerabilityFilters[k]) {
params.append(k, vulnerabilityFilters[k]);
}