XAI / static /admin.html
bobocup's picture
Update static/admin.html
f4e0a06 verified
<!DOCTYPE html>
<html>
<head>
<title>API Keys Management</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#loginForm, #mainContent {
margin: 20px 0;
}
.actions {
margin: 20px 0;
padding: 10px;
background-color: #f8f9fa;
border-radius: 4px;
}
.key-list {
margin-top: 20px;
}
.key-item {
display: flex;
align-items: center;
padding: 12px;
border: 1px solid #ddd;
margin: 5px 0;
border-radius: 4px;
background-color: white;
}
.key-text {
flex-grow: 1;
margin: 0 10px;
font-family: monospace;
}
.key-status {
margin: 0 10px;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.9em;
}
.status-valid {
background-color: #d4edda;
color: #155724;
}
.status-invalid {
background-color: #f8d7da;
color: #721c24;
}
.status-cooling {
background-color: #fff3cd;
color: #856404;
}
button {
padding: 8px 16px;
margin: 0 5px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #007bff;
color: white;
}
button:hover {
background-color: #0056b3;
}
button.delete {
background-color: #dc3545;
}
button.delete:hover {
background-color: #c82333;
}
input[type="text"], input[type="password"] {
padding: 8px;
margin: 5px;
border: 1px solid #ddd;
border-radius: 4px;
width: 300px;
}
.loading {
opacity: 0.5;
pointer-events: none;
}
.error {
color: #dc3545;
margin: 10px 0;
}
.success {
color: #28a745;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>API Keys Management</h1>
<!-- 登录表单 -->
<div id="loginForm">
<input type="password" id="password" placeholder="Enter admin password">
<button onclick="login()">Login</button>
</div>
<!-- 主要内容 -->
<div id="mainContent" style="display: none;">
<!-- 添加新key -->
<div class="actions">
<input type="text" id="newKey" placeholder="Enter new API key">
<button onclick="addKey()">Add Key</button>
</div>
<!-- 批量操作 -->
<div class="actions">
<button onclick="checkAllKeys()">Check All Keys</button>
<button onclick="deleteSelected()" class="delete">Delete Selected</button>
<button onclick="selectAll()" id="selectAllBtn">Select All</button>
</div>
<!-- 统计信息 -->
<div id="stats" class="actions">
Loading stats...
</div>
<!-- Key列表 -->
<div id="keyList" class="key-list">
Loading keys...
</div>
</div>
</div>
<script>
let adminPassword = '';
let allSelected = false;
// 登录
async function login() {
const password = document.getElementById('password').value;
try {
const response = await fetch('/api/admin/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({password})
});
if (response.ok) {
adminPassword = password;
document.getElementById('loginForm').style.display = 'none';
document.getElementById('mainContent').style.display = 'block';
loadKeys();
} else {
alert('Invalid password');
}
} catch (error) {
alert('Login failed');
}
}
// 加载keys
async function loadKeys() {
try {
const response = await fetch(`/api/keys?password=${encodeURIComponent(adminPassword)}`);
const data = await response.json();
updateKeyList(data.keys);
updateStats(data.keys);
} catch (error) {
alert('Error loading keys');
}
}
// 更新key列表
function updateKeyList(keys) {
const keyList = document.getElementById('keyList');
keyList.innerHTML = '';
keys.forEach(keyInfo => {
const keyItem = document.createElement('div');
keyItem.className = 'key-item';
const statusClass = `status-${keyInfo.status}`;
const coolingInfo = keyInfo.cooling_until ?
`(Cooling until ${new Date(keyInfo.cooling_until).toLocaleString()})` : '';
keyItem.innerHTML = `
<input type="checkbox" value="${keyInfo.key}">
<div class="key-text">${keyInfo.key}</div>
<div class="key-status ${statusClass}">
${keyInfo.status.toUpperCase()} ${coolingInfo}
</div>
<button onclick="checkKey('${keyInfo.key}')">Check</button>
<button onclick="deleteKey('${keyInfo.key}')" class="delete">Delete</button>
`;
keyList.appendChild(keyItem);
});
}
// 更新统计信息
function updateStats(keys) {
const stats = document.getElementById('stats');
const total = keys.length;
const valid = keys.filter(k => k.status === 'valid').length;
const cooling = keys.filter(k => k.status === 'cooling').length;
const invalid = keys.filter(k => k.status === 'invalid').length;
stats.innerHTML = `
Total Keys: ${total} |
Valid: ${valid} |
Cooling: ${cooling} |
Invalid: ${invalid}
`;
}
// 添加新key
async function addKey() {
const key = document.getElementById('newKey').value.trim();
if (!key) return;
try {
const response = await fetch('/api/keys/add', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({password: adminPassword, key})
});
if (response.ok) {
document.getElementById('newKey').value = '';
loadKeys();
} else {
const data = await response.json();
alert(data.detail || 'Error adding key');
}
} catch (error) {
alert('Error adding key');
}
}
// 检查所有keys
async function checkAllKeys() {
try {
const response = await fetch(`/api/keys/check-all?password=${encodeURIComponent(adminPassword)}`, {
method: 'POST'
});
if (response.ok) {
loadKeys();
alert('All keys checked');
} else {
alert('Error checking keys');
}
} catch (error) {
alert('Error checking keys');
}
}
// 删除选中的keys
async function deleteSelected() {
const selected = Array.from(document.querySelectorAll('input[type="checkbox"]:checked'))
.map(cb => cb.value);
if (!selected.length) return;
if (!confirm(`Delete ${selected.length} selected keys?`)) return;
try {
const response = await fetch('/api/keys/delete-batch', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
password: adminPassword,
keys: selected
})
});
if (response.ok) {
loadKeys();
} else {
alert('Error deleting keys');
}
} catch (error) {
alert('Error deleting keys');
}
}
// 全选/取消全选
function selectAll() {
allSelected = !allSelected;
document.querySelectorAll('input[type="checkbox"]')
.forEach(cb => cb.checked = allSelected);
document.getElementById('selectAllBtn').textContent =
allSelected ? 'Deselect All' : 'Select All';
}
// 检查单个key
async function checkKey(key) {
try {
const response = await fetch(`/api/keys/check/${key}?password=${encodeURIComponent(adminPassword)}`);
if (response.ok) {
loadKeys();
} else {
alert('Error checking key');
}
} catch (error) {
alert('Error checking key');
}
}
// 删除单个key
async function deleteKey(key) {
if (!confirm('Delete this key?')) return;
try {
const response = await fetch(`/api/keys/${key}?password=${encodeURIComponent(adminPassword)}`, {
method: 'DELETE'
});
if (response.ok) {
loadKeys();
} else {
alert('Error deleting key');
}
} catch (error) {
alert('Error deleting key');
}
}
// 按Enter键登录
document.getElementById('password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
login();
}
});
// 按Enter键添加key
document.getElementById('newKey').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addKey();
}
});
</script>
</body>
</html>