mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-23 16:09:44 +02:00
Add files via upload
This commit is contained in:
@@ -84,6 +84,12 @@ header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
font-size: 0.875rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
@@ -91,6 +97,28 @@ header {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.settings-btn {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.settings-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.settings-btn svg {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
@@ -755,6 +783,7 @@ header {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: 1px solid var(--border-color);
|
||||
animation: slideDown 0.3s ease-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
@@ -775,6 +804,8 @@ header {
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background: var(--bg-primary);
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
@@ -1302,3 +1333,216 @@ header {
|
||||
font-size: 0.875rem;
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
/* 设置模态框样式 */
|
||||
.settings-modal-content {
|
||||
max-width: 800px;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.settings-body {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.settings-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.settings-section h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9375rem;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||||
}
|
||||
|
||||
.form-group input.error {
|
||||
border-color: var(--error-color);
|
||||
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
.form-group input.error:focus {
|
||||
border-color: var(--error-color);
|
||||
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
|
||||
.tools-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tools-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tools-actions button {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tools-actions button:hover {
|
||||
background: var(--bg-tertiary);
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.tools-actions input {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tools-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-primary);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tool-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tool-item:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.tool-item input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.tool-item-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tool-item-name {
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9375rem;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.tool-item-desc {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-secondary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tool-item.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
flex-shrink: 0;
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 10px 20px;
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 10px 20px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-tertiary);
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
+205
-7
@@ -1002,13 +1002,6 @@ function closeMCPDetail() {
|
||||
document.getElementById('mcp-detail-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
window.onclick = function(event) {
|
||||
const modal = document.getElementById('mcp-detail-modal');
|
||||
if (event.target == modal) {
|
||||
closeMCPDetail();
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function getStatusText(status) {
|
||||
@@ -1353,6 +1346,211 @@ async function cancelActiveTask(conversationId, button) {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置相关功能
|
||||
let currentConfig = null;
|
||||
let allTools = [];
|
||||
|
||||
// 打开设置
|
||||
async function openSettings() {
|
||||
const modal = document.getElementById('settings-modal');
|
||||
modal.style.display = 'block';
|
||||
|
||||
// 每次打开时重新加载最新配置
|
||||
await loadConfig();
|
||||
|
||||
// 清除之前的验证错误状态
|
||||
document.querySelectorAll('.form-group input').forEach(input => {
|
||||
input.classList.remove('error');
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭设置
|
||||
function closeSettings() {
|
||||
const modal = document.getElementById('settings-modal');
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
window.onclick = function(event) {
|
||||
const settingsModal = document.getElementById('settings-modal');
|
||||
const mcpModal = document.getElementById('mcp-detail-modal');
|
||||
|
||||
if (event.target == settingsModal) {
|
||||
closeSettings();
|
||||
}
|
||||
if (event.target == mcpModal) {
|
||||
closeMCPDetail();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/config');
|
||||
if (!response.ok) {
|
||||
throw new Error('获取配置失败');
|
||||
}
|
||||
|
||||
currentConfig = await response.json();
|
||||
|
||||
// 填充OpenAI配置
|
||||
document.getElementById('openai-api-key').value = currentConfig.openai.api_key || '';
|
||||
document.getElementById('openai-base-url').value = currentConfig.openai.base_url || '';
|
||||
document.getElementById('openai-model').value = currentConfig.openai.model || '';
|
||||
|
||||
// 填充Agent配置
|
||||
document.getElementById('agent-max-iterations').value = currentConfig.agent.max_iterations || 30;
|
||||
|
||||
// 填充工具列表
|
||||
allTools = currentConfig.tools || [];
|
||||
renderToolsList();
|
||||
} catch (error) {
|
||||
console.error('加载配置失败:', error);
|
||||
alert('加载配置失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染工具列表
|
||||
function renderToolsList() {
|
||||
const toolsList = document.getElementById('tools-list');
|
||||
toolsList.innerHTML = '';
|
||||
|
||||
allTools.forEach(tool => {
|
||||
const toolItem = document.createElement('div');
|
||||
toolItem.className = 'tool-item';
|
||||
toolItem.dataset.toolName = tool.name; // 保存原始工具名称
|
||||
toolItem.innerHTML = `
|
||||
<input type="checkbox" id="tool-${tool.name}" ${tool.enabled ? 'checked' : ''} />
|
||||
<div class="tool-item-info">
|
||||
<div class="tool-item-name">${escapeHtml(tool.name)}</div>
|
||||
<div class="tool-item-desc">${escapeHtml(tool.description || '无描述')}</div>
|
||||
</div>
|
||||
`;
|
||||
toolsList.appendChild(toolItem);
|
||||
});
|
||||
}
|
||||
|
||||
// 全选工具
|
||||
function selectAllTools() {
|
||||
document.querySelectorAll('#tools-list input[type="checkbox"]').forEach(checkbox => {
|
||||
checkbox.checked = true;
|
||||
});
|
||||
}
|
||||
|
||||
// 全不选工具
|
||||
function deselectAllTools() {
|
||||
document.querySelectorAll('#tools-list input[type="checkbox"]').forEach(checkbox => {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 过滤工具
|
||||
function filterTools() {
|
||||
const searchTerm = document.getElementById('tools-search').value.toLowerCase();
|
||||
document.querySelectorAll('.tool-item').forEach(item => {
|
||||
const toolName = (item.dataset.toolName || '').toLowerCase();
|
||||
const toolDesc = item.querySelector('.tool-item-desc').textContent.toLowerCase();
|
||||
if (toolName.includes(searchTerm) || toolDesc.includes(searchTerm)) {
|
||||
item.classList.remove('hidden');
|
||||
} else {
|
||||
item.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 应用设置
|
||||
async function applySettings() {
|
||||
try {
|
||||
// 清除之前的验证错误状态
|
||||
document.querySelectorAll('.form-group input').forEach(input => {
|
||||
input.classList.remove('error');
|
||||
});
|
||||
|
||||
// 验证必填字段
|
||||
const apiKey = document.getElementById('openai-api-key').value.trim();
|
||||
const baseUrl = document.getElementById('openai-base-url').value.trim();
|
||||
const model = document.getElementById('openai-model').value.trim();
|
||||
|
||||
let hasError = false;
|
||||
|
||||
if (!apiKey) {
|
||||
document.getElementById('openai-api-key').classList.add('error');
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!baseUrl) {
|
||||
document.getElementById('openai-base-url').classList.add('error');
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
document.getElementById('openai-model').classList.add('error');
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
alert('请填写所有必填字段(标记为 * 的字段)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 收集配置
|
||||
const config = {
|
||||
openai: {
|
||||
api_key: apiKey,
|
||||
base_url: baseUrl,
|
||||
model: model
|
||||
},
|
||||
agent: {
|
||||
max_iterations: parseInt(document.getElementById('agent-max-iterations').value) || 30
|
||||
},
|
||||
tools: []
|
||||
};
|
||||
|
||||
// 收集工具启用状态
|
||||
document.querySelectorAll('#tools-list .tool-item').forEach(item => {
|
||||
const checkbox = item.querySelector('input[type="checkbox"]');
|
||||
const toolName = item.dataset.toolName;
|
||||
if (toolName) {
|
||||
// 直接使用工具名称
|
||||
config.tools.push({
|
||||
name: toolName,
|
||||
enabled: checkbox.checked
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 更新配置
|
||||
const updateResponse = await fetch('/api/config', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
|
||||
if (!updateResponse.ok) {
|
||||
const error = await updateResponse.json();
|
||||
throw new Error(error.error || '更新配置失败');
|
||||
}
|
||||
|
||||
// 应用配置
|
||||
const applyResponse = await fetch('/api/config/apply', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!applyResponse.ok) {
|
||||
const error = await applyResponse.json();
|
||||
throw new Error(error.error || '应用配置失败');
|
||||
}
|
||||
|
||||
alert('配置已成功应用!');
|
||||
closeSettings();
|
||||
} catch (error) {
|
||||
console.error('应用配置失败:', error);
|
||||
alert('应用配置失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 加载对话列表
|
||||
|
||||
@@ -18,7 +18,15 @@
|
||||
</svg>
|
||||
<h1>CyberStrike</h1>
|
||||
</div>
|
||||
<p class="header-subtitle">安全测试平台</p>
|
||||
<div class="header-right">
|
||||
<p class="header-subtitle">安全测试平台</p>
|
||||
<button class="settings-btn" onclick="openSettings()" title="设置">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -48,6 +56,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置模态框 -->
|
||||
<div id="settings-modal" class="modal">
|
||||
<div class="modal-content settings-modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>系统设置</h2>
|
||||
<span class="modal-close" onclick="closeSettings()">×</span>
|
||||
</div>
|
||||
<div class="modal-body settings-body">
|
||||
<!-- OpenAI配置 -->
|
||||
<div class="settings-section">
|
||||
<h3>OpenAI 配置</h3>
|
||||
<div class="settings-form">
|
||||
<div class="form-group">
|
||||
<label for="openai-api-key">API Key <span style="color: red;">*</span></label>
|
||||
<input type="password" id="openai-api-key" placeholder="输入OpenAI API Key" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openai-base-url">Base URL <span style="color: red;">*</span></label>
|
||||
<input type="text" id="openai-base-url" placeholder="https://api.openai.com/v1" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openai-model">模型 <span style="color: red;">*</span></label>
|
||||
<input type="text" id="openai-model" placeholder="gpt-4" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MCP工具配置 -->
|
||||
<div class="settings-section">
|
||||
<h3>MCP 工具配置</h3>
|
||||
<div class="tools-controls">
|
||||
<div class="tools-actions">
|
||||
<button class="btn-secondary" onclick="selectAllTools()">全选</button>
|
||||
<button class="btn-secondary" onclick="deselectAllTools()">全不选</button>
|
||||
<input type="text" id="tools-search" placeholder="搜索工具..." oninput="filterTools()" />
|
||||
</div>
|
||||
<div id="tools-list" class="tools-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent配置 -->
|
||||
<div class="settings-section">
|
||||
<h3>Agent 配置</h3>
|
||||
<div class="settings-form">
|
||||
<div class="form-group">
|
||||
<label for="agent-max-iterations">最大迭代次数</label>
|
||||
<input type="number" id="agent-max-iterations" min="1" max="100" placeholder="30" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" onclick="closeSettings()">取消</button>
|
||||
<button class="btn-primary" onclick="applySettings()">应用配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MCP调用详情模态框 -->
|
||||
<div id="mcp-detail-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
|
||||
Reference in New Issue
Block a user