diff --git a/cli/cli.js b/cli/cli.js index cb79ee0985ac9181dee33ed26142fda018b0ea76..868be113d5a52cdebb09ca59005c3b7ac36b3261 100644 --- a/cli/cli.js +++ b/cli/cli.js @@ -111,8 +111,10 @@ class MinecraftWSCLIClient { return true; } - async healthCheck() { - return await this.request('GET', '/health'); + async resetAIConfig() { + this.ensureAdminKeyForManagement(); + await this.request('DELETE', '/api/ai/config'); + return true; } } @@ -125,6 +127,22 @@ program .option('-s, --server-url ', 'API服务器URL', process.env.MC_WS_API_URL || HARDCODED_API_URL) .option('-k, --admin-key ', '用于管理操作的Admin Key', process.env.ADMIN_KEY || HARDCODED_ADMIN_KEY); +program + .command('reset-ai-config') + .description('重置/清除 AI 配置 (使用 Admin Key)') + .action(async () => { + try { + const opts = program.opts(); + const client = new MinecraftWSCLIClient(opts.serverUrl, opts.adminKey); + await client.resetAIConfig(); + console.log(`\x1b[32m✅ AI 配置已成功重置/清除!\x1b[0m`); + console.log('现在您可以重新在 Dashboard 配置 AI 设置。'); + } catch (error) { + console.error(`\x1b[31m❌ 错误: ${error.message}\x1b[0m`); + process.exit(1); + } + }); + program .command('create-key ') .description('创建新的API密钥') diff --git a/dashboard/public/app.js b/dashboard/public/app.js index 4948be20bae648f050142e5d62f18c1cd66e8a37..7d598555193c2c907b24f3bfe922136cc80813fd 100644 --- a/dashboard/public/app.js +++ b/dashboard/public/app.js @@ -35,8 +35,83 @@ const aiSystemPromptInput = document.getElementById('ai-system-prompt'); const aiEnabledCheckbox = document.getElementById('ai-enabled'); const aiTestBtn = document.getElementById('ai-test-btn'); const aiDeleteBtn = document.getElementById('ai-delete-btn'); +const aiProviderSelect = document.getElementById('ai-provider-select'); +const systemLogs = document.getElementById('system-logs'); +const clearLogsBtn = document.getElementById('clear-logs-btn'); + +// AI Providers Configuration +const AI_PROVIDERS = { + openai: { + url: 'https://api.openai.com/v1/chat/completions', + model: 'gpt-3.5-turbo' + }, + siliconflow: { + url: 'https://api.siliconflow.cn/v1/chat/completions', + model: 'deepseek-ai/DeepSeek-R1' + }, + gemini: { + url: 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', + model: 'gemini-2.0-flash-exp' + }, + deepseek: { + url: 'https://api.deepseek.com/chat/completions', + model: 'deepseek-chat' + }, + moonshot: { + url: 'https://api.moonshot.cn/v1/chat/completions', + model: 'moonshot-v1-8k' + }, + custom: { + url: '', + model: '' + } +}; + +if (aiProviderSelect) { + aiProviderSelect.addEventListener('change', (e) => { + const provider = AI_PROVIDERS[e.target.value]; + if (provider && e.target.value !== 'custom') { + if (aiApiUrlInput) aiApiUrlInput.value = provider.url; + if (aiModelIdInput) aiModelIdInput.value = provider.model; + } + }); +} + const aiStatus = document.getElementById('ai-status'); +// Theme Management +const themeToggleBtns = document.querySelectorAll('.theme-toggle'); + +function initTheme() { + const savedTheme = localStorage.getItem('theme') || 'dark'; + document.documentElement.setAttribute('data-theme', savedTheme); + updateThemeIcons(savedTheme); +} + +function toggleTheme() { + const currentTheme = document.documentElement.getAttribute('data-theme'); + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + updateThemeIcons(newTheme); +} + +function updateThemeIcons(theme) { + const text = theme === 'dark' ? '浅色模式' : '深色模式'; + + themeToggleBtns.forEach(btn => { + btn.textContent = text; + }); +} + +// Initialize Theme +initTheme(); + +themeToggleBtns.forEach(btn => { + btn.addEventListener('click', toggleTheme); +}); + function authHeaders(key) { return { Authorization: `Bearer ${key}` }; } @@ -172,33 +247,102 @@ function renderAdminKeys(keys) { return; } - adminKeysList.innerHTML = keys.map((key) => ` -
+ const adminKeys = keys.filter(k => k.keyType === 'admin'); + const regularKeys = keys.filter(k => k.keyType === 'regular'); + const serverKeys = keys.filter(k => k.keyType === 'server'); + const serverKeysMap = {}; + + serverKeys.forEach(key => { + if (key.regularKeyId) { + if (!serverKeysMap[key.regularKeyId]) { + serverKeysMap[key.regularKeyId] = []; + } + serverKeysMap[key.regularKeyId].push(key); + } + }); + + let html = ''; + + // Render Admin Keys + if (adminKeys.length > 0) { + html += '

