feat: 实现 OpenAI 兼容 API 端点和批量代理删除

新增与 OpenAI 规范兼容的 API 端点:
- `/openai/v1/models`
- `/openai/v1/chat/completions` (支持流式传输、重试和密钥切换)
- `/openai/v1/embeddings`
- `/openai/v1/images/generations`

包含:
- 在 `app/router/openai_compatiable_routes.py` 中新增路由。
- `OpenAICompatiableService` 用于处理请求逻辑、日志记录和错误管理。
- 更新 `OpenaiApiClient` 以支持新方法和代理使用。
- 修改 `app/domain/openai_models.py` 以实现兼容性。
- 为新 API 添加专用日志记录器 (`openai_compatible`)。
- 为新路由 (`/openai`, `/api/version/check`) 添加认证中间件豁免。

增强配置编辑器 UI:
- 在 `app/static/js/config_editor.js` 和 `app/templates/config_editor.html` 中添加批量代理删除功能。
This commit is contained in:
snaily
2025-04-30 20:39:47 +08:00
parent 7da9110704
commit 83ce50975a
9 changed files with 562 additions and 22 deletions
+95 -5
View File
@@ -71,6 +71,12 @@ document.addEventListener('DOMContentLoaded', function() {
const cancelAddProxyBtn = document.getElementById('cancelAddProxyBtn');
const confirmAddProxyBtn = document.getElementById('confirmAddProxyBtn');
const proxyBulkInput = document.getElementById('proxyBulkInput');
const bulkDeleteProxyBtn = document.getElementById('bulkDeleteProxyBtn'); // 新增
const bulkDeleteProxyModal = document.getElementById('bulkDeleteProxyModal'); // 新增
const closeBulkDeleteProxyModalBtn = document.getElementById('closeBulkDeleteProxyModalBtn'); // 新增
const cancelBulkDeleteProxyBtn = document.getElementById('cancelBulkDeleteProxyBtn'); // 新增
const confirmBulkDeleteProxyBtn = document.getElementById('confirmBulkDeleteProxyBtn'); // 新增
const bulkDeleteProxyInput = document.getElementById('bulkDeleteProxyInput'); // 新增
// --- 结束:Proxy 模态框相关 ---
// --- 新增:重置确认模态框相关 ---
@@ -120,9 +126,12 @@ document.addEventListener('DOMContentLoaded', function() {
if (event.target == bulkDeleteApiKeyModal) { // 新增对批量删除模态框的处理
bulkDeleteApiKeyModal.classList.remove('show');
}
if (event.target == proxyModal) { // 新增对代理模态框的处理
if (event.target == proxyModal) { // 新增对代理模态框的处理
proxyModal.classList.remove('show');
}
if (event.target == bulkDeleteProxyModal) { // 新增对批量删除代理模态框的处理
bulkDeleteProxyModal.classList.remove('show');
}
});
// 确认添加 API Key
@@ -205,6 +214,41 @@ document.addEventListener('DOMContentLoaded', function() {
confirmAddProxyBtn.addEventListener('click', handleBulkAddProxies);
}
// --- 结束:Proxy 模态框事件 ---
// --- 新增:批量删除 Proxy 相关事件 ---
// 打开批量删除模态框
if (bulkDeleteProxyBtn) {
bulkDeleteProxyBtn.addEventListener('click', () => {
if (bulkDeleteProxyModal) {
bulkDeleteProxyModal.classList.add('show');
}
if (bulkDeleteProxyInput) bulkDeleteProxyInput.value = ''; // 清空输入框
});
}
// 关闭批量删除模态框 (X 按钮)
if (closeBulkDeleteProxyModalBtn) {
closeBulkDeleteProxyModalBtn.addEventListener('click', () => {
if (bulkDeleteProxyModal) {
bulkDeleteProxyModal.classList.remove('show');
}
});
}
// 关闭批量删除模态框 (取消按钮)
if (cancelBulkDeleteProxyBtn) {
cancelBulkDeleteProxyBtn.addEventListener('click', () => {
if (bulkDeleteProxyModal) {
bulkDeleteProxyModal.classList.remove('show');
}
});
}
// 确认批量删除 Proxy
if (confirmBulkDeleteProxyBtn) {
confirmBulkDeleteProxyBtn.addEventListener('click', handleBulkDeleteProxies);
}
// --- 结束:批量删除 Proxy 相关 ---
// --- 新增:重置确认模态框事件监听 (移到 DOMContentLoaded 内部) ---
if (closeResetModalBtn) {
@@ -609,6 +653,56 @@ function handleBulkAddProxies() {
}
// --- 结束:处理批量添加 Proxy 的逻辑 ---
// --- 新增:处理批量删除 Proxy 的逻辑 ---
function handleBulkDeleteProxies() {
const bulkDeleteTextarea = document.getElementById('bulkDeleteProxyInput');
const proxyContainer = document.getElementById('PROXIES_container');
const bulkDeleteModal = document.getElementById('bulkDeleteProxyModal');
if (!bulkDeleteTextarea || !proxyContainer || !bulkDeleteModal) return;
const bulkText = bulkDeleteTextarea.value;
if (!bulkText.trim()) {
showNotification('请粘贴需要删除的代理地址', 'warning');
return;
}
// 使用与添加时相同的正则表达式来提取要删除的代理
const proxyRegex = /(?:https?|socks5):\/\/(?:[^:@\/]+(?::[^@\/]+)?@)?(?:[^:\/\s]+)(?::\d+)?/g;
const proxiesToDelete = new Set(bulkText.match(proxyRegex) || []); // 使用 Set 进行高效查找
if (proxiesToDelete.size === 0) {
showNotification('未在输入内容中提取到有效的代理地址格式', 'warning');
return;
}
const proxyItems = proxyContainer.querySelectorAll('.array-item');
let deleteCount = 0;
proxyItems.forEach(item => {
const input = item.querySelector('.array-input');
// 检查输入框是否存在及其值是否在要删除的集合中
if (input && proxiesToDelete.has(input.value)) {
item.remove(); // 删除整个数组项元素
deleteCount++;
}
});
// 关闭模态框
bulkDeleteModal.classList.remove('show');
// 提供反馈
if (deleteCount > 0) {
showNotification(`成功删除了 ${deleteCount} 个匹配的代理`, 'success');
} else {
showNotification('列表中未找到您输入的任何代理进行删除', 'info');
}
// 处理后清空文本区域
bulkDeleteTextarea.value = '';
}
// --- 结束:处理批量删除 Proxy 的逻辑 ---
// 切换标签
function switchTab(tabId) {
// 更新标签按钮状态
@@ -1063,10 +1157,6 @@ function generateRandomToken() {
}
// --- 结束:生成随机令牌函数 ---
// --- 修改:添加思考模型预算映射项 (现在由添加思考模型触发) ---
// function addBudgetMapItem() {
// // 不再需要手动添加
// }
// Deprecated: This function is now effectively replaced by createAndAppendBudgetMapItem
// for the initial population logic. It delegates to the new function if called.