Add files via upload

This commit is contained in:
公明
2026-07-08 15:17:06 +08:00
committed by GitHub
parent c2a1d6c632
commit 784be8a162
5 changed files with 116 additions and 66 deletions
+9 -5
View File
@@ -1405,8 +1405,10 @@
"totalItems": "Total items", "totalItems": "Total items",
"categories": "Categories", "categories": "Categories",
"addKnowledge": "Add knowledge", "addKnowledge": "Add knowledge",
"rebuildIndex": "Rebuild index", "buildIndex": "Build index",
"rebuildIndexConfirm": "Rebuild index?", "buildIndexConfirm": "Build vectors for knowledge items that are not indexed yet?",
"rebuildIndexFull": "Full rebuild",
"rebuildIndexFullConfirm": "Re-index all knowledge items? This may take a while.",
"deleteItemConfirm": "Delete this knowledge item?", "deleteItemConfirm": "Delete this knowledge item?",
"notEnabledTitle": "Knowledge base function not enabled", "notEnabledTitle": "Knowledge base function not enabled",
"notEnabledHint": "Please go to system settings to enable knowledge retrieval.", "notEnabledHint": "Please go to system settings to enable knowledge retrieval.",
@@ -1653,7 +1655,7 @@
"updateKnowledgeItem": "Update knowledge item", "updateKnowledgeItem": "Update knowledge item",
"deleteKnowledgeItem": "Delete knowledge item", "deleteKnowledgeItem": "Delete knowledge item",
"getIndexStatus": "Get index status", "getIndexStatus": "Get index status",
"rebuildIndex": "Rebuild index", "startKnowledgeIndex": "Build index",
"scanKnowledgeBase": "Scan knowledge base", "scanKnowledgeBase": "Scan knowledge base",
"searchKnowledgeBase": "Search knowledge base", "searchKnowledgeBase": "Search knowledge base",
"basicSearch": "Basic search", "basicSearch": "Basic search",
@@ -2469,7 +2471,8 @@
"connection_create": "Create connection", "connection_create": "Create connection",
"connection_delete": "Delete connection", "connection_delete": "Delete connection",
"item_delete": "Delete knowledge item", "item_delete": "Delete knowledge item",
"index_rebuild": "Rebuild index", "index_build": "Build knowledge index",
"index_rebuild_full": "Full knowledge index rebuild",
"delete": "Delete", "delete": "Delete",
"delete_turn": "Delete turn", "delete_turn": "Delete turn",
"create": "Create", "create": "Create",
@@ -2542,7 +2545,8 @@
}, },
"knowledge": { "knowledge": {
"item_delete": "Knowledge item deleted", "item_delete": "Knowledge item deleted",
"index_rebuild": "Knowledge index rebuilt" "index_build": "Knowledge index built",
"index_rebuild_full": "Knowledge index fully rebuilt"
}, },
"vulnerability": { "vulnerability": {
"create": "Vulnerability record created", "create": "Vulnerability record created",
+9 -5
View File
@@ -1393,8 +1393,10 @@
"totalItems": "总知识项", "totalItems": "总知识项",
"categories": "分类数", "categories": "分类数",
"addKnowledge": "添加知识", "addKnowledge": "添加知识",
"rebuildIndex": "建索引", "buildIndex": "建索引",
"rebuildIndexConfirm": "确定要重建索引吗", "buildIndexConfirm": "将为尚未生成向量的知识项构建索引,是否继续",
"rebuildIndexFull": "全量重建",
"rebuildIndexFullConfirm": "将重新索引全部知识项,这可能需要较长时间,是否继续?",
"deleteItemConfirm": "确定要删除这个知识项吗?", "deleteItemConfirm": "确定要删除这个知识项吗?",
"notEnabledTitle": "知识库功能未启用", "notEnabledTitle": "知识库功能未启用",
"notEnabledHint": "请前往系统设置启用知识检索功能", "notEnabledHint": "请前往系统设置启用知识检索功能",
@@ -1641,7 +1643,7 @@
"updateKnowledgeItem": "更新知识项", "updateKnowledgeItem": "更新知识项",
"deleteKnowledgeItem": "删除知识项", "deleteKnowledgeItem": "删除知识项",
"getIndexStatus": "获取索引状态", "getIndexStatus": "获取索引状态",
"rebuildIndex": "建索引", "startKnowledgeIndex": "建索引",
"scanKnowledgeBase": "扫描知识库", "scanKnowledgeBase": "扫描知识库",
"searchKnowledgeBase": "搜索知识库", "searchKnowledgeBase": "搜索知识库",
"basicSearch": "基础搜索", "basicSearch": "基础搜索",
@@ -2457,7 +2459,8 @@
"connection_create": "创建连接", "connection_create": "创建连接",
"connection_delete": "删除连接", "connection_delete": "删除连接",
"item_delete": "删除知识项", "item_delete": "删除知识项",
"index_rebuild": "重建索引", "index_build": "构建知识库索引",
"index_rebuild_full": "全量重建知识库索引",
"delete": "删除", "delete": "删除",
"delete_turn": "删除轮次", "delete_turn": "删除轮次",
"create": "创建", "create": "创建",
@@ -2530,7 +2533,8 @@
}, },
"knowledge": { "knowledge": {
"item_delete": "删除知识项", "item_delete": "删除知识项",
"index_rebuild": "建知识库索引" "index_build": "建知识库索引",
"index_rebuild_full": "全量重建知识库索引"
}, },
"vulnerability": { "vulnerability": {
"create": "创建漏洞记录", "create": "创建漏洞记录",
+1 -1
View File
@@ -15,7 +15,7 @@ const AUDIT_ACTIONS_BY_CATEGORY = {
c2: ['listener_create', 'listener_delete', 'listener_start', 'listener_stop', c2: ['listener_create', 'listener_delete', 'listener_start', 'listener_stop',
'session_delete', 'task_create', 'task_cancel', 'task_delete'], 'session_delete', 'task_create', 'task_cancel', 'task_delete'],
webshell: ['connection_create', 'connection_delete'], webshell: ['connection_create', 'connection_delete'],
knowledge: ['item_delete', 'index_rebuild'], knowledge: ['item_delete', 'index_build', 'index_rebuild_full'],
conversation: ['create', 'delete', 'delete_turn'], conversation: ['create', 'delete', 'delete_turn'],
vulnerability: ['create', 'update', 'delete'], vulnerability: ['create', 'update', 'delete'],
external_mcp: ['upsert', 'delete'], external_mcp: ['upsert', 'delete'],
+89 -48
View File
@@ -495,8 +495,8 @@ async function updateIndexProgress() {
<div style="color: #999; font-size: 12px; margin-bottom: 12px;"> <div style="color: #999; font-size: 12px; margin-bottom: 12px;">
可能的原因:嵌入模型配置错误、API密钥无效、余额不足等。请检查配置后重试。 可能的原因:嵌入模型配置错误、API密钥无效、余额不足等。请检查配置后重试。
</div> </div>
<div style="display: flex; gap: 8px;"> <div style="display: flex; gap: 8px; flex-wrap: wrap;">
<button onclick="rebuildKnowledgeIndex()" style=" <button onclick="buildKnowledgeIndex()" style="
background: #007bff; background: #007bff;
color: white; color: white;
border: none; border: none;
@@ -505,6 +505,15 @@ async function updateIndexProgress() {
cursor: pointer; cursor: pointer;
font-size: 13px; font-size: 13px;
">重试</button> ">重试</button>
<button onclick="rebuildKnowledgeIndexFull()" style="
background: #6c757d;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
">全量重建</button>
<button onclick="stopIndexProgressPolling()" style=" <button onclick="stopIndexProgressPolling()" style="
background: #6c757d; background: #6c757d;
color: white; color: white;
@@ -547,7 +556,7 @@ async function updateIndexProgress() {
<div class="knowledge-index-progress"> <div class="knowledge-index-progress">
<div class="progress-header"> <div class="progress-header">
<span class="progress-icon">🔨</span> <span class="progress-icon">🔨</span>
<span class="progress-text">正在建索引:${rebuildCurrent}/${rebuildTotal} (${rebuildProgress.toFixed(1)}%) - 失败:${rebuildFailed}</span> <span class="progress-text">正在建索引:${rebuildCurrent}/${rebuildTotal} (${rebuildProgress.toFixed(1)}%) - 失败:${rebuildFailed}</span>
</div> </div>
<div class="progress-bar-container"> <div class="progress-bar-container">
<div class="progress-bar" style="width: ${rebuildProgress}%"></div> <div class="progress-bar" style="width: ${rebuildProgress}%"></div>
@@ -579,6 +588,7 @@ async function updateIndexProgress() {
indexProgressInterval = null; indexProgressInterval = null;
} }
} else { } else {
const showResume = indexedItems < totalItems;
progressContainer.innerHTML = ` progressContainer.innerHTML = `
<div class="knowledge-index-progress"> <div class="knowledge-index-progress">
<div class="progress-header"> <div class="progress-header">
@@ -588,7 +598,10 @@ async function updateIndexProgress() {
<div class="progress-bar-container"> <div class="progress-bar-container">
<div class="progress-bar" style="width: ${progressPercent}%"></div> <div class="progress-bar" style="width: ${progressPercent}%"></div>
</div> </div>
<div class="progress-hint">索引构建完成后,语义搜索功能将可用</div> <div class="progress-hint">
索引构建完成后,语义搜索功能将可用
${showResume ? '<br><button type="button" onclick="buildKnowledgeIndex()" style="margin-top:8px;background:#007bff;color:#fff;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px;">构建索引</button>' : ''}
</div>
</div> </div>
`; `;
@@ -842,59 +855,87 @@ async function refreshKnowledgeBase() {
} }
} }
// 重建索引 // 启动知识库索引任务(mode: missing | full
async function rebuildKnowledgeIndex() { async function startKnowledgeIndexJob(mode = 'missing') {
try { const fullRebuild = mode === 'full';
if (!confirm('确定要重建索引吗?这可能需要一些时间。')) { const confirmMessage = fullRebuild
return; ? '将重新索引全部知识项,这可能需要较长时间,是否继续?'
} : '将为尚未生成向量的知识项构建索引,是否继续?';
showNotification('正在重建索引...', 'info'); if (!confirm(confirmMessage)) {
return;
}
// 先停止现有的轮询 const actionLabel = fullRebuild ? '全量重建索引' : '构建索引';
if (indexProgressInterval) { showNotification(`正在启动${actionLabel}...`, 'info');
clearInterval(indexProgressInterval);
indexProgressInterval = null;
}
// 立即显示"正在重建"状态,因为重建开始时会清空旧索引 if (indexProgressInterval) {
const progressContainer = document.getElementById('knowledge-index-progress'); clearInterval(indexProgressInterval);
if (progressContainer) { indexProgressInterval = null;
progressContainer.style.display = 'block'; }
progressContainer.innerHTML = `
<div class="knowledge-index-progress"> const progressContainer = document.getElementById('knowledge-index-progress');
<div class="progress-header"> if (progressContainer) {
<span class="progress-icon">🔨</span> progressContainer.style.display = 'block';
<span class="progress-text">正在重建索引: 准备中...</span> progressContainer.innerHTML = `
</div> <div class="knowledge-index-progress">
<div class="progress-bar-container"> <div class="progress-header">
<div class="progress-bar" style="width: 0%"></div> <span class="progress-icon">🔨</span>
</div> <span class="progress-text">正在${actionLabel}: 准备中...</span>
<div class="progress-hint">索引构建完成后,语义搜索功能将可用</div>
</div> </div>
`; <div class="progress-bar-container">
} <div class="progress-bar" style="width: 0%"></div>
</div>
<div class="progress-hint">索引构建完成后,语义搜索功能将可用</div>
</div>
`;
}
const response = await apiFetch('/api/knowledge/index', { const query = fullRebuild ? '?mode=full' : '';
method: 'POST' const response = await apiFetch(`/api/knowledge/index${query}`, {
}); method: 'POST'
if (!response.ok) { });
throw new Error('重建索引失败');
}
showNotification('索引重建已开始,将在后台进行', 'success');
// 等待一小段时间,确保后端已经开始处理并清空了旧索引 let payload = {};
await new Promise(resolve => setTimeout(resolve, 500)); try {
payload = await response.json();
} catch (_) {
payload = {};
}
// 立即更新一次进度 if (response.status === 409) {
showNotification(payload.error || '已有索引任务正在进行,请等待完成', 'warning');
updateIndexProgress(); updateIndexProgress();
return;
}
if (!response.ok) {
throw new Error(payload.error || `${actionLabel}失败`);
}
// 开始轮询进度(每2秒刷新一次,比默认的3秒更频繁) showNotification(payload.message || `${actionLabel}已开始,将在后台进行`, 'success');
if (!indexProgressInterval) { await new Promise(resolve => setTimeout(resolve, 500));
indexProgressInterval = setInterval(updateIndexProgress, 2000); updateIndexProgress();
} if (!indexProgressInterval) {
indexProgressInterval = setInterval(updateIndexProgress, 2000);
}
}
// 构建索引(默认:仅处理尚无向量的知识项)
async function buildKnowledgeIndex() {
try {
await startKnowledgeIndexJob('missing');
} catch (error) { } catch (error) {
console.error('建索引失败:', error); console.error('建索引失败:', error);
showNotification('建索引失败: ' + error.message, 'error'); showNotification('建索引失败: ' + error.message, 'error');
}
}
// 全量重建索引(显式 opt-in)
async function rebuildKnowledgeIndexFull() {
try {
await startKnowledgeIndexJob('full');
} catch (error) {
console.error('全量重建索引失败:', error);
showNotification('全量重建索引失败: ' + error.message, 'error');
} }
} }
+2 -1
View File
@@ -1509,7 +1509,8 @@
<h2 data-i18n="knowledge.title">知识管理</h2> <h2 data-i18n="knowledge.title">知识管理</h2>
<div class="page-header-actions"> <div class="page-header-actions">
<button class="btn-secondary" onclick="refreshKnowledgeBase()" data-i18n="common.refresh">刷新</button> <button class="btn-secondary" onclick="refreshKnowledgeBase()" data-i18n="common.refresh">刷新</button>
<button class="btn-secondary" onclick="rebuildKnowledgeIndex()" data-i18n="knowledge.rebuildIndex">建索引</button> <button class="btn-secondary" onclick="buildKnowledgeIndex()" data-i18n="knowledge.buildIndex">建索引</button>
<button class="btn-secondary" onclick="rebuildKnowledgeIndexFull()" data-i18n="knowledge.rebuildIndexFull">全量重建</button>
<button class="btn-primary" onclick="showAddKnowledgeItemModal()" data-i18n="knowledge.addKnowledge">添加知识</button> <button class="btn-primary" onclick="showAddKnowledgeItemModal()" data-i18n="knowledge.addKnowledge">添加知识</button>
</div> </div>
</div> </div>