AIClient-2-API/static/components/section-config.html
hex2077 fa19bae517 refactor(potluck): 简化 API 大锅饭系统并增强安全性和 UI
- 移除凭证管理和资源包系统,简化为基于每日限额的 Key 管理
- 新增登录安全防护(频率限制、账户锁定、IP 追踪)
- 重构日志系统使用 AsyncLocalStorage 替代全局状态
- 全面升级 UI 界面(主题切换、使用分布统计、响应式设计)
- 优化安装脚本(PowerShell 支持、手动安装指引)

BREAKING CHANGE: API Potluck 插件不再支持凭证资源包功能,所有 Key 仅基于每日限额进行配额管理。user-data-manager 模块已禁用,相关 API 端点已移除。
2026-03-05 17:21:47 +08:00

347 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<link rel="stylesheet" href="components/section-config.css">
<!-- Configuration Section -->
<section id="config" class="section" aria-labelledby="config-title">
<h2 id="config-title" data-i18n="config.title">配置管理</h2>
<div class="config-panel">
<div class="config-form">
<!-- 基础设置 -->
<div class="config-group-section">
<h3 data-i18n="config.basic.title"><i class="fas fa-cog"></i> 基础设置</h3>
<div class="form-group password-input-group">
<label for="apiKey" data-i18n="config.apiKey">API密钥</label>
<div class="password-input-wrapper">
<div class="input-with-toggle">
<input type="password" id="apiKey" class="form-control" data-i18n="config.apiKeyPlaceholder" placeholder="请输入API密钥" autocomplete="off">
<button type="button" class="password-toggle" data-target="apiKey" aria-label="显示/隐藏密码">
<i class="fas fa-eye" aria-hidden="true"></i>
</button>
</div>
<button type="button" class="btn btn-sm btn-secondary generate-key-btn" id="generateApiKey" data-i18n-title="config.apiKey.generateTitle" title="自动生成API密钥">
<i class="fas fa-magic"></i> <span data-i18n="config.apiKey.generate">生成</span>
</button>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="host" data-i18n="config.host">监听地址</label>
<input type="text" id="host" class="form-control" value="127.0.0.1" data-i18n-placeholder="config.hostPlaceholder" placeholder="例如: 127.0.0.1">
</div>
<div class="form-group">
<label for="port" data-i18n="config.port">端口</label>
<input type="number" id="port" class="form-control" value="3000" data-i18n-placeholder="config.portPlaceholder" placeholder="3000">
</div>
</div>
<div class="form-group pool-section">
<label data-i18n="config.modelProvider">模型提供商 (可多选)</label>
<div id="modelProvider" class="provider-tags">
<button type="button" class="provider-tag" data-value="gemini-cli-oauth">
<i class="fas fa-robot"></i>
<span data-i18n="dashboard.routing.nodeName.gemini">Gemini CLI OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="gemini-antigravity">
<i class="fas fa-rocket"></i>
<span data-i18n="dashboard.routing.nodeName.antigravity">Gemini Antigravity</span>
</button>
<button type="button" class="provider-tag" data-value="openai-custom">
<i class="fas fa-brain"></i>
<span data-i18n="dashboard.routing.nodeName.openai">OpenAI Custom</span>
</button>
<button type="button" class="provider-tag" data-value="claude-custom">
<i class="fas fa-comment-dots"></i>
<span data-i18n="dashboard.routing.nodeName.claude">Claude Custom</span>
</button>
<button type="button" class="provider-tag" data-value="claude-kiro-oauth">
<i class="fas fa-key"></i>
<span data-i18n="dashboard.routing.nodeName.kiro">Claude Kiro OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openai-qwen-oauth">
<i class="fas fa-cloud"></i>
<span data-i18n="dashboard.routing.nodeName.qwen">Qwen OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openaiResponses-custom">
<i class="fas fa-reply"></i>
<span>OpenAI Responses</span>
</button>
<button type="button" class="provider-tag" data-value="openai-iflow">
<i class="fas fa-stream"></i>
<span data-i18n="dashboard.routing.nodeName.iflow">iFlow OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openai-codex-oauth">
<i class="fas fa-code"></i>
<span data-i18n="dashboard.routing.nodeName.codex">OpenAI Codex OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="grok-custom">
<i class="fas fa-search"></i>
<span data-i18n="dashboard.routing.nodeName.grok">Grok Reverse</span>
</button>
</div>
<small class="form-text" data-i18n="config.modelProviderHelp">点击选择启动时初始化的模型提供商 (必须至少选择一个)</small>
</div>
</div>
<!-- 代理与网络 -->
<div class="config-group-section">
<h3 data-i18n="config.proxy.title"><i class="fas fa-globe"></i> 代理设置</h3>
<div class="form-group">
<label for="proxyUrl" data-i18n="config.proxy.url">代理地址</label>
<input type="text" id="proxyUrl" class="form-control" data-i18n-placeholder="config.proxy.urlPlaceholder" placeholder="例如: http://127.0.0.1:7890 或 socks5://127.0.0.1:1080">
<small class="form-text" data-i18n="config.proxy.urlNote">支持 HTTP、HTTPS 和 SOCKS5 代理,留空则不使用代理</small>
</div>
<div class="form-group pool-section">
<label data-i18n="config.proxy.enabledProviders">启用代理的提供商</label>
<div id="proxyProviders" class="provider-tags">
<button type="button" class="provider-tag" data-value="gemini-cli-oauth">
<i class="fas fa-robot"></i>
<span>Gemini CLI OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="gemini-antigravity">
<i class="fas fa-rocket"></i>
<span>Gemini Antigravity</span>
</button>
<button type="button" class="provider-tag" data-value="openai-custom">
<i class="fas fa-brain"></i>
<span>OpenAI Custom</span>
</button>
<button type="button" class="provider-tag" data-value="claude-custom">
<i class="fas fa-comment-dots"></i>
<span>Claude Custom</span>
</button>
<button type="button" class="provider-tag" data-value="claude-kiro-oauth">
<i class="fas fa-key"></i>
<span>Claude Kiro OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openai-qwen-oauth">
<i class="fas fa-cloud"></i>
<span>Qwen OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openaiResponses-custom">
<i class="fas fa-reply"></i>
<span>OpenAI Responses</span>
</button>
<button type="button" class="provider-tag" data-value="openai-iflow">
<i class="fas fa-stream"></i>
<span>iFlow OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="openai-codex-oauth">
<i class="fas fa-code"></i>
<span>OpenAI Codex OAuth</span>
</button>
<button type="button" class="provider-tag" data-value="grok-custom">
<i class="fas fa-search"></i>
<span>Grok Reverse</span>
</button>
</div>
<small class="form-text" data-i18n="config.proxy.enabledProvidersNote">点击选择需要通过代理访问的提供商,未选中的提供商将直接连接</small>
</div>
<hr>
<div class="config-row">
<div class="form-group">
<label data-i18n="config.proxy.tlsSidecarEnabled">TLS 指纹伪装 (uTLS Sidecar)</label>
<label class="toggle-switch">
<input type="checkbox" id="tlsSidecarEnabled">
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group">
<label for="tlsSidecarPort" data-i18n="config.proxy.tlsSidecarPort">Sidecar 端口</label>
<input type="number" id="tlsSidecarPort" class="form-control" min="1024" max="65535" value="9090">
</div>
</div>
<small class="form-text" data-i18n="config.proxy.tlsSidecarNote">启用后 Grok 请求将通过 Go uTLS sidecar 转发,完美模拟 Chrome TLS/H2 指纹绕过 Cloudflare需重启服务</small>
</div>
<!-- 服务治理与高可用 -->
<div class="config-group-section">
<h3 data-i18n="config.governance.title"><i class="fas fa-shield-alt"></i> 服务治理</h3>
<div class="config-row">
<div class="form-group">
<label for="requestMaxRetries" data-i18n="config.advanced.maxRetries">请求最大重试次数</label>
<input type="number" id="requestMaxRetries" class="form-control" min="0" max="10" value="3">
</div>
<div class="form-group">
<label for="requestBaseDelay" data-i18n="config.advanced.baseDelay">重试基础延迟(毫秒)</label>
<input type="number" id="requestBaseDelay" class="form-control" min="0" step="100" value="1000">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="credentialSwitchMaxRetries" data-i18n="config.advanced.credentialSwitchMaxRetries">坏凭证切换最大重试次数</label>
<input type="number" id="credentialSwitchMaxRetries" class="form-control" min="1" max="50" value="5">
</div>
<div class="form-group">
<label for="maxErrorCount" data-i18n="config.advanced.maxErrorCount">节点最大错误阈值</label>
<input type="number" id="maxErrorCount" class="form-control" value="10" min="1" max="20">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="warmupTarget" data-i18n="config.advanced.warmupTarget">系统预热节点数</label>
<input type="number" id="warmupTarget" class="form-control" min="0" max="100" value="0">
</div>
<div class="form-group">
<label for="refreshConcurrencyPerProvider" data-i18n="config.advanced.refreshConcurrencyPerProvider">提供商刷新并发数</label>
<input type="number" id="refreshConcurrencyPerProvider" class="form-control" min="1" max="10" value="1">
</div>
</div>
<div class="form-group pool-section">
<label for="providerFallbackChain" data-i18n="config.advanced.fallbackChain">跨类型 Fallback 链配置 (JSON)</label>
<textarea id="providerFallbackChain" class="form-control" rows="4" data-i18n-placeholder="config.advanced.fallbackChainPlaceholder"></textarea>
</div>
<div class="form-group pool-section">
<label for="modelFallbackMapping" data-i18n="config.advanced.modelFallbackMapping">跨协议模型 Fallback 映射 (JSON)</label>
<textarea id="modelFallbackMapping" class="form-control" rows="4" data-i18n-placeholder="config.advanced.modelFallbackMappingPlaceholder"></textarea>
</div>
</div>
<!-- OAuth 与令牌 -->
<div class="config-group-section">
<h3 data-i18n="config.oauth.title"><i class="fas fa-key"></i> OAuth & 令牌</h3>
<div class="config-row">
<div class="form-group">
<label for="cronNearMinutes" data-i18n="config.advanced.cronInterval">令牌刷新间隔(分钟)</label>
<input type="number" id="cronNearMinutes" class="form-control" min="1" max="60" value="1">
</div>
<div class="form-group">
<label data-i18n="config.advanced.cronEnabled">启用自动刷新 (需重启)</label>
<label class="toggle-switch">
<input type="checkbox" id="cronRefreshToken">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="form-group">
<label for="loginExpiry" data-i18n="config.advanced.loginExpiry">登录过期时间(秒)</label>
<input type="number" id="loginExpiry" class="form-control" min="60" value="3600">
<small class="form-text" data-i18n="config.advanced.loginExpiryNote">管理后台登录后的 Token 有效期,默认 3600 秒 (1小时)</small>
</div>
</div>
<!-- 日志管理 -->
<div class="config-group-section">
<h3 data-i18n="config.log.title"><i class="fas fa-file-alt"></i> 日志设置</h3>
<div class="config-row">
<div class="form-group">
<label data-i18n="config.log.enabled">启用日志</label>
<label class="toggle-switch">
<input type="checkbox" id="logEnabled" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group">
<label for="logOutputMode" data-i18n="config.log.outputMode">日志输出模式</label>
<select id="logOutputMode" class="form-control">
<option value="all" selected data-i18n="config.log.outputMode.all">全部 (控制台+文件)</option>
<option value="console" data-i18n="config.log.outputMode.console">仅控制台</option>
<option value="file" data-i18n="config.log.outputMode.file">仅文件</option>
<option value="none" data-i18n="config.log.outputMode.none">禁用</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="logLevel" data-i18n="config.log.level">日志级别</label>
<select id="logLevel" class="form-control">
<option value="debug" data-i18n="config.log.level.debug">调试 (debug)</option>
<option value="info" selected data-i18n="config.log.level.info">信息 (info)</option>
<option value="warn" data-i18n="config.log.level.warn">警告 (warn)</option>
<option value="error" data-i18n="config.log.level.error">错误 (error)</option>
</select>
</div>
<div class="form-group">
<label for="logDir" data-i18n="config.log.dir">日志目录</label>
<input type="text" id="logDir" class="form-control" value="logs">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="logMaxFileSize" data-i18n="config.log.maxFileSize">最大文件大小(字节)</label>
<input type="number" id="logMaxFileSize" class="form-control" value="10485760" step="1048576">
</div>
<div class="form-group">
<label for="logMaxFiles" data-i18n="config.log.maxFiles">最大保留文件数</label>
<input type="number" id="logMaxFiles" class="form-control" value="10" min="1">
</div>
</div>
<div class="config-row">
<div class="form-group">
<label data-i18n="config.log.includeRequestId">包含请求ID</label>
<label class="toggle-switch">
<input type="checkbox" id="logIncludeRequestId" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group">
<label data-i18n="config.log.includeTimestamp">包含时间戳</label>
<label class="toggle-switch">
<input type="checkbox" id="logIncludeTimestamp" checked>
<span class="toggle-slider"></span>
</label>
</div>
</div>
<hr>
<div class="form-row">
<div class="form-group">
<label for="promptLogMode" data-i18n="config.advanced.promptLogMode">提示日志模式</label>
<select id="promptLogMode" class="form-control">
<option value="none" data-i18n="config.advanced.promptLogMode.none">无 (none)</option>
<option value="console" data-i18n="config.advanced.promptLogMode.console">控制台 (console)</option>
<option value="file" data-i18n="config.advanced.promptLogMode.file">文件 (file)</option>
</select>
</div>
<div class="form-group">
<label for="promptLogBaseName" data-i18n="config.advanced.promptLogBaseName">提示日志基础名称</label>
<input type="text" id="promptLogBaseName" class="form-control">
</div>
</div>
</div>
<!-- 系统与高级 -->
<div class="config-group-section">
<h3 data-i18n="config.advanced.title"><i class="fas fa-cogs"></i> 系统与高级</h3>
<div class="form-group">
<label for="providerPoolsFilePath" data-i18n="config.advanced.poolFilePath">提供商池配置文件路径</label>
<input type="text" id="providerPoolsFilePath" class="form-control">
</div>
<div class="form-row">
<div class="form-group">
<label for="systemPromptFilePath" data-i18n="config.advanced.systemPromptFile">系统提示文件路径</label>
<input type="text" id="systemPromptFilePath" class="form-control">
</div>
<div class="form-group">
<label for="systemPromptMode" data-i18n="config.advanced.systemPromptMode">系统提示模式</label>
<select id="systemPromptMode" class="form-control">
<option value="append" selected data-i18n="config.advanced.systemPromptMode.append">追加 (append)</option>
<option value="overwrite" data-i18n="config.advanced.systemPromptMode.overwrite">覆盖 (overwrite)</option>
</select>
</div>
</div>
<div class="form-group">
<label for="systemPrompt" data-i18n="config.advanced.systemPrompt">系统提示内容</label>
<textarea id="systemPrompt" class="form-control" rows="4"></textarea>
</div>
<div class="form-group">
<label for="adminPassword" data-i18n="config.advanced.adminPassword">后台登录密码</label>
<div class="password-input-wrapper">
<div class="input-with-toggle">
<input type="password" id="adminPassword" class="form-control" autocomplete="new-password">
<button type="button" class="password-toggle" data-target="adminPassword" aria-label="显示/隐藏密码">
<i class="fas fa-eye" aria-hidden="true"></i>
</button>
</div>
</div>
<small class="form-text" data-i18n="config.advanced.adminPasswordNote">修改后需要重新登录</small>
</div>
</div>
<div class="form-actions">
<button class="btn btn-success" id="saveConfig">
<i class="fas fa-save"></i> <span data-i18n="config.save">保存配置</span>
</button>
<button class="btn btn-secondary" id="resetConfig">
<i class="fas fa-undo"></i> <span data-i18n="config.reset">重置</span>
</button>
</div>
</div>
</div>
</section>