mirror of
https://github.com/Zippland/Snap-Solver.git
synced 2026-01-19 01:21:13 +08:00
移除API密钥验证相关的代码和样式,删除无用的图片文件,优化进度指示器的样式和动画效果,调整设置面板的布局,简化API密钥管理功能,提升用户体验。
This commit is contained in:
151
app.py
151
app.py
@@ -679,157 +679,6 @@ def update_api_keys():
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "message": f"更新API密钥错误: {str(e)}"}), 500
|
||||
|
||||
# 验证API密钥
|
||||
@app.route('/api/keys/validate', methods=['POST'])
|
||||
def validate_api_key():
|
||||
"""验证API密钥有效性"""
|
||||
try:
|
||||
validate_data = request.json
|
||||
if not isinstance(validate_data, dict) or 'key_type' not in validate_data or 'key_value' not in validate_data:
|
||||
return jsonify({"success": False, "message": "无效的请求格式"}), 400
|
||||
|
||||
key_type = validate_data['key_type']
|
||||
key_value = validate_data['key_value']
|
||||
|
||||
if not key_value or key_value.strip() == "":
|
||||
return jsonify({"success": False, "message": f"未提供{key_type}密钥"}), 400
|
||||
|
||||
# 基于密钥类型进行验证
|
||||
result = {"success": False, "message": "不支持的密钥类型"}
|
||||
|
||||
if key_type == "AnthropicApiKey":
|
||||
result = validate_anthropic_key(key_value)
|
||||
elif key_type == "OpenaiApiKey":
|
||||
result = validate_openai_key(key_value)
|
||||
elif key_type == "DeepseekApiKey":
|
||||
result = validate_deepseek_key(key_value)
|
||||
elif key_type == "AlibabaApiKey":
|
||||
result = validate_alibaba_key(key_value)
|
||||
elif key_type == "MathpixAppId" or key_type == "MathpixAppKey":
|
||||
# Mathpix需要两个密钥一起验证
|
||||
mathpix_app_id = key_value if key_type == "MathpixAppId" else get_api_key("MathpixAppId")
|
||||
mathpix_app_key = key_value if key_type == "MathpixAppKey" else get_api_key("MathpixAppKey")
|
||||
if mathpix_app_id and mathpix_app_key:
|
||||
result = validate_mathpix_key(f"{mathpix_app_id}:{mathpix_app_key}")
|
||||
else:
|
||||
result = {"success": False, "message": "需要同时设置Mathpix App ID和App Key"}
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "message": f"验证API密钥时出错: {str(e)}"}), 500
|
||||
|
||||
def validate_anthropic_key(api_key):
|
||||
"""验证Anthropic API密钥"""
|
||||
try:
|
||||
import anthropic
|
||||
client = anthropic.Anthropic(api_key=api_key)
|
||||
# 发送一个简单请求来验证密钥
|
||||
response = client.messages.create(
|
||||
model="claude-3-haiku-20240307",
|
||||
max_tokens=10,
|
||||
messages=[{"role": "user", "content": "Hello"}]
|
||||
)
|
||||
return {"success": True, "message": "Anthropic API密钥有效"}
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
if "401" in error_message or "unauthorized" in error_message.lower():
|
||||
return {"success": False, "message": "Anthropic API密钥无效或已过期"}
|
||||
return {"success": False, "message": f"验证Anthropic API密钥时出错: {error_message}"}
|
||||
|
||||
def validate_openai_key(api_key):
|
||||
"""验证OpenAI API密钥"""
|
||||
try:
|
||||
import openai
|
||||
client = openai.OpenAI(api_key=api_key)
|
||||
# 发送一个简单请求来验证密钥
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
max_tokens=10
|
||||
)
|
||||
return {"success": True, "message": "OpenAI API密钥有效"}
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
if "401" in error_message or "invalid" in error_message.lower():
|
||||
return {"success": False, "message": "OpenAI API密钥无效或已过期"}
|
||||
return {"success": False, "message": f"验证OpenAI API密钥时出错: {error_message}"}
|
||||
|
||||
def validate_deepseek_key(api_key):
|
||||
"""验证DeepSeek API密钥"""
|
||||
try:
|
||||
import requests
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {api_key}"
|
||||
}
|
||||
data = {
|
||||
"model": "deepseek-chat",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"max_tokens": 10
|
||||
}
|
||||
response = requests.post(
|
||||
"https://api.deepseek.com/v1/chat/completions",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return {"success": True, "message": "DeepSeek API密钥有效"}
|
||||
else:
|
||||
error_message = response.json().get("error", {}).get("message", "未知错误")
|
||||
return {"success": False, "message": f"DeepSeek API密钥无效: {error_message}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": f"验证DeepSeek API密钥时出错: {str(e)}"}
|
||||
|
||||
def validate_alibaba_key(api_key):
|
||||
"""验证阿里巴巴DashScope API密钥"""
|
||||
try:
|
||||
import dashscope
|
||||
dashscope.api_key = api_key
|
||||
response = dashscope.Generation.call(
|
||||
model='qwen-vl-plus',
|
||||
messages=[{'role': 'user', 'content': 'Hello'}],
|
||||
result_format='message',
|
||||
max_tokens=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return {"success": True, "message": "阿里巴巴API密钥有效"}
|
||||
else:
|
||||
error_message = response.message
|
||||
return {"success": False, "message": f"阿里巴巴API密钥无效: {error_message}"}
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
if "unauthorized" in error_message.lower() or "invalid" in error_message.lower():
|
||||
return {"success": False, "message": "阿里巴巴API密钥无效或已过期"}
|
||||
return {"success": False, "message": f"验证阿里巴巴API密钥时出错: {error_message}"}
|
||||
|
||||
def validate_mathpix_key(api_key):
|
||||
"""验证Mathpix API密钥"""
|
||||
try:
|
||||
import requests
|
||||
# 分解API密钥
|
||||
app_id, app_key = api_key.split(":")
|
||||
|
||||
headers = {
|
||||
"app_id": app_id,
|
||||
"app_key": app_key,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
# 构造一个简单的请求(只检查API密钥,不实际发送图像)
|
||||
response = requests.get(
|
||||
"https://api.mathpix.com/v3/app-setting",
|
||||
headers=headers,
|
||||
timeout=10
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return {"success": True, "message": "Mathpix API密钥有效"}
|
||||
else:
|
||||
error_message = response.json().get("error", "未知错误")
|
||||
return {"success": False, "message": f"Mathpix API密钥无效: {error_message}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": f"验证Mathpix API密钥时出错: {str(e)}"}
|
||||
|
||||
# 加载API密钥配置
|
||||
def load_api_keys():
|
||||
"""从配置文件加载API密钥"""
|
||||
|
||||
@@ -45,7 +45,3 @@ class BaseModel(ABC):
|
||||
def get_model_identifier(self) -> str:
|
||||
"""Return the model identifier used in API calls"""
|
||||
pass
|
||||
|
||||
def validate_api_key(self) -> bool:
|
||||
"""Validate if the API key is in the correct format"""
|
||||
return bool(self.api_key and self.api_key.strip())
|
||||
|
||||
@@ -211,16 +211,6 @@ class MathpixModel(BaseModel):
|
||||
"""
|
||||
return "mathpix"
|
||||
|
||||
def validate_api_key(self) -> bool:
|
||||
"""
|
||||
Validate if the API key is in the correct format (app_id:app_key).
|
||||
"""
|
||||
try:
|
||||
app_id, app_key = self.api_key.split(':')
|
||||
return bool(app_id.strip() and app_key.strip())
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def _format_response(self, result: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Format the Mathpix API response into a readable string.
|
||||
|
||||
@@ -136,51 +136,62 @@ class SnapSolver {
|
||||
}
|
||||
|
||||
updateStatusLight(status) {
|
||||
const statusLight = document.querySelector('.status-light');
|
||||
// 获取进度指示器元素
|
||||
const progressLine = document.querySelector('.progress-line');
|
||||
const statusText = document.querySelector('.status-text');
|
||||
const analysisIndicator = document.querySelector('.analysis-indicator');
|
||||
|
||||
if (!statusLight || !progressLine || !statusText || !analysisIndicator) {
|
||||
if (!progressLine || !statusText || !analysisIndicator) {
|
||||
console.error('状态指示器元素未找到:', {
|
||||
progressLine: !!progressLine,
|
||||
statusText: !!statusText,
|
||||
analysisIndicator: !!analysisIndicator
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保指示器可见
|
||||
analysisIndicator.style.display = 'flex';
|
||||
statusLight.classList.remove('completed', 'error', 'working');
|
||||
|
||||
// 先移除所有可能的状态类
|
||||
analysisIndicator.classList.remove('processing', 'completed', 'error');
|
||||
progressLine.classList.remove('processing', 'completed', 'error');
|
||||
|
||||
switch (status) {
|
||||
case 'started':
|
||||
case 'thinking':
|
||||
case 'reasoning':
|
||||
case 'streaming':
|
||||
statusLight.classList.add('working');
|
||||
progressLine.style.animation = 'progress-animation 2s linear infinite';
|
||||
// 添加处理中状态类
|
||||
analysisIndicator.classList.add('processing');
|
||||
progressLine.classList.add('processing');
|
||||
statusText.textContent = '生成中';
|
||||
break;
|
||||
|
||||
case 'completed':
|
||||
statusLight.classList.add('completed');
|
||||
progressLine.style.animation = 'none';
|
||||
progressLine.style.width = '100%';
|
||||
// 添加完成状态类
|
||||
analysisIndicator.classList.add('completed');
|
||||
progressLine.classList.add('completed');
|
||||
statusText.textContent = '完成';
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
statusLight.classList.add('error');
|
||||
progressLine.style.animation = 'none';
|
||||
progressLine.style.width = '100%';
|
||||
// 添加错误状态类
|
||||
analysisIndicator.classList.add('error');
|
||||
progressLine.classList.add('error');
|
||||
statusText.textContent = '出错';
|
||||
break;
|
||||
|
||||
case 'stopped':
|
||||
statusLight.classList.add('error');
|
||||
progressLine.style.animation = 'none';
|
||||
progressLine.style.width = '100%';
|
||||
// 添加错误状态类(用于停止状态)
|
||||
analysisIndicator.classList.add('error');
|
||||
progressLine.classList.add('error');
|
||||
statusText.textContent = '已停止';
|
||||
break;
|
||||
|
||||
default:
|
||||
analysisIndicator.style.display = 'none';
|
||||
// 对于未知状态,显示准备中
|
||||
statusText.textContent = '准备中';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,45 +230,6 @@ class SettingsManager {
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize API key validate buttons
|
||||
document.querySelectorAll('.validate-api-key').forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
const keyType = e.currentTarget.getAttribute('data-key-type');
|
||||
const input = e.currentTarget.closest('.input-group').querySelector('input');
|
||||
const keyValue = input.value;
|
||||
|
||||
if (keyValue.trim() === '') {
|
||||
window.uiManager?.showToast('请先输入API密钥', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示验证中状态
|
||||
const icon = e.currentTarget.querySelector('i');
|
||||
const originalClass = icon.className;
|
||||
icon.className = 'fas fa-spinner fa-spin';
|
||||
e.currentTarget.disabled = true;
|
||||
|
||||
this.validateApiKey(keyType, keyValue)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
window.uiManager?.showToast(result.message, 'success');
|
||||
// 更新状态显示
|
||||
this.saveSettings();
|
||||
} else {
|
||||
window.uiManager?.showToast(result.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
window.uiManager?.showToast(`验证失败: ${error.message}`, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
// 恢复按钮状态
|
||||
icon.className = originalClass;
|
||||
e.currentTarget.disabled = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 存储API密钥的对象
|
||||
this.apiKeyValues = {
|
||||
'AnthropicApiKey': '',
|
||||
@@ -1005,36 +966,6 @@ class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证API密钥
|
||||
* @param {string} keyType 密钥类型
|
||||
* @param {string} keyValue 密钥值
|
||||
* @returns {Promise<Object>} 验证结果
|
||||
*/
|
||||
async validateApiKey(keyType, keyValue) {
|
||||
try {
|
||||
const response = await fetch('/api/keys/validate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key_type: keyType,
|
||||
key_value: keyValue
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`服务器响应错误: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('验证API密钥时出错:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个Toast提示消息
|
||||
* @param {string} message 提示消息内容
|
||||
|
||||
@@ -745,12 +745,13 @@ body {
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
height: 2px;
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
width: 40px;
|
||||
background-color: var(--border-color);
|
||||
border-radius: 1px;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) inset;
|
||||
}
|
||||
|
||||
.progress-line::before {
|
||||
@@ -759,45 +760,48 @@ body {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
width: 100%;
|
||||
background-color: var(--primary);
|
||||
border-radius: 1px;
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 2px;
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-line.processing::before {
|
||||
animation: progress-animation 2s ease-in-out infinite;
|
||||
animation: pulse-animation 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.progress-line.completed::before {
|
||||
width: 100%;
|
||||
background-color: var(--success);
|
||||
transform: translateX(0);
|
||||
background-color: #4CAF50;
|
||||
transition: transform 0.3s cubic-bezier(0.19, 1, 0.22, 1), background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-line.error::before {
|
||||
width: 100%;
|
||||
background-color: var(--danger);
|
||||
transform: translateX(0);
|
||||
background-color: #F44336;
|
||||
transition: transform 0.3s cubic-bezier(0.19, 1, 0.22, 1), background-color 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes progress-animation {
|
||||
@keyframes pulse-animation {
|
||||
0% {
|
||||
width: 0%;
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
50% {
|
||||
width: 50%;
|
||||
left: 25%;
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
width: 0%;
|
||||
left: 100%;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
transition: color 0.3s ease;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.3px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.analysis-indicator.processing .status-text {
|
||||
@@ -805,11 +809,11 @@ body {
|
||||
}
|
||||
|
||||
.analysis-indicator.completed .status-text {
|
||||
color: var(--success);
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.analysis-indicator.error .status-text {
|
||||
color: var(--danger);
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* Thinking Section */
|
||||
@@ -2454,23 +2458,28 @@ button:disabled {
|
||||
/* 在适当位置添加停止生成按钮样式 */
|
||||
.btn-stop-generation {
|
||||
display: none;
|
||||
background-color: var(--error-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: rgba(244, 67, 54, 0.1);
|
||||
color: #F44336;
|
||||
border: 1px solid rgba(244, 67, 54, 0.2);
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin-left: 10px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-stop-generation:hover {
|
||||
background-color: var(--error-hover-color);
|
||||
transform: scale(1.05);
|
||||
background-color: rgba(244, 67, 54, 0.15);
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-stop-generation:active {
|
||||
@@ -2478,13 +2487,19 @@ button:disabled {
|
||||
}
|
||||
|
||||
.btn-stop-generation i {
|
||||
font-size: 14px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.btn-stop-generation.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-stop-generation {
|
||||
background-color: rgba(248, 81, 73, 0.15);
|
||||
color: #f85149;
|
||||
border-color: rgba(248, 81, 73, 0.3);
|
||||
}
|
||||
|
||||
/* API密钥状态高亮 */
|
||||
.api-key-status.highlight {
|
||||
background-color: var(--hover-bg);
|
||||
@@ -2565,15 +2580,6 @@ textarea:focus {
|
||||
background-color: rgba(var(--primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
.input-group .validate-api-key {
|
||||
color: var(--text-success);
|
||||
}
|
||||
|
||||
.input-group .validate-api-key:hover {
|
||||
color: var(--text-success);
|
||||
background-color: rgba(40, 167, 69, 0.1);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -344,16 +344,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置面板底部按钮 -->
|
||||
<div class="settings-footer">
|
||||
<button id="resetSettings" class="btn btn-secondary">
|
||||
<i class="fas fa-undo"></i> 重置设置
|
||||
</button>
|
||||
<button id="saveSettings" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> 保存设置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user