Upload 70 files
Browse files- config.py +34 -6
- core/__pycache__/update.cpython-313.pyc +0 -0
- core/api/__pycache__/xai.cpython-313.pyc +0 -0
- core/api/xai.py +69 -0
- core/update.py +3 -2
- models/__pycache__/api_key.cpython-313.pyc +0 -0
- routes/__pycache__/api.cpython-313.pyc +0 -0
- routes/__pycache__/web.cpython-313.pyc +0 -0
- routes/web.py +42 -1
- static/img/groq.svg +1 -0
- static/img/openrouter.svg +1 -0
- static/img/siliconflow.svg +1 -0
- static/img/xai.svg +1 -0
- static/js/api-key-manager/core.js +9 -7
- static/js/api-key-manager/main-manager.js +20 -1
- static/js/api-key-manager/platform-utils.js +3 -2
- utils/__pycache__/db.cpython-313.pyc +0 -0
config.py
CHANGED
@@ -28,7 +28,11 @@ PLATFORMS = [
|
|
28 |
{"id": "anthropic", "name": "Anthropic"},
|
29 |
{"id": "openai", "name": "OpenAI"},
|
30 |
{"id": "google", "name": "Google"},
|
31 |
-
{"id": "deepseek", "name": "Deepseek"}
|
|
|
|
|
|
|
|
|
32 |
]
|
33 |
|
34 |
# 平台标签样式
|
@@ -52,16 +56,40 @@ PLATFORM_STYLES = {
|
|
52 |
"background-color": "rgba(77, 107, 254, 0.1)",
|
53 |
"border-color": "rgba(77, 107, 254, 0.3)",
|
54 |
"color": "#3246d3"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
}
|
57 |
|
58 |
# API调用节流控制配置
|
59 |
PLATFORM_LIMITS = {
|
60 |
-
'openai':
|
61 |
-
'anthropic':
|
62 |
-
'google':
|
63 |
-
'deepseek': 50,
|
64 |
-
'
|
|
|
|
|
|
|
|
|
65 |
}
|
66 |
|
67 |
# 请求节流时间窗口(秒)
|
|
|
28 |
{"id": "anthropic", "name": "Anthropic"},
|
29 |
{"id": "openai", "name": "OpenAI"},
|
30 |
{"id": "google", "name": "Google"},
|
31 |
+
{"id": "deepseek", "name": "Deepseek"},
|
32 |
+
{"id": "siliconflow", "name": "SiliconFlow"},
|
33 |
+
{"id": "xai", "name": "xAI"},
|
34 |
+
{"id": "groq", "name": "Groq"},
|
35 |
+
{"id": "openrouter", "name": "OpenRouter"}
|
36 |
]
|
37 |
|
38 |
# 平台标签样式
|
|
|
56 |
"background-color": "rgba(77, 107, 254, 0.1)",
|
57 |
"border-color": "rgba(77, 107, 254, 0.3)",
|
58 |
"color": "#3246d3"
|
59 |
+
},
|
60 |
+
"siliconflow": {
|
61 |
+
"background-color": "rgba(124, 58, 237, 0.1)",
|
62 |
+
"border-color": "rgba(124, 58, 237, 0.3)",
|
63 |
+
"color": "#6429c8"
|
64 |
+
},
|
65 |
+
"xai": {
|
66 |
+
"background-color": "rgba(90, 90, 90, 0.1)",
|
67 |
+
"border-color": "rgba(90, 90, 90, 0.3)",
|
68 |
+
"color": "#5A5A5A"
|
69 |
+
},
|
70 |
+
"groq": {
|
71 |
+
"background-color": "rgba(255, 95, 31, 0.1)",
|
72 |
+
"border-color": "rgba(255, 95, 31, 0.3)",
|
73 |
+
"color": "#FF5F1F"
|
74 |
+
},
|
75 |
+
"openrouter": {
|
76 |
+
"background-color": "rgba(61, 88, 171, 0.1)",
|
77 |
+
"border-color": "rgba(61, 88, 171, 0.3)",
|
78 |
+
"color": "#3D58AB"
|
79 |
}
|
80 |
}
|
81 |
|
82 |
# API调用节流控制配置
|
83 |
PLATFORM_LIMITS = {
|
84 |
+
'openai': 100,
|
85 |
+
'anthropic': 100,
|
86 |
+
'google': 500,
|
87 |
+
'deepseek': 50,
|
88 |
+
'siliconflow': 50,
|
89 |
+
'xai': 50,
|
90 |
+
'groq': 50,
|
91 |
+
'openrouter': 50,
|
92 |
+
'default': 50
|
93 |
}
|
94 |
|
95 |
# 请求节流时间窗口(秒)
|
core/__pycache__/update.cpython-313.pyc
CHANGED
Binary files a/core/__pycache__/update.cpython-313.pyc and b/core/__pycache__/update.cpython-313.pyc differ
|
|
core/api/__pycache__/xai.cpython-313.pyc
ADDED
Binary file (2.18 kB). View file
|
|
core/api/xai.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
xAI API相关功能模块 - 提供xAI API的调用和验证功能
|
3 |
+
"""
|
4 |
+
import requests
|
5 |
+
|
6 |
+
def validate_api_key(api_key):
|
7 |
+
"""
|
8 |
+
验证xAI API密钥是否有效
|
9 |
+
|
10 |
+
Args:
|
11 |
+
api_key (str): 需要验证的xAI API密钥
|
12 |
+
|
13 |
+
Returns:
|
14 |
+
dict: 包含验证结果的字典,包括:
|
15 |
+
- success (bool): API密钥验证成功为True,失败为False
|
16 |
+
- return_message (str): 验证响应的具体消息,成功时为"200: Success!",失败时为错误详情
|
17 |
+
- balance (float): 账户余额,固定为0
|
18 |
+
- states (str): 账户状态,xAI没有Tier等级,默认为空字符串
|
19 |
+
"""
|
20 |
+
url = "https://api.x.ai/v1/chat/completions"
|
21 |
+
|
22 |
+
headers = {
|
23 |
+
"Authorization": f"Bearer {api_key}",
|
24 |
+
"Content-Type": "application/json"
|
25 |
+
}
|
26 |
+
|
27 |
+
payload = {
|
28 |
+
"model": "grok-2-1212",
|
29 |
+
"max_tokens": 1,
|
30 |
+
"messages": [
|
31 |
+
{"role": "user", "content": "Hello"}
|
32 |
+
]
|
33 |
+
}
|
34 |
+
|
35 |
+
try:
|
36 |
+
response = requests.post(url, headers=headers, json=payload)
|
37 |
+
|
38 |
+
if response.status_code == 200:
|
39 |
+
return {
|
40 |
+
"success": True,
|
41 |
+
"return_message": "200: Success!",
|
42 |
+
"balance": 0,
|
43 |
+
"states": ""
|
44 |
+
}
|
45 |
+
else:
|
46 |
+
return_message = ""
|
47 |
+
try:
|
48 |
+
error_data = response.json()
|
49 |
+
if "error" in error_data:
|
50 |
+
return_message = error_data["error"].get("message", "")
|
51 |
+
return_message = f"{response.status_code}: {return_message}"
|
52 |
+
except:
|
53 |
+
return_message = f"{response.status_code}: {response.text or f'HTTP错误'}"
|
54 |
+
|
55 |
+
return {
|
56 |
+
"success": False,
|
57 |
+
"return_message": return_message,
|
58 |
+
"balance": 0,
|
59 |
+
"states": ""
|
60 |
+
}
|
61 |
+
except Exception as e:
|
62 |
+
error_str = str(e)
|
63 |
+
print(f"验证API密钥时出错: {error_str}")
|
64 |
+
return {
|
65 |
+
"success": False,
|
66 |
+
"return_message": f"500: 请求异常: {error_str}",
|
67 |
+
"balance": 0,
|
68 |
+
"states": ""
|
69 |
+
}
|
core/update.py
CHANGED
@@ -4,6 +4,7 @@ API密钥更新模块 - 提供API密钥的验证和更新功能
|
|
4 |
import json
|
5 |
import os
|
6 |
import sqlite3
|
|
|
7 |
from datetime import datetime
|
8 |
from core.api_manager import get_api_manager
|
9 |
from utils.db import get_db_connection
|
@@ -60,8 +61,8 @@ def update(key_id):
|
|
60 |
except Exception as e:
|
61 |
return {"success": False, "message": f"验证API密钥时出错: {str(e)}"}
|
62 |
|
63 |
-
# 当前时间
|
64 |
-
current_time = datetime.now().isoformat()
|
65 |
|
66 |
# 将布尔值转换为整数
|
67 |
success_int = 1 if result.get("success", False) else 0
|
|
|
4 |
import json
|
5 |
import os
|
6 |
import sqlite3
|
7 |
+
import pytz
|
8 |
from datetime import datetime
|
9 |
from core.api_manager import get_api_manager
|
10 |
from utils.db import get_db_connection
|
|
|
61 |
except Exception as e:
|
62 |
return {"success": False, "message": f"验证API密钥时出错: {str(e)}"}
|
63 |
|
64 |
+
# 当前时间 - 使用亚洲/上海时区 (UTC+8)
|
65 |
+
current_time = datetime.now(pytz.timezone('Asia/Shanghai')).isoformat()
|
66 |
|
67 |
# 将布尔值转换为整数
|
68 |
success_int = 1 if result.get("success", False) else 0
|
models/__pycache__/api_key.cpython-313.pyc
CHANGED
Binary files a/models/__pycache__/api_key.cpython-313.pyc and b/models/__pycache__/api_key.cpython-313.pyc differ
|
|
routes/__pycache__/api.cpython-313.pyc
CHANGED
Binary files a/routes/__pycache__/api.cpython-313.pyc and b/routes/__pycache__/api.cpython-313.pyc differ
|
|
routes/__pycache__/web.cpython-313.pyc
CHANGED
Binary files a/routes/__pycache__/web.cpython-313.pyc and b/routes/__pycache__/web.cpython-313.pyc differ
|
|
routes/web.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
"""
|
2 |
Web路由模块 - 处理所有页面请求和身份验证
|
3 |
"""
|
|
|
4 |
from flask import Blueprint, render_template, request, redirect, url_for, make_response, session
|
5 |
from datetime import datetime
|
6 |
from config import ADMIN_PASSWORD, PLATFORMS, PLATFORM_STYLES, TOKEN_EXPIRY_DAYS
|
@@ -10,10 +11,50 @@ from models.api_key import ApiKeyManager
|
|
10 |
# 创建Web蓝图
|
11 |
web_bp = Blueprint('web', __name__)
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
@web_bp.route('/')
|
14 |
def index():
|
15 |
"""首页 - 显示API密钥管理界面"""
|
16 |
api_keys_data = ApiKeyManager.get_all_keys()
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
# 按平台分组密钥
|
19 |
grouped_keys = {}
|
@@ -24,7 +65,7 @@ def index():
|
|
24 |
}
|
25 |
|
26 |
# 将密钥加入对应的平台组
|
27 |
-
for key in
|
28 |
platform_id = key.get("platform", "other").lower()
|
29 |
if platform_id not in grouped_keys:
|
30 |
platform_id = "other"
|
|
|
1 |
"""
|
2 |
Web路由模块 - 处理所有页面请求和身份验证
|
3 |
"""
|
4 |
+
import re # 导入正则表达式模块
|
5 |
from flask import Blueprint, render_template, request, redirect, url_for, make_response, session
|
6 |
from datetime import datetime
|
7 |
from config import ADMIN_PASSWORD, PLATFORMS, PLATFORM_STYLES, TOKEN_EXPIRY_DAYS
|
|
|
11 |
# 创建Web蓝图
|
12 |
web_bp = Blueprint('web', __name__)
|
13 |
|
14 |
+
# 辅助函数:为API密钥生成排序键
|
15 |
+
def get_sort_key(key):
|
16 |
+
"""
|
17 |
+
生成用于排序的元组 (balance, platform_priority)。
|
18 |
+
- balance: 主要排序键,降序。
|
19 |
+
- platform_priority: 次要排序键,仅在 balance 相同时生效,降序。
|
20 |
+
- Google: paid=1, free=0
|
21 |
+
- OpenAI/Claude: t5=5, t4=4, ..., t1=1, 其他=0
|
22 |
+
"""
|
23 |
+
balance = key.get('balance', 0)
|
24 |
+
platform_priority = 0
|
25 |
+
platform = key.get('platform', '').lower()
|
26 |
+
name = key.get('name', '').lower() # 用于 OpenAI/Claude tier 判断
|
27 |
+
states = key.get('states', '').lower() # 获取 states 字段并转小写,用于 Google 判断
|
28 |
+
|
29 |
+
if platform == 'google':
|
30 |
+
if 'paid' in states: # 检查 states 字段是否包含 'paid' (已转小写)
|
31 |
+
platform_priority = 1 # paid 优先
|
32 |
+
elif platform in ['openai', 'anthropic']: # 假设 Claude 的 platform id 是 'anthropic'
|
33 |
+
match = re.search(r't(\d)', name)
|
34 |
+
if match:
|
35 |
+
try:
|
36 |
+
tier = int(match.group(1))
|
37 |
+
if 1 <= tier <= 5: # 假设 tier 在 1-5 之间
|
38 |
+
platform_priority = tier
|
39 |
+
except ValueError:
|
40 |
+
pass # 如果转换失败,保持 priority 为 0
|
41 |
+
|
42 |
+
# 返回元组,Python 的 sorted 会按元组顺序比较
|
43 |
+
# 因为 sorted 默认升序,我们需要对 balance 和 priority 都取反来实现降序效果
|
44 |
+
# 或者直接在 sorted 中用 reverse=True,然后只对 platform_priority 取反(或用正数表示优先级)
|
45 |
+
# 这里我们用 reverse=True,所以 balance 自然降序,platform_priority 越大越优先
|
46 |
+
return (balance, platform_priority)
|
47 |
+
|
48 |
@web_bp.route('/')
|
49 |
def index():
|
50 |
"""首页 - 显示API密钥管理界面"""
|
51 |
api_keys_data = ApiKeyManager.get_all_keys()
|
52 |
+
all_keys_list = api_keys_data.get("api_keys", [])
|
53 |
+
|
54 |
+
# 按 balance 降序排序密钥
|
55 |
+
# 注意:确保 balance 存在且为数字类型,否则排序可能出错。使用 .get('balance', 0) 提供默认值。
|
56 |
+
# 使用新的排序逻辑
|
57 |
+
sorted_keys = sorted(all_keys_list, key=get_sort_key, reverse=True)
|
58 |
|
59 |
# 按平台分组密钥
|
60 |
grouped_keys = {}
|
|
|
65 |
}
|
66 |
|
67 |
# 将密钥加入对应的平台组
|
68 |
+
for key in sorted_keys: # 使用排序后的列表进行分组
|
69 |
platform_id = key.get("platform", "other").lower()
|
70 |
if platform_id not in grouped_keys:
|
71 |
platform_id = "other"
|
static/img/groq.svg
ADDED
|
static/img/openrouter.svg
ADDED
|
static/img/siliconflow.svg
ADDED
|
static/img/xai.svg
ADDED
|
static/js/api-key-manager/core.js
CHANGED
@@ -10,19 +10,21 @@ function initApiKeyManager() {
|
|
10 |
// 初始化平台ID列表
|
11 |
this.platformIds = platforms.map(platform => platform.id);
|
12 |
|
13 |
-
// 从localStorage
|
14 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
const parsedPlatformStates = savedPlatformStates ? JSON.parse(savedPlatformStates) : {};
|
16 |
|
17 |
// 从localStorage读取平台筛选状态,如果有的话
|
18 |
const savedPlatformFilters = localStorage.getItem('platformFilters');
|
19 |
const parsedPlatformFilters = savedPlatformFilters ? JSON.parse(savedPlatformFilters) : {};
|
20 |
|
21 |
-
// 从localStorage读取当前视图状态,如果有的话
|
22 |
-
const savedView = localStorage.getItem('keyView');
|
23 |
-
if (savedView === 'valid' || savedView === 'invalid') {
|
24 |
-
this.currentView = savedView;
|
25 |
-
}
|
26 |
|
27 |
// 初始化平台状态,优先使用保存的状态
|
28 |
platforms.forEach(platform => {
|
|
|
10 |
// 初始化平台ID列表
|
11 |
this.platformIds = platforms.map(platform => platform.id);
|
12 |
|
13 |
+
// 从localStorage读取当前视图状态,如果有的话
|
14 |
+
const savedView = localStorage.getItem('keyView');
|
15 |
+
if (savedView === 'valid' || savedView === 'invalid') {
|
16 |
+
this.currentView = savedView;
|
17 |
+
}
|
18 |
+
|
19 |
+
// 从localStorage读取对应视图的平台展开状态,如果有的话
|
20 |
+
const stateKey = `platformStates_${this.currentView}`;
|
21 |
+
const savedPlatformStates = localStorage.getItem(stateKey);
|
22 |
const parsedPlatformStates = savedPlatformStates ? JSON.parse(savedPlatformStates) : {};
|
23 |
|
24 |
// 从localStorage读取平台筛选状态,如果有的话
|
25 |
const savedPlatformFilters = localStorage.getItem('platformFilters');
|
26 |
const parsedPlatformFilters = savedPlatformFilters ? JSON.parse(savedPlatformFilters) : {};
|
27 |
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
// 初始化平台状态,优先使用保存的状态
|
30 |
platforms.forEach(platform => {
|
static/js/api-key-manager/main-manager.js
CHANGED
@@ -140,10 +140,29 @@ function apiKeyManager() {
|
|
140 |
// 切换当前视图(有效/无效密钥)
|
141 |
switchView(view) {
|
142 |
if (this.currentView !== view) {
|
|
|
|
|
|
|
|
|
|
|
143 |
this.currentView = view;
|
144 |
-
|
|
|
145 |
localStorage.setItem('keyView', view);
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
// 重置选择状态
|
148 |
this.selectedKeys = [];
|
149 |
this.selectedPlatforms = [];
|
|
|
140 |
// 切换当前视图(有效/无效密钥)
|
141 |
switchView(view) {
|
142 |
if (this.currentView !== view) {
|
143 |
+
// 保存当前视图的平台展开状态
|
144 |
+
const currentStateKey = `platformStates_${this.currentView}`;
|
145 |
+
localStorage.setItem(currentStateKey, JSON.stringify(this.platformStates));
|
146 |
+
|
147 |
+
// 切换视图
|
148 |
this.currentView = view;
|
149 |
+
|
150 |
+
// 保存用户的视图选择到localStorage
|
151 |
localStorage.setItem('keyView', view);
|
152 |
|
153 |
+
// 加载新视图的平台展开状态
|
154 |
+
const newStateKey = `platformStates_${view}`;
|
155 |
+
const savedPlatformStates = localStorage.getItem(newStateKey);
|
156 |
+
if (savedPlatformStates) {
|
157 |
+
this.platformStates = JSON.parse(savedPlatformStates);
|
158 |
+
} else {
|
159 |
+
// 如果没有保存过这个视图的状态,使用默认值(全部展开)
|
160 |
+
const platforms = this.getPlatforms();
|
161 |
+
platforms.forEach(platform => {
|
162 |
+
this.platformStates[platform.id] = true;
|
163 |
+
});
|
164 |
+
}
|
165 |
+
|
166 |
// 重置选择状态
|
167 |
this.selectedKeys = [];
|
168 |
this.selectedPlatforms = [];
|
static/js/api-key-manager/platform-utils.js
CHANGED
@@ -7,8 +7,9 @@
|
|
7 |
function togglePlatform(platformId) {
|
8 |
this.platformStates[platformId] = !this.platformStates[platformId];
|
9 |
|
10 |
-
// 保存展开状态到localStorage
|
11 |
-
|
|
|
12 |
}
|
13 |
|
14 |
// 获取平台API密钥数量
|
|
|
7 |
function togglePlatform(platformId) {
|
8 |
this.platformStates[platformId] = !this.platformStates[platformId];
|
9 |
|
10 |
+
// 保存展开状态到localStorage - 分开存储不同视图的状态
|
11 |
+
const stateKey = `platformStates_${this.currentView}`;
|
12 |
+
localStorage.setItem(stateKey, JSON.stringify(this.platformStates));
|
13 |
}
|
14 |
|
15 |
// 获取平台API密钥数量
|
utils/__pycache__/db.cpython-313.pyc
CHANGED
Binary files a/utils/__pycache__/db.cpython-313.pyc and b/utils/__pycache__/db.cpython-313.pyc differ
|
|