|
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>API密钥测试更新</title>
|
|
<link rel="stylesheet" href="/static/css/base.css">
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
<style>
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
.key-list {
|
|
margin-top: 20px;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
.key-item {
|
|
padding: 15px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.key-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.key-item:hover {
|
|
background-color: #f9fafb;
|
|
}
|
|
.key-info {
|
|
flex-grow: 1;
|
|
}
|
|
.key-name {
|
|
font-weight: 500;
|
|
margin-bottom: 5px;
|
|
}
|
|
.key-value {
|
|
font-family: monospace;
|
|
color: #6b7280;
|
|
word-break: break-all;
|
|
}
|
|
.key-platform {
|
|
color: #4f46e5;
|
|
font-size: 0.875rem;
|
|
margin-bottom: 8px;
|
|
}
|
|
.key-status {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
min-width: 120px;
|
|
}
|
|
.update-btn {
|
|
padding: 6px 12px;
|
|
border-radius: 6px;
|
|
background-color: #4f46e5;
|
|
color: white;
|
|
font-size: 0.875rem;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
}
|
|
.update-btn:hover {
|
|
background-color: #4338ca;
|
|
}
|
|
.success-badge {
|
|
margin-top: 8px;
|
|
padding: 4px 8px;
|
|
border-radius: 9999px;
|
|
font-size: 0.75rem;
|
|
background-color: #dcfce7;
|
|
color: #16a34a;
|
|
}
|
|
.error-badge {
|
|
margin-top: 8px;
|
|
padding: 4px 8px;
|
|
border-radius: 9999px;
|
|
font-size: 0.75rem;
|
|
background-color: #fee2e2;
|
|
color: #dc2626;
|
|
}
|
|
.pending-badge {
|
|
margin-top: 8px;
|
|
padding: 4px 8px;
|
|
border-radius: 9999px;
|
|
font-size: 0.75rem;
|
|
background-color: #e0f2fe;
|
|
color: #0284c7;
|
|
}
|
|
#loading {
|
|
display: none;
|
|
margin: 20px auto;
|
|
text-align: center;
|
|
font-style: italic;
|
|
color: #6b7280;
|
|
}
|
|
.response-container {
|
|
margin-top: 15px;
|
|
background-color: #f9fafb;
|
|
border-radius: 6px;
|
|
padding: 10px;
|
|
font-family: monospace;
|
|
font-size: 0.875rem;
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
display: none;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>API密钥测试与更新</h1>
|
|
<p>在此页面可以测试update.py脚本的功能,验证API密钥并更新其状态。</p>
|
|
|
|
<div id="loading">正在加载API密钥列表...</div>
|
|
|
|
<div id="keyList" class="key-list"></div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadKeys();
|
|
|
|
|
|
const container = document.querySelector('.container');
|
|
const backButton = document.createElement('a');
|
|
backButton.href = '/';
|
|
backButton.textContent = '返回主页';
|
|
backButton.style.display = 'inline-block';
|
|
backButton.style.marginTop = '20px';
|
|
backButton.style.color = '#4f46e5';
|
|
backButton.style.textDecoration = 'none';
|
|
container.appendChild(backButton);
|
|
});
|
|
|
|
async function loadKeys() {
|
|
const loadingElement = document.getElementById('loading');
|
|
const keyListElement = document.getElementById('keyList');
|
|
|
|
loadingElement.style.display = 'block';
|
|
keyListElement.innerHTML = '';
|
|
|
|
try {
|
|
const response = await fetch('/api/keys');
|
|
const data = await response.json();
|
|
|
|
if (data && data.api_keys && data.api_keys.length > 0) {
|
|
data.api_keys.forEach(key => {
|
|
const keyElement = createKeyElement(key);
|
|
keyListElement.appendChild(keyElement);
|
|
});
|
|
} else {
|
|
keyListElement.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">没有找到API密钥</div>';
|
|
}
|
|
} catch (error) {
|
|
console.error('加载API密钥失败:', error);
|
|
keyListElement.innerHTML = `<div style="padding: 20px; text-align: center; color: #dc2626;">加载API密钥失败: ${error.message}</div>`;
|
|
} finally {
|
|
loadingElement.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function createKeyElement(key) {
|
|
const keyItem = document.createElement('div');
|
|
keyItem.className = 'key-item';
|
|
keyItem.id = `key-${key.id}`;
|
|
|
|
|
|
const keyInfo = document.createElement('div');
|
|
keyInfo.className = 'key-info';
|
|
|
|
|
|
const platformElement = document.createElement('div');
|
|
platformElement.className = 'key-platform';
|
|
platformElement.textContent = getPlatformName(key.platform);
|
|
keyInfo.appendChild(platformElement);
|
|
|
|
|
|
const nameElement = document.createElement('div');
|
|
nameElement.className = 'key-name';
|
|
nameElement.textContent = key.name;
|
|
keyInfo.appendChild(nameElement);
|
|
|
|
|
|
const valueElement = document.createElement('div');
|
|
valueElement.className = 'key-value';
|
|
valueElement.textContent = key.key;
|
|
keyInfo.appendChild(valueElement);
|
|
|
|
|
|
if (key.updated_at) {
|
|
const updatedElement = document.createElement('div');
|
|
updatedElement.style.fontSize = '0.75rem';
|
|
updatedElement.style.color = '#6b7280';
|
|
updatedElement.style.marginTop = '5px';
|
|
|
|
const date = new Date(key.updated_at);
|
|
updatedElement.textContent = `上次更新: ${date.toLocaleString()}`;
|
|
keyInfo.appendChild(updatedElement);
|
|
}
|
|
|
|
|
|
const responseContainer = document.createElement('div');
|
|
responseContainer.className = 'response-container';
|
|
responseContainer.id = `response-${key.id}`;
|
|
keyInfo.appendChild(responseContainer);
|
|
|
|
keyItem.appendChild(keyInfo);
|
|
|
|
|
|
const keyStatus = document.createElement('div');
|
|
keyStatus.className = 'key-status';
|
|
|
|
|
|
const updateButton = document.createElement('button');
|
|
updateButton.className = 'update-btn';
|
|
updateButton.textContent = '更新状态';
|
|
updateButton.onclick = function() {
|
|
updateKeyStatus(key.id);
|
|
};
|
|
keyStatus.appendChild(updateButton);
|
|
|
|
|
|
const statusBadge = document.createElement('div');
|
|
if (key.success === true) {
|
|
statusBadge.className = 'success-badge';
|
|
statusBadge.textContent = '验证成功';
|
|
} else if (key.success === false) {
|
|
statusBadge.className = 'error-badge';
|
|
statusBadge.textContent = key.return_message || '验证失败';
|
|
} else {
|
|
statusBadge.className = 'pending-badge';
|
|
statusBadge.textContent = '等待验证';
|
|
}
|
|
keyStatus.appendChild(statusBadge);
|
|
|
|
keyItem.appendChild(keyStatus);
|
|
|
|
return keyItem;
|
|
}
|
|
|
|
async function updateKeyStatus(keyId) {
|
|
const keyElement = document.getElementById(`key-${keyId}`);
|
|
const updateButton = keyElement.querySelector('.update-btn');
|
|
const responseContainer = document.getElementById(`response-${keyId}`);
|
|
|
|
|
|
updateButton.disabled = true;
|
|
updateButton.textContent = '更新中...';
|
|
|
|
try {
|
|
const response = await fetch(`/api/keys/update/${keyId}`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
responseContainer.textContent = JSON.stringify(result, null, 2);
|
|
responseContainer.style.display = 'block';
|
|
|
|
|
|
loadKeys();
|
|
|
|
} catch (error) {
|
|
console.error(`更新密钥 ${keyId} 失败:`, error);
|
|
responseContainer.textContent = `更新失败: ${error.message}`;
|
|
responseContainer.style.display = 'block';
|
|
} finally {
|
|
|
|
updateButton.disabled = false;
|
|
updateButton.textContent = '更新状态';
|
|
}
|
|
}
|
|
|
|
function getPlatformName(platformId) {
|
|
const platforms = {
|
|
'openai': 'OpenAI',
|
|
'anthropic': 'Anthropic',
|
|
'google': 'Google',
|
|
'deepseek': 'DeepSeek'
|
|
};
|
|
|
|
return platforms[platformId] || platformId;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|