管理员密钥 (Admin Keys)

'; + html += adminKeys.map(key => renderKeyCard(key)).join(''); + } + + // Render Regular Keys with nested Server Keys + if (regularKeys.length > 0) { + html += '

用户密钥 (Regular Keys)

'; + html += regularKeys.map(regularKey => { + const childServerKeys = serverKeysMap[regularKey.id] || []; + return ` +
+
+ ${renderKeyCard(regularKey)} + ${childServerKeys.length > 0 ? `` : ''} +
+ ${childServerKeys.length > 0 ? ` + + ` : ''} +
+ `; + }).join(''); + } + + // Render Orphaned Server Keys (if any) + const linkedServerKeyIds = new Set(Object.values(serverKeysMap).flat().map(k => k.id)); + const orphanServerKeys = serverKeys.filter(k => !linkedServerKeyIds.has(k.id)); + + if (orphanServerKeys.length > 0) { + html += '

独立服务器密钥 (Orphan Server Keys)

'; + html += orphanServerKeys.map(key => renderKeyCard(key)).join(''); + } + + adminKeysList.innerHTML = html; +} + +function toggleGroup(headerElement) { + const nestedContainer = headerElement.nextElementSibling; + const toggleIcon = headerElement.querySelector('.toggle-icon'); + + if (nestedContainer && nestedContainer.classList.contains('nested-server-keys')) { + nestedContainer.classList.toggle('hidden'); + if (toggleIcon) { + toggleIcon.style.transform = nestedContainer.classList.contains('hidden') ? 'rotate(0deg)' : 'rotate(180deg)'; + } + } +} + +function renderKeyCard(key, isNested = false) { + return ` +

${key.keyType === 'admin' ? 'Admin' : key.keyType === 'server' ? 'Server' : 'Regular'} - ${key.isActive ? 'Active' : 'Inactive'} + ${key.isActive ? '已启用' : '已停用'} ${key.name}

ID: ${key.id}

Prefix: ${key.keyPrefix}

${key.serverId ? `

Server ID: ${key.serverId}

` : ''} -

Created: ${new Date(key.createdAt).toLocaleString()}

-

Last Used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleString() : 'Never'}

+

创建时间: ${new Date(key.createdAt).toLocaleString()}

+

最后使用: ${key.lastUsed ? new Date(key.lastUsed).toLocaleString() : '从未'}

${key.isActive - ? `` - : `` + ? `` + : `` } - +
- `).join(''); + `; } function renderUserServerKeys(keys) { @@ -217,20 +361,20 @@ function renderUserServerKeys(keys) {

Server - ${key.isActive ? 'Active' : 'Inactive'} + ${key.isActive ? '已启用' : '已停用'} ${key.name}

ID: ${key.id}

Prefix: ${key.keyPrefix}

${key.serverId ? `

Server ID: ${key.serverId}

` : ''} -

Created: ${new Date(key.createdAt).toLocaleString()}

-

Last Used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleString() : 'Never'}

+

创建时间: ${new Date(key.createdAt).toLocaleString()}

+

最后使用: ${key.lastUsed ? new Date(key.lastUsed).toLocaleString() : '从未'}

${key.isActive - ? `` - : `` + ? `` + : `` }
@@ -293,7 +437,7 @@ async function deleteKey(keyId, keyName) { if (currentRole !== 'admin') { return; } - if (!confirm(`Delete key "${keyName}"? This action cannot be undone.`)) { + if (!confirm(`确定要删除密钥 "${keyName}" 吗? 此操作无法撤销。`)) { return; } try { @@ -365,21 +509,6 @@ async function loadStats() { connections.textContent = data.active_ws || 0; } - const totalKeys = document.getElementById('user-stat-total-keys'); - if (totalKeys) { - totalKeys.textContent = data.keys_total || 0; - } - - const adminKeys = document.getElementById('user-stat-admin-keys'); - if (adminKeys) { - adminKeys.textContent = data.admin_active || 0; - } - - const regularKeys = document.getElementById('user-stat-regular-keys'); - if (regularKeys) { - regularKeys.textContent = data.regular_active || 0; - } - const serverKeys = document.getElementById('user-stat-server-keys'); if (serverKeys) { serverKeys.textContent = data.server_active || 0; @@ -737,7 +866,7 @@ async function deleteAIConfig() { if (!apiKey) { return; } - if (!confirm('Are you sure you want to delete the AI configuration?')) { + if (!confirm('确定要删除 AI 配置吗?')) { return; } diff --git a/dashboard/public/index.html b/dashboard/public/index.html index ca06a8a7cbaf41bfd8bed0674ca4fea03c984cfe..0dff2f3dbfabe960d232125afddcfa0cfb060c76 100644 --- a/dashboard/public/index.html +++ b/dashboard/public/index.html @@ -3,7 +3,10 @@ - Minecraft WebSocket API - 控制台 + InterConnect Dashboard + + + @@ -28,6 +31,9 @@