docs(terminology): 统一术语标准 - 将"供应商"规范为"提供商"
将项目中所有文档、注释和用户界面中的"供应商"术语统一修改为"提供商",提升术语一致性。同时: - 优化提供商健康检查频率从30分钟调整为10分钟,提高监控及时性 - 新增路由URL动态更新功能,支持不同部署环境的路径适配 - 更新相关样式类名和注释,确保代码与文档术语保持一致
This commit is contained in:
parent
ee050c77f2
commit
0149bf6558
12 changed files with 137 additions and 106 deletions
|
|
@ -202,7 +202,7 @@ install-and-run.bat
|
|||
|
||||
**⚙️ 配置管理**:实时参数修改,支持所有提供商(Gemini、OpenAI、Claude、Kiro、Qwen),包含高级设置和文件上传
|
||||
|
||||
**🔗 供应商池**:监控活动连接、提供商健康统计、启用/禁用管理
|
||||
**🔗 提供商池**:监控活动连接、提供商健康统计、启用/禁用管理
|
||||
|
||||
**📁 配置文件**:OAuth 凭据集中管理,支持搜索过滤和文件操作
|
||||
|
||||
|
|
@ -261,17 +261,17 @@ install-and-run.bat
|
|||
|
||||
---
|
||||
|
||||
### 🔄 模型供应商切换
|
||||
### 🔄 模型提供商切换
|
||||
|
||||
本项目提供两种灵活的模型切换方式,满足不同使用场景的需求。
|
||||
|
||||
通过在 API 请求路径中指定供应商标识,实现即时切换:
|
||||
通过在 API 请求路径中指定提供商标识,实现即时切换:
|
||||
|
||||
| 路由路径 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| `/claude-custom` | 使用配置文件中的 Claude API | 官方 Claude API 调用 |
|
||||
| `/claude-kiro-oauth` | 通过 Kiro OAuth 访问 Claude | 免费使用 Claude Sonnet 4.5 |
|
||||
| `/openai-custom` | 使用 OpenAI 供应商处理请求 | 标准 OpenAI API 调用 |
|
||||
| `/openai-custom` | 使用 OpenAI 提供商处理请求 | 标准 OpenAI API 调用 |
|
||||
| `/gemini-cli-oauth` | 通过 Gemini CLI OAuth 访问 | 突破 Gemini 免费限制 |
|
||||
| `/openai-qwen-oauth` | 通过 Qwen OAuth 访问 | 使用 Qwen Code Plus |
|
||||
| `/openaiResponses-custom` | OpenAI Responses API | 结构化对话场景 |
|
||||
|
|
|
|||
12
UI_README.md
12
UI_README.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 概述
|
||||
|
||||
AIClient2API 现在包含一个功能完整的可视化 Web UI 管理控制台,允许您通过浏览器轻松管理配置、监控供应商池状态、查看实时日志、配置AI模型提供商等。
|
||||
AIClient2API 现在包含一个功能完整的可视化 Web UI 管理控制台,允许您通过浏览器轻松管理配置、监控提供商池状态、查看实时日志、配置AI模型提供商等。
|
||||
|
||||
## 功能特性
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ AIClient2API 现在包含一个功能完整的可视化 Web UI 管理控制台
|
|||
|
||||
### 📊 实时监控
|
||||
- **仪表盘**:显示运行时间、系统信息、Node.js版本、服务器时间、内存使用
|
||||
- **供应商池管理**:查看各提供商账户状态、使用统计、错误率
|
||||
- **提供商池管理**:查看各提供商账户状态、使用统计、错误率
|
||||
- **活动统计**:活动连接、活跃提供商、健康提供商数量
|
||||
|
||||
### ⚙️ 配置管理
|
||||
|
|
@ -32,7 +32,7 @@ AIClient2API 现在包含一个功能完整的可视化 Web UI 管理控制台
|
|||
- 提示日志配置
|
||||
- 请求重试机制(最大重试次数、基础延迟)
|
||||
- OAuth令牌自动刷新设置
|
||||
- 供应商池配置文件路径
|
||||
- 提供商池配置文件路径
|
||||
|
||||
### 🔧 上传配置管理
|
||||
- 搜索配置文件功能
|
||||
|
|
@ -87,7 +87,7 @@ node src/api-server.js --port 3000 --api-key 123456
|
|||
|
||||
1. **仪表盘** - 系统概览、统计信息和路径路由示例
|
||||
2. **配置管理** - 修改服务器配置和提供商设置
|
||||
3. **供应商池管理** - 管理多个API提供商账户
|
||||
3. **提供商池管理** - 管理多个API提供商账户
|
||||
4. **上传配置管理** - 管理配置文件和搜索过滤
|
||||
5. **实时日志** - 查看服务器运行日志
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ node src/api-server.js --port 3000 --api-key 123456
|
|||
|
||||
### 系统信息
|
||||
- `GET /api/system` - 获取系统信息
|
||||
- `GET /api/providers` - 获取供应商池信息
|
||||
- `GET /api/providers` - 获取提供商池信息
|
||||
|
||||
### 实时数据
|
||||
- `GET /api/events` - Server-Sent Events 流,用于实时更新
|
||||
|
|
@ -203,7 +203,7 @@ A: 在配置管理页面选择对应的OAuth提供商,填写项目ID和OAuth
|
|||
A: 上传配置文件可以将本地配置文件上传到服务器,方便管理和在不同环境间同步配置。
|
||||
|
||||
### Q: 如何查看更详细的提供商信息?
|
||||
A: 在"供应商池"页面可以看到每个提供商的使用次数、错误次数、最后使用时间等详细信息。
|
||||
A: 在"提供商池"页面可以看到每个提供商的使用次数、错误次数、最后使用时间等详细信息。
|
||||
|
||||
### Q: 配置修改后需要重启服务器吗?
|
||||
A: 大部分配置(如系统提示、API密钥)会立即生效,但网络端口等更改需要重启服务器。
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class ProviderPoolManager {
|
|||
this.providerStatus = {}; // Tracks health and usage for each provider instance
|
||||
this.roundRobinIndex = {}; // Tracks the current index for round-robin selection for each provider type
|
||||
this.maxErrorCount = options.maxErrorCount || 3; // Default to 1 errors before marking unhealthy
|
||||
this.healthCheckInterval = options.healthCheckInterval || 30 * 60 * 1000; // Default to 30 minutes
|
||||
this.healthCheckInterval = options.healthCheckInterval || 10 * 60 * 1000; // Default to 10 minutes
|
||||
|
||||
// 优化1: 添加防抖机制,避免频繁的文件 I/O 操作
|
||||
this.saveDebounceTime = options.saveDebounceTime || 1000; // 默认1秒防抖
|
||||
|
|
@ -140,9 +140,9 @@ export class ProviderPoolManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* 禁用指定供应商
|
||||
* @param {string} providerType - 供应商类型
|
||||
* @param {object} providerConfig - 供应商配置
|
||||
* 禁用指定提供商
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @param {object} providerConfig - 提供商配置
|
||||
*/
|
||||
disableProvider(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
|
|
@ -159,9 +159,9 @@ export class ProviderPoolManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* 启用指定供应商
|
||||
* @param {string} providerType - 供应商类型
|
||||
* @param {object} providerConfig - 供应商配置
|
||||
* 启用指定提供商
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @param {object} providerConfig - 提供商配置
|
||||
*/
|
||||
enableProvider(providerType, providerConfig) {
|
||||
const pool = this.providerStatus[providerType];
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
|||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// 广播供应商更新事件
|
||||
// 广播提供商更新事件
|
||||
broadcastEvent('provider_update', {
|
||||
action: 'add',
|
||||
providerType,
|
||||
|
|
@ -1216,13 +1216,13 @@ async function scanConfigFiles(currentConfig, providerPoolManager) {
|
|||
}
|
||||
}
|
||||
|
||||
// 使用最新的供应商池数据
|
||||
// 使用最新的提供商池数据
|
||||
let providerPools = currentConfig.providerPools;
|
||||
if (providerPoolManager && providerPoolManager.providerPools) {
|
||||
providerPools = providerPoolManager.providerPools;
|
||||
}
|
||||
|
||||
// 检查供应商池文件中的所有OAuth凭据路径 - 标准化路径格式
|
||||
// 检查提供商池文件中的所有OAuth凭据路径 - 标准化路径格式
|
||||
if (providerPools) {
|
||||
for (const [providerType, providers] of Object.entries(providerPools)) {
|
||||
for (const provider of providers) {
|
||||
|
|
@ -1409,7 +1409,7 @@ function getFileUsageInfo(relativePath, fileName, usedPaths, currentConfig) {
|
|||
});
|
||||
}
|
||||
|
||||
// 检查供应商池中的使用情况
|
||||
// 检查提供商池中的使用情况
|
||||
if (currentConfig.providerPools) {
|
||||
// 使用 flatMap 将双重循环优化为单层循环 O(n)
|
||||
const allProviders = Object.entries(currentConfig.providerPools).flatMap(
|
||||
|
|
@ -1424,7 +1424,7 @@ function getFileUsageInfo(relativePath, fileName, usedPaths, currentConfig) {
|
|||
(pathsEqual(relativePath, provider.GEMINI_OAUTH_CREDS_FILE_PATH) ||
|
||||
pathsEqual(relativePath, provider.GEMINI_OAUTH_CREDS_FILE_PATH.replace(/\\/g, '/')))) {
|
||||
providerUsages.push({
|
||||
type: '供应商池',
|
||||
type: '提供商池',
|
||||
location: `Gemini OAuth凭据 (节点${index + 1})`,
|
||||
providerType: providerType,
|
||||
providerIndex: index,
|
||||
|
|
@ -1436,7 +1436,7 @@ function getFileUsageInfo(relativePath, fileName, usedPaths, currentConfig) {
|
|||
(pathsEqual(relativePath, provider.KIRO_OAUTH_CREDS_FILE_PATH) ||
|
||||
pathsEqual(relativePath, provider.KIRO_OAUTH_CREDS_FILE_PATH.replace(/\\/g, '/')))) {
|
||||
providerUsages.push({
|
||||
type: '供应商池',
|
||||
type: '提供商池',
|
||||
location: `Kiro OAuth凭据 (节点${index + 1})`,
|
||||
providerType: providerType,
|
||||
providerIndex: index,
|
||||
|
|
@ -1448,7 +1448,7 @@ function getFileUsageInfo(relativePath, fileName, usedPaths, currentConfig) {
|
|||
(pathsEqual(relativePath, provider.QWEN_OAUTH_CREDS_FILE_PATH) ||
|
||||
pathsEqual(relativePath, provider.QWEN_OAUTH_CREDS_FILE_PATH.replace(/\\/g, '/')))) {
|
||||
providerUsages.push({
|
||||
type: '供应商池',
|
||||
type: '提供商池',
|
||||
location: `Qwen OAuth凭据 (节点${index + 1})`,
|
||||
providerType: providerType,
|
||||
providerIndex: index,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ async function loadConfiguration() {
|
|||
if (cronRefreshTokenEl) cronRefreshTokenEl.checked = data.CRON_REFRESH_TOKEN || false;
|
||||
if (providerPoolsFilePathEl) providerPoolsFilePathEl.value = data.PROVIDER_POOLS_FILE_PATH || '';
|
||||
|
||||
// 触发供应商配置显示
|
||||
// 触发提供商配置显示
|
||||
handleProviderChange();
|
||||
|
||||
// 根据Gemini凭据类型设置显示
|
||||
|
|
@ -105,7 +105,7 @@ async function loadConfiguration() {
|
|||
handleKiroCredsTypeChange({ target: kiroRadio });
|
||||
}
|
||||
|
||||
// 检查并设置供应商池菜单显示状态
|
||||
// 检查并设置提供商池菜单显示状态
|
||||
const providerPoolsFilePath = data.PROVIDER_POOLS_FILE_PATH;
|
||||
const providersMenuItem = document.querySelector('.nav-item[data-section="providers"]');
|
||||
if (providerPoolsFilePath && providerPoolsFilePath.trim() !== '') {
|
||||
|
|
@ -131,7 +131,7 @@ async function saveConfiguration() {
|
|||
systemPrompt: document.getElementById('systemPrompt')?.value || '',
|
||||
};
|
||||
|
||||
// 根据不同供应商保存不同的配置
|
||||
// 根据不同提供商保存不同的配置
|
||||
const provider = document.getElementById('modelProvider')?.value;
|
||||
|
||||
switch (provider) {
|
||||
|
|
@ -194,12 +194,12 @@ async function saveConfiguration() {
|
|||
|
||||
showToast('配置已保存', 'success');
|
||||
|
||||
// 检查当前是否在供应商池管理页面,如果是则刷新数据
|
||||
// 检查当前是否在提供商池管理页面,如果是则刷新数据
|
||||
const providersSection = document.getElementById('providers');
|
||||
if (providersSection && providersSection.classList.contains('active')) {
|
||||
// 当前在供应商池页面,刷新数据
|
||||
// 当前在提供商池页面,刷新数据
|
||||
await loadProviders();
|
||||
showToast('供应商池数据已刷新', 'success');
|
||||
showToast('提供商池数据已刷新', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save configuration:', error);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ function initEventListeners() {
|
|||
button.addEventListener('click', handlePasswordToggle);
|
||||
});
|
||||
|
||||
// 供应商池配置监听
|
||||
// 提供商池配置监听
|
||||
const providerPoolsInput = document.getElementById('providerPoolsFilePath');
|
||||
if (providerPoolsInput) {
|
||||
providerPoolsInput.addEventListener('input', handleProviderPoolsConfigChange);
|
||||
|
|
@ -92,7 +92,7 @@ function initEventListeners() {
|
|||
}
|
||||
|
||||
/**
|
||||
* 供应商配置切换处理
|
||||
* 提供商配置切换处理
|
||||
*/
|
||||
function handleProviderChange() {
|
||||
const selectedProvider = elements.modelProvider?.value;
|
||||
|
|
@ -100,12 +100,12 @@ function handleProviderChange() {
|
|||
|
||||
const allProviderConfigs = document.querySelectorAll('.provider-config');
|
||||
|
||||
// 隐藏所有供应商配置
|
||||
// 隐藏所有提供商配置
|
||||
allProviderConfigs.forEach(config => {
|
||||
config.style.display = 'none';
|
||||
});
|
||||
|
||||
// 显示当前选中的供应商配置
|
||||
// 显示当前选中的提供商配置
|
||||
const targetConfig = document.querySelector(`[data-provider="${selectedProvider}"]`);
|
||||
if (targetConfig) {
|
||||
targetConfig.style.display = 'block';
|
||||
|
|
@ -172,7 +172,7 @@ function handlePasswordToggle(event) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 供应商池配置变化处理
|
||||
* 提供商池配置变化处理
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
function handleProviderPoolsConfigChange(event) {
|
||||
|
|
@ -180,13 +180,13 @@ function handleProviderPoolsConfigChange(event) {
|
|||
const providersMenuItem = document.querySelector('.nav-item[data-section="providers"]');
|
||||
|
||||
if (filePath) {
|
||||
// 显示供应商池菜单
|
||||
// 显示提供商池菜单
|
||||
if (providersMenuItem) providersMenuItem.style.display = 'flex';
|
||||
} else {
|
||||
// 隐藏供应商池菜单
|
||||
// 隐藏提供商池菜单
|
||||
if (providersMenuItem) providersMenuItem.style.display = 'none';
|
||||
|
||||
// 如果当前在供应商池页面,切换到仪表盘
|
||||
// 如果当前在提供商池页面,切换到仪表盘
|
||||
if (providersMenuItem && providersMenuItem.classList.contains('active')) {
|
||||
const dashboardItem = document.querySelector('.nav-item[data-section="dashboard"]');
|
||||
const dashboardSection = document.getElementById('dashboard');
|
||||
|
|
|
|||
|
|
@ -106,19 +106,19 @@ function updateProviderStatus(data) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理供应商更新事件
|
||||
* 处理提供商更新事件
|
||||
* @param {Object} data - 更新数据
|
||||
*/
|
||||
function handleProviderUpdate(data) {
|
||||
if (data.action && data.providerType) {
|
||||
// 如果当前打开的模态框是更新事件的供应商类型,则刷新该模态框
|
||||
// 如果当前打开的模态框是更新事件的提供商类型,则刷新该模态框
|
||||
const modal = document.querySelector('.provider-modal');
|
||||
if (modal && modal.getAttribute('data-provider-type') === data.providerType) {
|
||||
if (typeof refreshProviderConfig === 'function') {
|
||||
refreshProviderConfig(data.providerType);
|
||||
}
|
||||
} else {
|
||||
// 否则更新主界面的供应商列表
|
||||
// 否则更新主界面的提供商列表
|
||||
if (typeof loadProviders === 'function') {
|
||||
loadProviders();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { showToast, getFieldLabel, getProviderTypeFields } from './utils.js';
|
|||
import { handleProviderPasswordToggle } from './event-handlers.js';
|
||||
|
||||
/**
|
||||
* 显示供应商管理模态框
|
||||
* @param {Object} data - 供应商数据
|
||||
* 显示提供商管理模态框
|
||||
* @param {Object} data - 提供商数据
|
||||
*/
|
||||
function showProviderManagerModal(data) {
|
||||
const { providerType, providers, totalCount, healthyCount } = data;
|
||||
|
|
@ -27,7 +27,7 @@ function showProviderManagerModal(data) {
|
|||
modal.innerHTML = `
|
||||
<div class="provider-modal-content">
|
||||
<div class="provider-modal-header">
|
||||
<h3><i class="fas fa-cogs"></i> 管理 ${providerType} 供应商配置</h3>
|
||||
<h3><i class="fas fa-cogs"></i> 管理 ${providerType} 提供商配置</h3>
|
||||
<button class="modal-close" onclick="window.closeProviderModal(this)">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
|
|
@ -44,7 +44,7 @@ function showProviderManagerModal(data) {
|
|||
</div>
|
||||
<div class="provider-summary-actions">
|
||||
<button class="btn btn-success" onclick="window.showAddProviderForm('${providerType}')">
|
||||
<i class="fas fa-plus"></i> 添加新供应商
|
||||
<i class="fas fa-plus"></i> 添加新提供商
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -149,8 +149,8 @@ function closeProviderModal(button) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 渲染供应商列表
|
||||
* @param {Array} providers - 供应商数组
|
||||
* 渲染提供商列表
|
||||
* @param {Array} providers - 提供商数组
|
||||
* @returns {string} HTML字符串
|
||||
*/
|
||||
function renderProviderList(providers) {
|
||||
|
|
@ -188,7 +188,7 @@ function renderProviderList(providers) {
|
|||
</div>
|
||||
</div>
|
||||
<div class="provider-actions-group">
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${provider.uuid}', event)" title="${toggleButtonText}此供应商">
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${provider.uuid}', event)" title="${toggleButtonText}此提供商">
|
||||
<i class="${toggleButtonIcon}"></i> ${toggleButtonText}
|
||||
</button>
|
||||
<button class="btn-small btn-edit" onclick="window.editProvider('${provider.uuid}', event)">
|
||||
|
|
@ -210,8 +210,8 @@ function renderProviderList(providers) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 渲染供应商配置
|
||||
* @param {Object} provider - 供应商对象
|
||||
* 渲染提供商配置
|
||||
* @param {Object} provider - 提供商对象
|
||||
* @returns {string} HTML字符串
|
||||
*/
|
||||
function renderProviderConfig(provider) {
|
||||
|
|
@ -385,7 +385,7 @@ function renderProviderConfig(provider) {
|
|||
|
||||
/**
|
||||
* 获取字段显示顺序
|
||||
* @param {Object} provider - 供应商对象
|
||||
* @param {Object} provider - 提供商对象
|
||||
* @returns {Array} 字段键数组
|
||||
*/
|
||||
function getFieldOrder(provider) {
|
||||
|
|
@ -405,8 +405,8 @@ function getFieldOrder(provider) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 切换供应商详情显示
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* 切换提供商详情显示
|
||||
* @param {string} uuid - 提供商UUID
|
||||
*/
|
||||
function toggleProviderDetails(uuid) {
|
||||
const content = document.getElementById(`content-${uuid}`);
|
||||
|
|
@ -416,8 +416,8 @@ function toggleProviderDetails(uuid) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 编辑供应商
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* 编辑提供商
|
||||
* @param {string} uuid - 提供商UUID
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
function editProvider(uuid, event) {
|
||||
|
|
@ -465,7 +465,7 @@ function editProvider(uuid, event) {
|
|||
const toggleButtonClass = isCurrentlyDisabled ? 'btn-success' : 'btn-warning';
|
||||
|
||||
actionsGroup.innerHTML = `
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${uuid}', event)" title="${toggleButtonText}此供应商">
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${uuid}', event)" title="${toggleButtonText}此提供商">
|
||||
<i class="${toggleButtonIcon}"></i> ${toggleButtonText}
|
||||
</button>
|
||||
<button class="btn-small btn-save" onclick="window.saveProvider('${uuid}', event)">
|
||||
|
|
@ -480,7 +480,7 @@ function editProvider(uuid, event) {
|
|||
|
||||
/**
|
||||
* 取消编辑
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* @param {string} uuid - 提供商UUID
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
function cancelEdit(uuid, event) {
|
||||
|
|
@ -523,7 +523,7 @@ function cancelEdit(uuid, event) {
|
|||
const toggleButtonClass = isCurrentlyDisabled ? 'btn-success' : 'btn-warning';
|
||||
|
||||
actionsGroup.innerHTML = `
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${uuid}', event)" title="${toggleButtonText}此供应商">
|
||||
<button class="btn-small ${toggleButtonClass}" onclick="window.toggleProviderStatus('${uuid}', event)" title="${toggleButtonText}此提供商">
|
||||
<i class="${toggleButtonIcon}"></i> ${toggleButtonText}
|
||||
</button>
|
||||
<button class="btn-small btn-edit" onclick="window.editProvider('${uuid}', event)">
|
||||
|
|
@ -536,8 +536,8 @@ function cancelEdit(uuid, event) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 保存供应商
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* 保存提供商
|
||||
* @param {string} uuid - 提供商UUID
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
async function saveProvider(uuid, event) {
|
||||
|
|
@ -564,8 +564,8 @@ async function saveProvider(uuid, event) {
|
|||
|
||||
try {
|
||||
await window.apiClient.put(`/providers/${encodeURIComponent(providerType)}/${uuid}`, { providerConfig });
|
||||
showToast('供应商配置更新成功', 'success');
|
||||
// 重新获取该供应商类型的最新配置
|
||||
showToast('提供商配置更新成功', 'success');
|
||||
// 重新获取该提供商类型的最新配置
|
||||
await refreshProviderConfig(providerType);
|
||||
} catch (error) {
|
||||
console.error('Failed to update provider:', error);
|
||||
|
|
@ -574,14 +574,14 @@ async function saveProvider(uuid, event) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 删除供应商
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* 删除提供商
|
||||
* @param {string} uuid - 提供商UUID
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
async function deleteProvider(uuid, event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (!confirm('确定要删除这个供应商配置吗?此操作不可恢复。')) {
|
||||
if (!confirm('确定要删除这个提供商配置吗?此操作不可恢复。')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -590,7 +590,7 @@ async function deleteProvider(uuid, event) {
|
|||
|
||||
try {
|
||||
await window.apiClient.delete(`/providers/${encodeURIComponent(providerType)}/${uuid}`);
|
||||
showToast('供应商配置删除成功', 'success');
|
||||
showToast('提供商配置删除成功', 'success');
|
||||
// 重新获取最新配置
|
||||
await refreshProviderConfig(providerType);
|
||||
} catch (error) {
|
||||
|
|
@ -600,15 +600,15 @@ async function deleteProvider(uuid, event) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 重新获取并刷新供应商配置
|
||||
* @param {string} providerType - 供应商类型
|
||||
* 重新获取并刷新提供商配置
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
async function refreshProviderConfig(providerType) {
|
||||
try {
|
||||
// 重新获取该供应商类型的最新数据
|
||||
// 重新获取该提供商类型的最新数据
|
||||
const data = await window.apiClient.get(`/providers/${encodeURIComponent(providerType)}`);
|
||||
|
||||
// 如果当前显示的是该供应商类型的模态框,则更新模态框
|
||||
// 如果当前显示的是该提供商类型的模态框,则更新模态框
|
||||
const modal = document.querySelector('.provider-modal');
|
||||
if (modal && modal.getAttribute('data-provider-type') === providerType) {
|
||||
// 更新统计信息
|
||||
|
|
@ -622,14 +622,14 @@ async function refreshProviderConfig(providerType) {
|
|||
healthyCountElement.textContent = data.healthyCount;
|
||||
}
|
||||
|
||||
// 重新渲染供应商列表
|
||||
// 重新渲染提供商列表
|
||||
const providerList = modal.querySelector('.provider-list');
|
||||
if (providerList) {
|
||||
providerList.innerHTML = renderProviderList(data.providers);
|
||||
}
|
||||
}
|
||||
|
||||
// 同时更新主界面的供应商统计数据
|
||||
// 同时更新主界面的提供商统计数据
|
||||
if (typeof window.loadProviders === 'function') {
|
||||
await window.loadProviders();
|
||||
}
|
||||
|
|
@ -640,8 +640,8 @@ async function refreshProviderConfig(providerType) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 显示添加供应商表单
|
||||
* @param {string} providerType - 供应商类型
|
||||
* 显示添加提供商表单
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
function showAddProviderForm(providerType) {
|
||||
const modal = document.querySelector('.provider-modal');
|
||||
|
|
@ -655,7 +655,7 @@ function showAddProviderForm(providerType) {
|
|||
const form = document.createElement('div');
|
||||
form.className = 'add-provider-form';
|
||||
form.innerHTML = `
|
||||
<h4><i class="fas fa-plus"></i> 添加新供应商配置</h4>
|
||||
<h4><i class="fas fa-plus"></i> 添加新提供商配置</h4>
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label>检查模型名称 <span class="optional-mark">(选填)</span></label>
|
||||
|
|
@ -696,7 +696,7 @@ function showAddProviderForm(providerType) {
|
|||
/**
|
||||
* 添加动态配置字段
|
||||
* @param {HTMLElement} form - 表单元素
|
||||
* @param {string} providerType - 供应商类型
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
function addDynamicConfigFields(form, providerType) {
|
||||
const configFields = form.querySelector('#dynamicConfigFields');
|
||||
|
|
@ -802,7 +802,7 @@ function addDynamicConfigFields(form, providerType) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 为添加新供应商表单中的密码切换按钮绑定事件监听器
|
||||
* 为添加新提供商表单中的密码切换按钮绑定事件监听器
|
||||
* @param {HTMLElement} form - 表单元素
|
||||
*/
|
||||
function bindAddFormPasswordToggleListeners(form) {
|
||||
|
|
@ -827,8 +827,8 @@ function bindAddFormPasswordToggleListeners(form) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 添加新供应商
|
||||
* @param {string} providerType - 供应商类型
|
||||
* 添加新提供商
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
async function addProvider(providerType) {
|
||||
const checkModelName = document.getElementById('newCheckModelName')?.value;
|
||||
|
|
@ -870,7 +870,7 @@ async function addProvider(providerType) {
|
|||
providerType,
|
||||
providerConfig
|
||||
});
|
||||
showToast('供应商配置添加成功', 'success');
|
||||
showToast('提供商配置添加成功', 'success');
|
||||
// 移除添加表单
|
||||
const form = document.querySelector('.add-provider-form');
|
||||
if (form) {
|
||||
|
|
@ -885,8 +885,8 @@ async function addProvider(providerType) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 切换供应商禁用/启用状态
|
||||
* @param {string} uuid - 供应商UUID
|
||||
* 切换提供商禁用/启用状态
|
||||
* @param {string} uuid - 提供商UUID
|
||||
* @param {Event} event - 事件对象
|
||||
*/
|
||||
async function toggleProviderStatus(uuid, event) {
|
||||
|
|
@ -896,12 +896,12 @@ async function toggleProviderStatus(uuid, event) {
|
|||
const providerType = providerDetail.closest('.provider-modal').getAttribute('data-provider-type');
|
||||
const currentProvider = providerDetail.closest('.provider-modal').querySelector(`[data-uuid="${uuid}"]`);
|
||||
|
||||
// 获取当前供应商信息
|
||||
// 获取当前提供商信息
|
||||
const isCurrentlyDisabled = currentProvider.classList.contains('disabled');
|
||||
const action = isCurrentlyDisabled ? 'enable' : 'disable';
|
||||
const confirmMessage = isCurrentlyDisabled ?
|
||||
`确定要启用这个供应商配置吗?` :
|
||||
`确定要禁用这个供应商配置吗?禁用后该供应商将不会被选中使用。`;
|
||||
`确定要启用这个提供商配置吗?` :
|
||||
`确定要禁用这个提供商配置吗?禁用后该提供商将不会被选中使用。`;
|
||||
|
||||
if (!confirm(confirmMessage)) {
|
||||
return;
|
||||
|
|
@ -909,8 +909,8 @@ async function toggleProviderStatus(uuid, event) {
|
|||
|
||||
try {
|
||||
await window.apiClient.post(`/providers/${encodeURIComponent(providerType)}/${uuid}/${action}`, { action });
|
||||
showToast(`供应商${isCurrentlyDisabled ? '启用' : '禁用'}成功`, 'success');
|
||||
// 重新获取该供应商类型的最新配置
|
||||
showToast(`提供商${isCurrentlyDisabled ? '启用' : '禁用'}成功`, 'success');
|
||||
// 重新获取该提供商类型的最新配置
|
||||
await refreshProviderConfig(providerType);
|
||||
} catch (error) {
|
||||
console.error('Failed to toggle provider status:', error);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// 供应商管理功能模块
|
||||
// 提供商管理功能模块
|
||||
|
||||
import { providerStats, updateProviderStats } from './constants.js';
|
||||
import { showToast } from './utils.js';
|
||||
|
|
@ -96,7 +96,7 @@ function renderProviders(providers) {
|
|||
|
||||
container.innerHTML = '';
|
||||
|
||||
// 检查是否有供应商池数据
|
||||
// 检查是否有提供商池数据
|
||||
const hasProviders = Object.keys(providers).length > 0;
|
||||
const statsGrid = document.querySelector('#providers .stats-grid');
|
||||
|
||||
|
|
@ -114,10 +114,10 @@ function renderProviders(providers) {
|
|||
];
|
||||
|
||||
// 获取所有提供商类型并按指定顺序排序
|
||||
// 优先显示预定义的所有供应商类型,即使某些供应商没有数据也要显示
|
||||
// 优先显示预定义的所有提供商类型,即使某些提供商没有数据也要显示
|
||||
let allProviderTypes;
|
||||
if (hasProviders) {
|
||||
// 合并预定义类型和实际存在的类型,确保显示所有预定义供应商
|
||||
// 合并预定义类型和实际存在的类型,确保显示所有预定义提供商
|
||||
const actualProviderTypes = Object.keys(providers);
|
||||
allProviderTypes = [...new Set([...providerDisplayOrder, ...actualProviderTypes])];
|
||||
} else {
|
||||
|
|
@ -205,7 +205,7 @@ function renderProviders(providers) {
|
|||
providerDiv.classList.add('empty-provider');
|
||||
}
|
||||
|
||||
// 添加点击事件 - 整个供应商组都可以点击
|
||||
// 添加点击事件 - 整个提供商组都可以点击
|
||||
providerDiv.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
openProviderManager(providerType);
|
||||
|
|
@ -286,7 +286,7 @@ function updateProviderStatsDisplay(activeProviders, healthyProviders, totalAcco
|
|||
}
|
||||
|
||||
/**
|
||||
* 打开供应商管理模态框
|
||||
* 打开提供商管理模态框
|
||||
* @param {string} providerType - 提供商类型
|
||||
*/
|
||||
async function openProviderManager(providerType) {
|
||||
|
|
@ -296,7 +296,7 @@ async function openProviderManager(providerType) {
|
|||
showProviderManagerModal(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to load provider details:', error);
|
||||
showToast('加载供应商详情失败', 'error');
|
||||
showToast('加载提供商详情失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ textarea.form-control {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 供应商配置组 */
|
||||
/* 提供商配置组 */
|
||||
.provider-config {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
|
|
@ -1191,7 +1191,7 @@ input:checked + .toggle-slider:before {
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 供应商类型显示 */
|
||||
/* 提供商类型显示 */
|
||||
.provider-type-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
|
|
@ -2288,7 +2288,7 @@ input:checked + .toggle-slider:before {
|
|||
background: var(--success-color);
|
||||
}
|
||||
|
||||
/* 供应商池关联的特殊样式 */
|
||||
/* 提供商池关联的特殊样式 */
|
||||
.usage-detail-item[data-usage-type="provider_pool"] {
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #ffffff 100%);
|
||||
border-color: #0ea5e9;
|
||||
|
|
@ -2700,7 +2700,7 @@ input:checked + .toggle-slider:before {
|
|||
}
|
||||
}
|
||||
|
||||
/* 禁用供应商状态样式 */
|
||||
/* 禁用提供商状态样式 */
|
||||
.provider-item-detail.disabled {
|
||||
opacity: 0.6;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
|
|
@ -2776,7 +2776,7 @@ input:checked + .toggle-slider:before {
|
|||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 供应商状态指示器 */
|
||||
/* 提供商状态指示器 */
|
||||
.provider-status .disabled-indicator {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ function generateUsageInfoHtml(config) {
|
|||
|
||||
const typeLabels = {
|
||||
'main_config': '主要配置',
|
||||
'provider_pool': '供应商池',
|
||||
'provider_pool': '提供商池',
|
||||
'multiple': '多种用途'
|
||||
};
|
||||
|
||||
|
|
@ -567,7 +567,7 @@ function showDeleteConfirmModal(config) {
|
|||
<ul>
|
||||
<li>相关的AI服务无法正常工作</li>
|
||||
<li>配置管理中的设置失效</li>
|
||||
<li>供应商池配置丢失</li>
|
||||
<li>提供商池配置丢失</li>
|
||||
</ul>
|
||||
<p><strong>建议:</strong>请先在配置管理中解除文件引用后再删除。</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
<a href="#config" class="nav-item" data-section="config" aria-label="配置管理">
|
||||
<i class="fas fa-cog" aria-hidden="true"></i> <span>配置管理</span>
|
||||
</a>
|
||||
<a href="#providers" class="nav-item" data-section="providers" aria-label="供应商池管理">
|
||||
<i class="fas fa-network-wired" aria-hidden="true"></i> <span>供应商池管理</span>
|
||||
<a href="#providers" class="nav-item" data-section="providers" aria-label="提供商池管理">
|
||||
<i class="fas fa-network-wired" aria-hidden="true"></i> <span>提供商池管理</span>
|
||||
</a>
|
||||
<a href="#upload-config" class="nav-item" data-section="upload-config" aria-label="上传配置管理">
|
||||
<i class="fas fa-upload" aria-hidden="true"></i> <span>上传配置管理</span>
|
||||
|
|
@ -602,9 +602,9 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group pool-section">
|
||||
<label for="providerPoolsFilePath">供应商池配置文件路径</label>
|
||||
<label for="providerPoolsFilePath">提供商池配置文件路径</label>
|
||||
<input type="text" id="providerPoolsFilePath" class="form-control" value="" placeholder="例如: provider_pools.json">
|
||||
<small class="form-text">配置了供应商池后,可在供应商池管理中查看详细信息</small>
|
||||
<small class="form-text">配置了提供商池后,可在提供商池管理中查看详细信息</small>
|
||||
</div>
|
||||
|
||||
<!-- 系统提示配置移到最下面 -->
|
||||
|
|
@ -680,7 +680,7 @@
|
|||
|
||||
<!-- Provider Pools Section -->
|
||||
<section id="providers" class="section" aria-labelledby="providers-title">
|
||||
<h2 id="providers-title">供应商池管理</h2>
|
||||
<h2 id="providers-title">提供商池管理</h2>
|
||||
<!-- Provider Pool Stats -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
|
|
@ -764,7 +764,38 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新路径路由示例中的URL前缀
|
||||
updateRoutingExamplesURLs();
|
||||
})();
|
||||
|
||||
// 更新路径路由示例中的URL前缀
|
||||
function updateRoutingExamplesURLs() {
|
||||
// 获取当前页面的基础URL(去掉index.html)
|
||||
const currentURL = window.location.href;
|
||||
const baseURL = currentURL.replace(/\/index\.html$/, '');
|
||||
|
||||
// 更新所有端点路径
|
||||
const endpointPaths = document.querySelectorAll('.endpoint-path');
|
||||
endpointPaths.forEach(element => {
|
||||
const originalPath = element.textContent;
|
||||
if (!originalPath.startsWith(baseURL)) {
|
||||
// 确保baseURL不以斜杠结尾,然后正确拼接路径
|
||||
const cleanBaseURL = baseURL.replace(/\/$/, '');
|
||||
const cleanPath = originalPath.startsWith('/') ? originalPath : '/' + originalPath;
|
||||
element.textContent = cleanBaseURL + cleanPath;
|
||||
}
|
||||
});
|
||||
|
||||
// 更新curl命令中的URL
|
||||
const curlCodes = document.querySelectorAll('.usage-example pre code');
|
||||
curlCodes.forEach(element => {
|
||||
const curlCommand = element.textContent;
|
||||
// 替换curl命令中的http://localhost:3000部分
|
||||
const updatedCommand = curlCommand.replace(/curl http:\/\/localhost:3000/g, `curl ${baseURL}`);
|
||||
element.textContent = updatedCommand;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="app/app.js"></script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Reference in a new issue