mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-20 09:57:19 +02:00
Add files via upload
This commit is contained in:
@@ -70,10 +70,15 @@ func (db *DB) SaveToolExecution(exec *mcp.ToolExecution) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CountToolExecutions 统计工具执行记录总数
|
// CountToolExecutions 统计工具执行记录总数
|
||||||
func (db *DB) CountToolExecutions() (int, error) {
|
func (db *DB) CountToolExecutions(status string) (int, error) {
|
||||||
query := `SELECT COUNT(*) FROM tool_executions`
|
query := `SELECT COUNT(*) FROM tool_executions`
|
||||||
|
args := []interface{}{}
|
||||||
|
if status != "" {
|
||||||
|
query += ` WHERE status = ?`
|
||||||
|
args = append(args, status)
|
||||||
|
}
|
||||||
var count int
|
var count int
|
||||||
err := db.QueryRow(query).Scan(&count)
|
err := db.QueryRow(query, args...).Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -82,13 +87,14 @@ func (db *DB) CountToolExecutions() (int, error) {
|
|||||||
|
|
||||||
// LoadToolExecutions 加载所有工具执行记录(支持分页)
|
// LoadToolExecutions 加载所有工具执行记录(支持分页)
|
||||||
func (db *DB) LoadToolExecutions() ([]*mcp.ToolExecution, error) {
|
func (db *DB) LoadToolExecutions() ([]*mcp.ToolExecution, error) {
|
||||||
return db.LoadToolExecutionsWithPagination(0, 1000)
|
return db.LoadToolExecutionsWithPagination(0, 1000, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadToolExecutionsWithPagination 分页加载工具执行记录
|
// LoadToolExecutionsWithPagination 分页加载工具执行记录
|
||||||
// limit: 最大返回记录数,0 表示使用默认值 1000
|
// limit: 最大返回记录数,0 表示使用默认值 1000
|
||||||
// offset: 跳过的记录数,用于分页
|
// offset: 跳过的记录数,用于分页
|
||||||
func (db *DB) LoadToolExecutionsWithPagination(offset, limit int) ([]*mcp.ToolExecution, error) {
|
// status: 状态筛选,空字符串表示不过滤
|
||||||
|
func (db *DB) LoadToolExecutionsWithPagination(offset, limit int, status string) ([]*mcp.ToolExecution, error) {
|
||||||
if limit <= 0 {
|
if limit <= 0 {
|
||||||
limit = 1000 // 默认限制
|
limit = 1000 // 默认限制
|
||||||
}
|
}
|
||||||
@@ -99,11 +105,16 @@ func (db *DB) LoadToolExecutionsWithPagination(offset, limit int) ([]*mcp.ToolEx
|
|||||||
query := `
|
query := `
|
||||||
SELECT id, tool_name, arguments, status, result, error, start_time, end_time, duration_ms
|
SELECT id, tool_name, arguments, status, result, error, start_time, end_time, duration_ms
|
||||||
FROM tool_executions
|
FROM tool_executions
|
||||||
ORDER BY start_time DESC
|
|
||||||
LIMIT ? OFFSET ?
|
|
||||||
`
|
`
|
||||||
|
args := []interface{}{}
|
||||||
|
if status != "" {
|
||||||
|
query += ` WHERE status = ?`
|
||||||
|
args = append(args, status)
|
||||||
|
}
|
||||||
|
query += ` ORDER BY start_time DESC LIMIT ? OFFSET ?`
|
||||||
|
args = append(args, limit, offset)
|
||||||
|
|
||||||
rows, err := db.Query(query, limit, offset)
|
rows, err := db.Query(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ func (h *MonitorHandler) Monitor(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executions, total := h.loadExecutionsWithPagination(page, pageSize)
|
// 解析状态筛选参数
|
||||||
|
status := c.Query("status")
|
||||||
|
|
||||||
|
executions, total := h.loadExecutionsWithPagination(page, pageSize, status)
|
||||||
stats := h.loadStats()
|
stats := h.loadStats()
|
||||||
|
|
||||||
totalPages := (total + pageSize - 1) / pageSize
|
totalPages := (total + pageSize - 1) / pageSize
|
||||||
@@ -84,13 +87,23 @@ func (h *MonitorHandler) Monitor(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *MonitorHandler) loadExecutions() []*mcp.ToolExecution {
|
func (h *MonitorHandler) loadExecutions() []*mcp.ToolExecution {
|
||||||
executions, _ := h.loadExecutionsWithPagination(1, 1000)
|
executions, _ := h.loadExecutionsWithPagination(1, 1000, "")
|
||||||
return executions
|
return executions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MonitorHandler) loadExecutionsWithPagination(page, pageSize int) ([]*mcp.ToolExecution, int) {
|
func (h *MonitorHandler) loadExecutionsWithPagination(page, pageSize int, status string) ([]*mcp.ToolExecution, int) {
|
||||||
if h.db == nil {
|
if h.db == nil {
|
||||||
allExecutions := h.mcpServer.GetAllExecutions()
|
allExecutions := h.mcpServer.GetAllExecutions()
|
||||||
|
// 如果指定了状态筛选,先进行筛选
|
||||||
|
if status != "" {
|
||||||
|
filtered := make([]*mcp.ToolExecution, 0)
|
||||||
|
for _, exec := range allExecutions {
|
||||||
|
if exec.Status == status {
|
||||||
|
filtered = append(filtered, exec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allExecutions = filtered
|
||||||
|
}
|
||||||
total := len(allExecutions)
|
total := len(allExecutions)
|
||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
end := offset + pageSize
|
end := offset + pageSize
|
||||||
@@ -104,10 +117,20 @@ func (h *MonitorHandler) loadExecutionsWithPagination(page, pageSize int) ([]*mc
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
executions, err := h.db.LoadToolExecutionsWithPagination(offset, pageSize)
|
executions, err := h.db.LoadToolExecutionsWithPagination(offset, pageSize, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Warn("从数据库加载执行记录失败,回退到内存数据", zap.Error(err))
|
h.logger.Warn("从数据库加载执行记录失败,回退到内存数据", zap.Error(err))
|
||||||
allExecutions := h.mcpServer.GetAllExecutions()
|
allExecutions := h.mcpServer.GetAllExecutions()
|
||||||
|
// 如果指定了状态筛选,先进行筛选
|
||||||
|
if status != "" {
|
||||||
|
filtered := make([]*mcp.ToolExecution, 0)
|
||||||
|
for _, exec := range allExecutions {
|
||||||
|
if exec.Status == status {
|
||||||
|
filtered = append(filtered, exec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allExecutions = filtered
|
||||||
|
}
|
||||||
total := len(allExecutions)
|
total := len(allExecutions)
|
||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
end := offset + pageSize
|
end := offset + pageSize
|
||||||
@@ -120,8 +143,8 @@ func (h *MonitorHandler) loadExecutionsWithPagination(page, pageSize int) ([]*mc
|
|||||||
return allExecutions[offset:end], total
|
return allExecutions[offset:end], total
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取总数
|
// 获取总数(考虑状态筛选)
|
||||||
total, err := h.db.CountToolExecutions()
|
total, err := h.db.CountToolExecutions(status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Warn("获取执行记录总数失败", zap.Error(err))
|
h.logger.Warn("获取执行记录总数失败", zap.Error(err))
|
||||||
// 回退:使用已加载的记录数估算
|
// 回退:使用已加载的记录数估算
|
||||||
|
|||||||
+72
-16
@@ -972,7 +972,17 @@ async function refreshMonitorPanel(page = null) {
|
|||||||
const currentPage = page !== null ? page : monitorState.pagination.page;
|
const currentPage = page !== null ? page : monitorState.pagination.page;
|
||||||
const pageSize = monitorState.pagination.pageSize;
|
const pageSize = monitorState.pagination.pageSize;
|
||||||
|
|
||||||
const response = await apiFetch(`/api/monitor?page=${currentPage}&page_size=${pageSize}`, { method: 'GET' });
|
// 获取当前的筛选条件
|
||||||
|
const statusFilter = document.getElementById('monitor-status-filter');
|
||||||
|
const currentFilter = statusFilter ? statusFilter.value : 'all';
|
||||||
|
|
||||||
|
// 构建请求 URL
|
||||||
|
let url = `/api/monitor?page=${currentPage}&page_size=${pageSize}`;
|
||||||
|
if (currentFilter && currentFilter !== 'all') {
|
||||||
|
url += `&status=${encodeURIComponent(currentFilter)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiFetch(url, { method: 'GET' });
|
||||||
const result = await response.json().catch(() => ({}));
|
const result = await response.json().catch(() => ({}));
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(result.error || '获取监控数据失败');
|
throw new Error(result.error || '获取监控数据失败');
|
||||||
@@ -993,7 +1003,7 @@ async function refreshMonitorPanel(page = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
|
renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
|
||||||
renderMonitorExecutions(monitorState.executions);
|
renderMonitorExecutions(monitorState.executions, currentFilter);
|
||||||
renderMonitorPagination();
|
renderMonitorPagination();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('刷新监控面板失败:', error);
|
console.error('刷新监控面板失败:', error);
|
||||||
@@ -1006,10 +1016,59 @@ async function refreshMonitorPanel(page = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMonitorFilters() {
|
async function applyMonitorFilters() {
|
||||||
const statusFilter = document.getElementById('monitor-status-filter');
|
const statusFilter = document.getElementById('monitor-status-filter');
|
||||||
const status = statusFilter ? statusFilter.value : 'all';
|
const status = statusFilter ? statusFilter.value : 'all';
|
||||||
renderMonitorExecutions(monitorState.executions, status);
|
// 当筛选条件改变时,从后端重新获取数据
|
||||||
|
await refreshMonitorPanelWithFilter(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshMonitorPanelWithFilter(statusFilter = 'all') {
|
||||||
|
const statsContainer = document.getElementById('monitor-stats');
|
||||||
|
const execContainer = document.getElementById('monitor-executions');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentPage = 1; // 筛选时重置到第一页
|
||||||
|
const pageSize = monitorState.pagination.pageSize;
|
||||||
|
|
||||||
|
// 构建请求 URL
|
||||||
|
let url = `/api/monitor?page=${currentPage}&page_size=${pageSize}`;
|
||||||
|
if (statusFilter && statusFilter !== 'all') {
|
||||||
|
url += `&status=${encodeURIComponent(statusFilter)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiFetch(url, { method: 'GET' });
|
||||||
|
const result = await response.json().catch(() => ({}));
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(result.error || '获取监控数据失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorState.executions = Array.isArray(result.executions) ? result.executions : [];
|
||||||
|
monitorState.stats = result.stats || {};
|
||||||
|
monitorState.lastFetchedAt = new Date();
|
||||||
|
|
||||||
|
// 更新分页信息
|
||||||
|
if (result.total !== undefined) {
|
||||||
|
monitorState.pagination = {
|
||||||
|
page: result.page || currentPage,
|
||||||
|
pageSize: result.page_size || pageSize,
|
||||||
|
total: result.total || 0,
|
||||||
|
totalPages: result.total_pages || 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMonitorStats(monitorState.stats, monitorState.lastFetchedAt);
|
||||||
|
renderMonitorExecutions(monitorState.executions, statusFilter);
|
||||||
|
renderMonitorPagination();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新监控面板失败:', error);
|
||||||
|
if (statsContainer) {
|
||||||
|
statsContainer.innerHTML = `<div class="monitor-error">无法加载统计信息:${escapeHtml(error.message)}</div>`;
|
||||||
|
}
|
||||||
|
if (execContainer) {
|
||||||
|
execContainer.innerHTML = `<div class="monitor-error">无法加载执行记录:${escapeHtml(error.message)}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
|
function renderMonitorStats(statsMap = {}, lastFetchedAt = null) {
|
||||||
@@ -1091,21 +1150,18 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(executions) || executions.length === 0) {
|
if (!Array.isArray(executions) || executions.length === 0) {
|
||||||
container.innerHTML = '<div class="monitor-empty">暂无执行记录</div>';
|
// 根据是否有筛选条件显示不同的提示
|
||||||
|
if (statusFilter && statusFilter !== 'all') {
|
||||||
|
container.innerHTML = '<div class="monitor-empty">当前筛选条件下暂无记录</div>';
|
||||||
|
} else {
|
||||||
|
container.innerHTML = '<div class="monitor-empty">暂无执行记录</div>';
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedStatus = statusFilter === 'all' ? null : statusFilter;
|
// 由于筛选已经在后端完成,这里直接使用所有传入的执行记录
|
||||||
const filtered = normalizedStatus
|
// 不再需要前端再次筛选,因为后端已经返回了筛选后的数据
|
||||||
? executions.filter(exec => (exec.status || '').toLowerCase() === normalizedStatus)
|
const rows = executions
|
||||||
: executions;
|
|
||||||
|
|
||||||
if (filtered.length === 0) {
|
|
||||||
container.innerHTML = '<div class="monitor-empty">当前筛选条件下暂无记录</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = filtered
|
|
||||||
.map(exec => {
|
.map(exec => {
|
||||||
const status = (exec.status || 'unknown').toLowerCase();
|
const status = (exec.status || 'unknown').toLowerCase();
|
||||||
const statusClass = `monitor-status-chip ${status}`;
|
const statusClass = `monitor-status-chip ${status}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user