移除API密钥验证相关的代码和样式,删除无用的图片文件,优化进度指示器的样式和动画效果,调整设置面板的布局,简化API密钥管理功能,提升用户体验。

This commit is contained in:
Zylan
2025-04-02 23:00:14 +08:00
parent fd53f18ec9
commit e6445c61fd
8 changed files with 73 additions and 300 deletions

151
app.py
View File

@@ -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密钥"""

View File

@@ -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())

View File

@@ -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.

BIN
pic.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -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;
}
}

View File

@@ -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 提示消息内容

View File

@@ -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;

View File

@@ -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>