mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-09-05 15:36:45 +08:00
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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user