AIClient-2-API/static/components/section-config.html
Wenaixi 1bc193f5eb feat: 添加定时健康检查供应商类型选择功能
类似启用代理的提供商,现在可以选择对哪些供应商类型进行定时健康检查

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-30 23:28:19 +08:00

437 lines
28 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>
<div class="form-group">
<label for="tlsSidecarProxyUrl" data-i18n="config.proxy.tlsSidecarProxyUrl">Sidecar 上游代理</label>
<input type="text" id="tlsSidecarProxyUrl" class="form-control" data-i18n-placeholder="config.proxy.urlPlaceholder" placeholder="例如: http://127.0.0.1:7890">
<small class="form-text" data-i18n="config.proxy.urlNote">TLS Sidecar 专用上游代理,留空则不使用代理</small>
</div>
<div class="form-group pool-section">
<label data-i18n="config.proxy.tlsSidecarEnabledProviders">启用 TLS Sidecar 的提供商</label>
<div id="tlsSidecarProviders" class="provider-tags">
<!-- 动态渲染 -->
</div>
<small class="form-text" data-i18n="config.proxy.enabledProvidersNote">点击选择需要通过 TLS Sidecar 访问的提供商</small>
</div>
<small class="form-text" data-i18n="config.proxy.tlsSidecarNote">启用后选中的提供商请求将通过 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.healthCheck.title"><i class="fas fa-heartbeat"></i> 定时健康检查</h3>
<div class="config-row">
<div class="form-group">
<label data-i18n="config.healthCheck.enabled">启用定时检查</label>
<label class="toggle-switch">
<input type="checkbox" id="scheduledHealthCheckEnabled">
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group">
<label data-i18n="config.healthCheck.startupRun">启动时运行</label>
<label class="toggle-switch">
<input type="checkbox" id="scheduledHealthCheckStartupRun">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="form-group">
<label for="scheduledHealthCheckInterval" data-i18n="config.healthCheck.interval">检查间隔</label>
<div class="input-with-quick-select">
<input type="number" id="scheduledHealthCheckInterval" class="form-control" min="60000" max="3600000" step="60000" value="600000" placeholder="毫秒">
<div class="quick-select-btns">
<button type="button" class="btn btn-sm btn-outline-secondary" data-value="300000">5分钟</button>
<button type="button" class="btn btn-sm btn-outline-secondary" data-value="600000">10分钟</button>
<button type="button" class="btn btn-sm btn-outline-secondary" data-value="1800000">30分钟</button>
</div>
</div>
<small class="form-text" data-i18n="config.healthCheck.intervalNote">单位毫秒最小60000ms(1分钟)最大3600000ms(1小时)</small>
</div>
<div class="form-group">
<label data-i18n="config.healthCheck.providerTypes">定时检查的供应商</label>
<div id="scheduledHealthCheckProviders" 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.healthCheck.providerTypesNote">选择需要进行定时健康检查的供应商类型</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>