Add files via upload

This commit is contained in:
公明
2026-04-30 15:01:35 +08:00
committed by GitHub
parent 531b05299a
commit 44e7d3b340
6 changed files with 58 additions and 23 deletions
+4
View File
@@ -9289,6 +9289,7 @@ header {
margin-bottom: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
@@ -13231,6 +13232,9 @@ header {
font-variant-numeric: tabular-nums;
white-space: nowrap;
flex-shrink: 0;
justify-self: end;
text-align: right;
min-width: 3.5rem;
}
/* External MCP 健康度:能力总览中专门一行的 N/N + 状态徽章 */
+3 -3
View File
@@ -125,7 +125,7 @@
"lastUpdated": "Last updated",
"viewAll": "View all →",
"recentVulns": "Recent vulnerabilities",
"noVulnYet": "No vulnerabilities yet — start your first scan",
"noVulnYet": "No recent vulnerabilities",
"capabilities": "Capabilities",
"mcpTools": "MCP tools",
"rolesLabel": "Roles",
@@ -184,7 +184,7 @@
"recoCheckMonitorDesc": "View failed request details in MCP monitor",
"recoSetupMcp": "Configure your first MCP tool",
"recoSetupMcpDesc": "Install MCP server before Agent can invoke specific capabilities",
"recoStartScan": "Start your first scan",
"recoStartScan": "Start a scan from chat",
"recoStartScanDesc": "Describe your target in chat, AI will help execute",
"recentEvents": "Recent Events",
"eventUntitled": "Event",
@@ -193,7 +193,7 @@
"mcpPartialDown_one": "{{count}} stopped",
"mcpPartialDown_other": "{{count}} stopped",
"mcpAllDown": "All stopped",
"noVulnDesc": "System looks safe — start a scan to discover potential issues",
"noVulnDesc": "This list shows recent records; new results appear here when detection completes in chat",
"startScanBtn": "Go to chat to scan"
},
"chat": {
+3 -3
View File
@@ -125,7 +125,7 @@
"lastUpdated": "上次更新",
"viewAll": "查看全部 →",
"recentVulns": "最近漏洞",
"noVulnYet": "暂无漏洞,开始你的第一次扫描吧",
"noVulnYet": "暂无最近漏洞",
"capabilities": "能力总览",
"mcpTools": "MCP 工具",
"rolesLabel": "角色",
@@ -174,7 +174,7 @@
"recoCheckMonitorDesc": "在 MCP 监控中查看失败的请求详情",
"recoSetupMcp": "配置首个 MCP 工具",
"recoSetupMcpDesc": "安装 MCP 服务后 Agent 才能调用具体能力",
"recoStartScan": "开始第一次扫描",
"recoStartScan": "在对话中发起扫描",
"recoStartScanDesc": "在对话中描述目标,让 AI 协助执行",
"recentEvents": "最近事件",
"eventUntitled": "事件",
@@ -182,7 +182,7 @@
"mcpAllRunning": "全部运行",
"mcpPartialDown": "{{count}} 个未运行",
"mcpAllDown": "全部未运行",
"noVulnDesc": "系统目前安全,开始一次扫描可以发现潜在问题",
"noVulnDesc": "此处展示近期漏洞记录;在对话中完成检测后,新结果会出现在这里",
"startScanBtn": "前往对话发起扫描"
},
"chat": {
+12 -11
View File
@@ -662,8 +662,8 @@ function getHitlPendingCount(res) {
// 「最近事件」内联展示:取通知摘要里最重要的前 N 条
// 设计原则:
// - 不重复 alert banner / KPI 已表达过的信息(漏洞、HITL 等会被过滤掉避免冗余
// - 只显示 p0/p1 优先级,p2 作为兜底(当 p0/p1 不够时)
// - 不重复 alert banner / KPI 已表达的「新漏洞」通知(vulnerability_created 仍过滤
// - HITL 待审批在推荐操作等处也会提示,但仍在此展示时间线,便于与任务完成等并列查看
// - 整个 section 在没有可显示内容时整个隐藏,避免空模块占地方
function renderRecentEvents(notifRes) {
var section = document.getElementById('dashboard-section-events');
@@ -671,8 +671,8 @@ function renderRecentEvents(notifRes) {
if (!section || !listEl) return;
var items = (notifRes && Array.isArray(notifRes.items)) ? notifRes.items : [];
// 过滤:只看有意义的事件,去掉 actionable 已处理的、以及类型已经在仪表盘其他位置覆盖的
var coveredTypes = { 'vulnerability_created': true, 'hitl_pending': true };
// 过滤:去掉新漏洞类型(与「最近漏洞」等板块避免重复);HITL 不再过滤
var coveredTypes = { 'vulnerability_created': true };
var filtered = items.filter(function (it) {
if (!it || !it.type) return false;
if (coveredTypes[it.type]) return false;
@@ -685,8 +685,8 @@ function renderRecentEvents(notifRes) {
var la = levelOrder[a.level] != null ? levelOrder[a.level] : 9;
var lb = levelOrder[b.level] != null ? levelOrder[b.level] : 9;
if (la !== lb) return la - lb;
var ta = a.createdAt || a.created_at || 0;
var tb = b.createdAt || b.created_at || 0;
var ta = a.ts || a.createdAt || a.created_at || 0;
var tb = b.ts || b.createdAt || b.created_at || 0;
return new Date(tb).getTime() - new Date(ta).getTime();
});
@@ -701,8 +701,9 @@ function renderRecentEvents(notifRes) {
listEl.innerHTML = top.map(function (it) {
var level = it.level || 'p2';
var title = esc(it.title || it.message || dt('dashboard.eventUntitled', null, '事件'));
var msg = esc(it.message || it.summary || '');
var when = esc(timeAgoStr(it.createdAt || it.created_at));
var msg = esc(it.message || it.summary || it.desc || '');
var whenRaw = timeAgoStr(it.ts || it.createdAt || it.created_at);
var when = esc(whenRaw || '—');
return (
'<div class="dashboard-event-item lvl-' + esc(level) + '">' +
'<span class="dashboard-event-dot" aria-hidden="true"></span>' +
@@ -784,7 +785,7 @@ function renderRecommendedActions(state) {
actions.push({
level: 'setup',
icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',
title: dt('dashboard.recoStartScan', null, '开始第一次扫描'),
title: dt('dashboard.recoStartScan', null, '在对话中发起扫描'),
desc: dt('dashboard.recoStartScanDesc', null, '在对话中描述目标,让 AI 协助执行'),
page: 'chat'
});
@@ -972,8 +973,8 @@ function renderRecentVulns(res) {
'<span class="dashboard-empty-icon" aria-hidden="true">' +
'<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg>' +
'</span>' +
'<div class="dashboard-empty-title">' + esc(dt('dashboard.noVulnYet', null, '暂无漏洞')) + '</div>' +
'<div class="dashboard-empty-desc">' + esc(dt('dashboard.noVulnDesc', null, '系统目前安全,开始一次扫描可以发现潜在问题')) + '</div>' +
'<div class="dashboard-empty-title">' + esc(dt('dashboard.noVulnYet', null, '暂无最近漏洞')) + '</div>' +
'<div class="dashboard-empty-desc">' + esc(dt('dashboard.noVulnDesc', null, '此处展示近期漏洞记录;在对话中完成检测后,新结果会出现在这里')) + '</div>' +
'<button type="button" class="dashboard-empty-action" data-action="scan">' +
esc(dt('dashboard.startScanBtn', null, '前往对话发起扫描')) + ' →</button>'
);
+35 -5
View File
@@ -1,6 +1,28 @@
// 角色管理相关功能
function _t(key, opts) {
return typeof window.t === 'function' ? window.t(key, opts) : key;
if (typeof window.t === 'function') {
try {
var translated = window.t(key, opts);
if (typeof translated === 'string' && translated && translated !== key) {
return translated;
}
} catch (e) { /* ignore */ }
}
// i18n 未就绪或词条缺失时避免把 key 暴露给用户(与 zh-CN 默认一致)
if (key === 'roles.noDescription') return '暂无描述';
if (key === 'roles.noDescriptionShort') return '无描述';
if (key === 'roles.defaultRoleDescription') {
return '默认角色,不额外携带用户提示词,使用默认MCP';
}
return key;
}
/** 角色配置中的描述:trim,并把误存为 i18n key 的字面量视为空 */
function rolePlainDescription(role) {
const raw = typeof role.description === 'string' ? role.description.trim() : '';
if (!raw) return '';
if (raw === 'roles.noDescription' || raw === 'roles.noDescriptionShort') return '';
return raw;
}
let currentRole = localStorage.getItem('currentRole') || '';
let roles = [];
@@ -56,6 +78,11 @@ function sortRoles(rolesArray) {
// 加载所有角色
async function loadRoles() {
if (window.i18nReady && typeof window.i18nReady.then === 'function') {
try {
await window.i18nReady;
} catch (e) { /* ignore */ }
}
try {
const response = await apiFetch('/api/roles');
if (!response.ok) {
@@ -189,8 +216,9 @@ function renderRoleSelectionSidebar() {
const icon = getRoleIcon(role);
// 处理默认角色的描述
let description = role.description || _t('roles.noDescription');
if (isDefaultRole && !role.description) {
const plainDesc = rolePlainDescription(role);
let description = plainDesc || _t('roles.noDescription');
if (isDefaultRole && !plainDesc) {
description = _t('roles.defaultRoleDescription');
}
@@ -316,6 +344,7 @@ function renderRolesList() {
const sortedRoles = sortRoles(filteredRoles);
rolesList.innerHTML = sortedRoles.map(role => {
const plainDesc = rolePlainDescription(role);
// 获取角色图标,如果是Unicode转义格式则转换为emoji
let roleIcon = role.icon || '👤';
if (roleIcon && typeof roleIcon === 'string') {
@@ -369,7 +398,7 @@ function renderRolesList() {
${role.enabled !== false ? _t('roles.enabled') : _t('roles.disabled')}
</span>
</div>
<div class="role-card-description">${escapeHtml(role.description || _t('roles.noDescriptionShort'))}</div>
<div class="role-card-description">${escapeHtml(plainDesc || _t('roles.noDescriptionShort'))}</div>
<div class="role-card-tools">
<span class="role-card-tools-label">${_t('roleModal.toolsLabel')}</span>
<span class="role-card-tools-value">${toolsDisplay}</span>
@@ -1575,9 +1604,10 @@ document.addEventListener('DOMContentLoaded', () => {
updateRoleSelectorDisplay();
});
// 语言切换后刷新角色选择器显示(默认/自定义角色名)
// 语言切换后刷新角色选择器与「选择角色」列表文案
document.addEventListener('languagechange', () => {
updateRoleSelectorDisplay();
renderRoleSelectionSidebar();
});
// 获取当前选中的角色(供chat.js使用)
+1 -1
View File
@@ -498,7 +498,7 @@
<a class="dashboard-section-link" onclick="switchPage('vulnerabilities')" data-i18n="dashboard.viewAll">查看全部 →</a>
</div>
<div class="dashboard-recent-vulns" id="dashboard-recent-vulns">
<div class="dashboard-recent-vulns-empty" id="dashboard-recent-vulns-empty" data-i18n="dashboard.noVulnYet">暂无漏洞,开始你的第一次扫描吧</div>
<div class="dashboard-recent-vulns-empty" id="dashboard-recent-vulns-empty" data-i18n="dashboard.noVulnYet">暂无最近漏洞</div>
</div>
</section>
<section class="dashboard-section dashboard-section-overview">