|
<!DOCTYPE html> |
|
<html lang="zh-CN"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Cursor To OpenAI - API Key 管理</title> |
|
<style> |
|
body { |
|
font-family: 'Arial', sans-serif; |
|
line-height: 1.6; |
|
margin: 0; |
|
padding: 20px; |
|
color: #333; |
|
max-width: 1200px; |
|
margin: 0 auto; |
|
} |
|
h1, h2 { |
|
color: #2c3e50; |
|
} |
|
.container { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 20px; |
|
} |
|
.card { |
|
background: #fff; |
|
border-radius: 8px; |
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
|
padding: 20px; |
|
} |
|
.form-group { |
|
margin-bottom: 15px; |
|
} |
|
label { |
|
display: block; |
|
margin-bottom: 5px; |
|
font-weight: bold; |
|
} |
|
input, textarea { |
|
width: 100%; |
|
padding: 8px; |
|
border: 1px solid #ddd; |
|
border-radius: 4px; |
|
font-size: 16px; |
|
} |
|
textarea { |
|
min-height: 100px; |
|
font-family: monospace; |
|
} |
|
button { |
|
background: #3498db; |
|
color: white; |
|
border: none; |
|
padding: 10px 15px; |
|
border-radius: 4px; |
|
cursor: pointer; |
|
font-size: 16px; |
|
} |
|
button:hover { |
|
background: #2980b9; |
|
} |
|
table { |
|
width: 100%; |
|
border-collapse: collapse; |
|
} |
|
th, td { |
|
padding: 12px; |
|
text-align: left; |
|
border-bottom: 1px solid #ddd; |
|
} |
|
th { |
|
background-color: #f2f2f2; |
|
} |
|
.action-btn { |
|
background: #e74c3c; |
|
margin-right: 5px; |
|
} |
|
.action-btn:hover { |
|
background: #c0392b; |
|
} |
|
.edit-btn { |
|
background: #f39c12; |
|
margin-right: 5px; |
|
} |
|
.edit-btn:hover { |
|
background: #d35400; |
|
} |
|
.info { |
|
background-color: #d4edda; |
|
color: #155724; |
|
padding: 10px; |
|
border-radius: 4px; |
|
margin-bottom: 15px; |
|
} |
|
.error { |
|
background-color: #f8d7da; |
|
color: #721c24; |
|
padding: 10px; |
|
border-radius: 4px; |
|
margin-bottom: 15px; |
|
} |
|
.modal { |
|
display: none; |
|
position: fixed; |
|
z-index: 1; |
|
left: 0; |
|
top: 0; |
|
width: 100%; |
|
height: 100%; |
|
overflow: auto; |
|
background-color: rgba(0,0,0,0.4); |
|
} |
|
.modal-content { |
|
background-color: #fefefe; |
|
margin: 15% auto; |
|
padding: 20px; |
|
border: 1px solid #888; |
|
width: 80%; |
|
max-width: 600px; |
|
border-radius: 8px; |
|
} |
|
.close { |
|
color: #aaa; |
|
float: right; |
|
font-size: 28px; |
|
font-weight: bold; |
|
cursor: pointer; |
|
} |
|
.close:hover, |
|
.close:focus { |
|
color: black; |
|
text-decoration: none; |
|
} |
|
|
|
.cookie-text { |
|
max-width: 80%; |
|
word-break: break-all; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<div class="card"> |
|
<h1>Cursor To OpenAI - API Key 管理</h1> |
|
<p>在此页面上,您可以管理自定义 API Key 与 Cursor Cookie 的映射关系。</p> |
|
<div style="margin-top: 10px; display: flex; justify-content: space-between; align-items: center;"> |
|
<div> |
|
<button id="testApiBtn" style="margin-right: 10px;">测试API连接</button> |
|
<button id="clearCacheBtn">清除缓存并刷新</button> |
|
</div> |
|
<div> |
|
<span id="adminUsername" style="margin-right: 10px;"></span> |
|
<button id="logoutBtn" style="background: #e74c3c;">退出登录</button> |
|
</div> |
|
</div> |
|
<div id="testApiResult" style="margin-top: 10px;"></div> |
|
</div> |
|
|
|
<div class="card"> |
|
<h2>添加/更新 API Key</h2> |
|
<div id="addKeyMessage"></div> |
|
<form id="addKeyForm"> |
|
<div class="form-group"> |
|
<label for="apiKey">API Key(自定义)</label> |
|
<input type="text" id="apiKey" name="apiKey" placeholder="输入您想使用的自定义 API Key" required> |
|
</div> |
|
<div class="form-group"> |
|
<label for="cookieValues">Cursor Cookie 值(多个值请用逗号分隔)</label> |
|
<textarea id="cookieValues" name="cookieValues" placeholder="输入 WorkosCursorSessionToken 值,多个值请用逗号分隔" required></textarea> |
|
</div> |
|
<button type="submit">保存</button> |
|
</form> |
|
</div> |
|
|
|
<div class="card"> |
|
<h2>现有 API Key</h2> |
|
<div id="keyListMessage"></div> |
|
<table id="keyTable"> |
|
<thead> |
|
<tr> |
|
<th>API Key</th> |
|
<th>Cookie 数量</th> |
|
<th>操作</th> |
|
</tr> |
|
</thead> |
|
<tbody id="keyList"> |
|
|
|
</tbody> |
|
</table> |
|
</div> |
|
|
|
<div class="card"> |
|
<h2>使用说明</h2> |
|
<ol> |
|
<li>添加自定义 API Key 和对应的 Cursor Cookie 值。</li> |
|
<li>使用自定义 API Key 作为 OpenAI API 的认证凭证。</li> |
|
<li>系统将自动在多个 Cookie 之间进行轮询。</li> |
|
<li>API 端点: |
|
<ul> |
|
<li>模型列表:<code>/v1/models</code></li> |
|
<li>聊天补全:<code>/v1/chat/completions</code></li> |
|
</ul> |
|
</li> |
|
</ol> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="editModal" class="modal"> |
|
<div class="modal-content"> |
|
<span class="close">×</span> |
|
<h2>修改 API Key 的 Cookie</h2> |
|
<div id="editModalMessage"></div> |
|
<form id="editCookieForm"> |
|
<input type="hidden" id="editApiKey" name="editApiKey"> |
|
<div class="form-group"> |
|
<label for="editCookieValues">Cursor Cookie 值(多个值请用逗号分隔)</label> |
|
<textarea id="editCookieValues" name="editCookieValues" placeholder="输入 WorkosCursorSessionToken 值,多个值请用逗号分隔" required></textarea> |
|
</div> |
|
<button type="submit">保存修改</button> |
|
</form> |
|
</div> |
|
</div> |
|
|
|
<div class="card"> |
|
<h2>无效Cookie管理</h2> |
|
<div class="form-group"> |
|
<div class="info"> |
|
以下是系统自动检测到的无效Cookie列表。这些Cookie在请求过程中被发现无效,已被自动从API Key中移除。 |
|
</div> |
|
<div id="invalidCookiesContainer"> |
|
<div style="text-align: center; padding: 20px;"> |
|
<div>加载中...</div> |
|
</div> |
|
</div> |
|
<button id="clearAllInvalidCookies" style="background: #e74c3c;">清除所有无效Cookie</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="card"> |
|
<h2>Cookie自动刷新</h2> |
|
<div class="form-group"> |
|
<div class="info"> |
|
系统支持自动刷新Cookie,确保API Key始终有足够的可用Cookie。您可以在此手动触发刷新操作。 |
|
</div> |
|
<div id="refreshCookieMessage"></div> |
|
<div style="margin-top: 15px;"> |
|
<div class="form-group"> |
|
<label for="refreshApiKey">选择要刷新的API Key(不选则刷新所有)</label> |
|
<select id="refreshApiKey" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px;"> |
|
<option value="">所有API Key</option> |
|
|
|
</select> |
|
</div> |
|
<button id="refreshCookieBtn" style="background: #27ae60;">刷新Cookie</button> |
|
</div> |
|
<div id="refreshStatusContainer" style="margin-top: 15px; display: none;"> |
|
<div class="info"> |
|
<div>刷新状态:<span id="refreshStatus">准备中...</span></div> |
|
<div style="margin-top: 10px;"> |
|
<progress id="refreshProgress" value="0" max="100" style="width: 100%;"></progress> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
const modal = document.getElementById('editModal'); |
|
const closeBtn = document.getElementsByClassName('close')[0]; |
|
|
|
|
|
closeBtn.onclick = function() { |
|
modal.style.display = 'none'; |
|
} |
|
|
|
|
|
window.onclick = function(event) { |
|
if (event.target == modal) { |
|
modal.style.display = 'none'; |
|
} |
|
} |
|
|
|
|
|
async function loadApiKeys() { |
|
try { |
|
console.log('开始加载API Keys...'); |
|
const response = await fetch('/v1/api-keys', { |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
console.log('API响应状态:', response.status); |
|
const data = await response.json(); |
|
console.log('获取到的数据:', data); |
|
|
|
const keyList = document.getElementById('keyList'); |
|
keyList.innerHTML = ''; |
|
|
|
if (data.success && data.apiKeys.length > 0) { |
|
data.apiKeys.forEach(key => { |
|
const row = document.createElement('tr'); |
|
row.innerHTML = ` |
|
<td>${key.key}</td> |
|
<td>${key.cookieCount}</td> |
|
<td> |
|
<button class="edit-btn" onclick="editApiKey('${key.key}')">修改</button> |
|
<button class="action-btn" onclick="deleteApiKey('${key.key}')">删除</button> |
|
</td> |
|
`; |
|
keyList.appendChild(row); |
|
}); |
|
} else { |
|
keyList.innerHTML = '<tr><td colspan="3">暂无 API Key</td></tr>'; |
|
} |
|
} catch (error) { |
|
console.error('加载 API Key 失败:', error); |
|
document.getElementById('keyListMessage').innerHTML = ` |
|
<div class="error">加载 API Key 失败: ${error.message}</div> |
|
`; |
|
} |
|
} |
|
|
|
|
|
document.getElementById('addKeyForm').addEventListener('submit', async function(e) { |
|
e.preventDefault(); |
|
|
|
const apiKey = document.getElementById('apiKey').value.trim(); |
|
const cookieValuesText = document.getElementById('cookieValues').value.trim(); |
|
|
|
if (!apiKey || !cookieValuesText) { |
|
document.getElementById('addKeyMessage').innerHTML = ` |
|
<div class="error">API Key 和 Cookie 值不能为空</div> |
|
`; |
|
return; |
|
} |
|
|
|
|
|
const cookieValues = cookieValuesText.split(',').map(cookie => cookie.trim()).filter(cookie => cookie); |
|
|
|
try { |
|
const response = await fetch('/v1/api-keys', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
apiKey, |
|
cookieValues, |
|
}), |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (data.success) { |
|
document.getElementById('addKeyMessage').innerHTML = ` |
|
<div class="info">API Key 添加/更新成功</div> |
|
`; |
|
document.getElementById('apiKey').value = ''; |
|
document.getElementById('cookieValues').value = ''; |
|
loadApiKeys(); |
|
} else { |
|
document.getElementById('addKeyMessage').innerHTML = ` |
|
<div class="error">API Key 添加/更新失败: ${data.error}</div> |
|
`; |
|
} |
|
} catch (error) { |
|
console.error('添加/更新 API Key 失败:', error); |
|
document.getElementById('addKeyMessage').innerHTML = ` |
|
<div class="error">添加/更新 API Key 失败: ${error.message}</div> |
|
`; |
|
} |
|
}); |
|
|
|
|
|
async function deleteApiKey(apiKey) { |
|
if (!confirm(`确定要删除 API Key "${apiKey}" 吗?`)) { |
|
return; |
|
} |
|
|
|
try { |
|
const response = await fetch(`/v1/api-keys/${encodeURIComponent(apiKey)}`, { |
|
method: 'DELETE', |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (data.success) { |
|
document.getElementById('keyListMessage').innerHTML = ` |
|
<div class="info">API Key 删除成功</div> |
|
`; |
|
loadApiKeys(); |
|
} else { |
|
document.getElementById('keyListMessage').innerHTML = ` |
|
<div class="error">API Key 删除失败: ${data.error}</div> |
|
`; |
|
} |
|
} catch (error) { |
|
console.error('删除 API Key 失败:', error); |
|
document.getElementById('keyListMessage').innerHTML = ` |
|
<div class="error">删除 API Key 失败: ${error.message}</div> |
|
`; |
|
} |
|
} |
|
|
|
|
|
async function getCookiesForApiKey(apiKey) { |
|
try { |
|
const response = await fetch(`/v1/api-keys/${encodeURIComponent(apiKey)}/cookies`, { |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
return data.cookies; |
|
} catch (error) { |
|
console.error(`获取 ${apiKey} 的Cookie值失败:`, error); |
|
throw error; |
|
} |
|
} |
|
|
|
|
|
async function editApiKey(apiKey) { |
|
try { |
|
document.getElementById('editModalMessage').innerHTML = ''; |
|
document.getElementById('editApiKey').value = apiKey; |
|
|
|
|
|
const cookies = await getCookiesForApiKey(apiKey); |
|
document.getElementById('editCookieValues').value = cookies.join(','); |
|
|
|
|
|
modal.style.display = 'block'; |
|
} catch (error) { |
|
alert(`获取 ${apiKey} 的Cookie值失败: ${error.message}`); |
|
} |
|
} |
|
|
|
|
|
document.getElementById('editCookieForm').addEventListener('submit', async function(e) { |
|
e.preventDefault(); |
|
|
|
const apiKey = document.getElementById('editApiKey').value.trim(); |
|
const cookieValuesText = document.getElementById('editCookieValues').value.trim(); |
|
|
|
if (!apiKey || !cookieValuesText) { |
|
document.getElementById('editModalMessage').innerHTML = ` |
|
<div class="error">API Key 和 Cookie 值不能为空</div> |
|
`; |
|
return; |
|
} |
|
|
|
|
|
const cookieValues = cookieValuesText.split(',').map(cookie => cookie.trim()).filter(cookie => cookie); |
|
|
|
try { |
|
const response = await fetch('/v1/api-keys', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
apiKey, |
|
cookieValues, |
|
}), |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (data.success) { |
|
document.getElementById('editModalMessage').innerHTML = ` |
|
<div class="info">Cookie 修改成功</div> |
|
`; |
|
setTimeout(() => { |
|
modal.style.display = 'none'; |
|
loadApiKeys(); |
|
}, 1500); |
|
} else { |
|
document.getElementById('editModalMessage').innerHTML = ` |
|
<div class="error">Cookie 修改失败: ${data.error}</div> |
|
`; |
|
} |
|
} catch (error) { |
|
console.error('修改 Cookie 失败:', error); |
|
document.getElementById('editModalMessage').innerHTML = ` |
|
<div class="error">修改 Cookie 失败: ${error.message}</div> |
|
`; |
|
} |
|
}); |
|
|
|
|
|
document.getElementById('testApiBtn').addEventListener('click', async function() { |
|
const resultDiv = document.getElementById('testApiResult'); |
|
resultDiv.innerHTML = '<div class="info">正在测试API连接...</div>'; |
|
|
|
try { |
|
const response = await fetch('/v1/api-keys', { |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
resultDiv.innerHTML = `<div class="info">API响应状态: ${response.status}</div>`; |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
resultDiv.innerHTML += `<div class="info">获取到的数据: ${JSON.stringify(data)}</div>`; |
|
} catch (error) { |
|
console.error('测试API失败:', error); |
|
resultDiv.innerHTML = `<div class="error">测试API失败: ${error.message}</div>`; |
|
} |
|
}); |
|
|
|
|
|
document.getElementById('clearCacheBtn').addEventListener('click', function() { |
|
|
|
if ('caches' in window) { |
|
caches.keys().then(function(names) { |
|
for (let name of names) { |
|
caches.delete(name); |
|
} |
|
}); |
|
} |
|
|
|
|
|
window.location.reload(true); |
|
}); |
|
|
|
|
|
async function getInvalidCookies() { |
|
try { |
|
const response = await fetch('/v1/invalid-cookies', { |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
return data.invalidCookies; |
|
} catch (error) { |
|
console.error('获取无效Cookie失败:', error); |
|
throw error; |
|
} |
|
} |
|
|
|
|
|
async function clearInvalidCookie(cookie) { |
|
try { |
|
const response = await fetch(`/v1/invalid-cookies/${encodeURIComponent(cookie)}`, { |
|
method: 'DELETE', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
return data.success; |
|
} catch (error) { |
|
console.error(`清除无效Cookie失败:`, error); |
|
throw error; |
|
} |
|
} |
|
|
|
|
|
async function clearAllInvalidCookies() { |
|
try { |
|
const response = await fetch('/v1/invalid-cookies', { |
|
method: 'DELETE', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
return data.success; |
|
} catch (error) { |
|
console.error('清除所有无效Cookie失败:', error); |
|
throw error; |
|
} |
|
} |
|
|
|
|
|
async function renderInvalidCookies() { |
|
const container = document.getElementById('invalidCookiesContainer'); |
|
|
|
try { |
|
const invalidCookies = await getInvalidCookies(); |
|
|
|
if (invalidCookies.length === 0) { |
|
container.innerHTML = '<div class="info">没有检测到无效Cookie</div>'; |
|
return; |
|
} |
|
|
|
let html = '<table><thead><tr><th>无效Cookie</th><th>操作</th></tr></thead><tbody>'; |
|
|
|
invalidCookies.forEach(cookie => { |
|
|
|
const displayCookie = cookie.length > 50 ? cookie.substring(0, 50) + '...' : cookie; |
|
|
|
html += ` |
|
<tr> |
|
<td class="cookie-text" title="${cookie}">${displayCookie}</td> |
|
<td> |
|
<button class="action-btn clear-invalid-cookie" data-cookie="${cookie}"> |
|
清除 |
|
</button> |
|
</td> |
|
</tr> |
|
`; |
|
}); |
|
|
|
html += '</tbody></table>'; |
|
container.innerHTML = html; |
|
|
|
|
|
document.querySelectorAll('.clear-invalid-cookie').forEach(button => { |
|
button.addEventListener('click', async function() { |
|
const cookie = this.getAttribute('data-cookie'); |
|
|
|
try { |
|
await clearInvalidCookie(cookie); |
|
showMessage('invalidCookiesContainer', '无效Cookie已清除', 'info'); |
|
renderInvalidCookies(); |
|
} catch (error) { |
|
showMessage('invalidCookiesContainer', `清除失败: ${error.message}`, 'error'); |
|
} |
|
}); |
|
}); |
|
|
|
} catch (error) { |
|
container.innerHTML = `<div class="error">加载失败: ${error.message}</div>`; |
|
} |
|
} |
|
|
|
|
|
document.getElementById('clearAllInvalidCookies').addEventListener('click', async function() { |
|
try { |
|
await clearAllInvalidCookies(); |
|
showMessage('invalidCookiesContainer', '所有无效Cookie已清除', 'info'); |
|
renderInvalidCookies(); |
|
} catch (error) { |
|
showMessage('invalidCookiesContainer', `清除失败: ${error.message}`, 'error'); |
|
} |
|
}); |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
checkAuth(); |
|
loadApiKeys(); |
|
renderInvalidCookies(); |
|
populateRefreshApiKeySelect(); |
|
}); |
|
|
|
|
|
function showMessage(containerId, message, type) { |
|
const container = document.getElementById(containerId); |
|
container.innerHTML = `<div class="${type}">${message}</div>`; |
|
} |
|
|
|
|
|
async function populateRefreshApiKeySelect() { |
|
try { |
|
const apiKeys = await getApiKeys(); |
|
const select = document.getElementById('refreshApiKey'); |
|
|
|
|
|
while (select.options.length > 1) { |
|
select.remove(1); |
|
} |
|
|
|
|
|
apiKeys.forEach(key => { |
|
const option = document.createElement('option'); |
|
option.value = key.key; |
|
option.textContent = `${key.key} (${key.cookieCount} 个Cookie)`; |
|
select.appendChild(option); |
|
}); |
|
} catch (error) { |
|
console.error('加载API Key选项失败:', error); |
|
} |
|
} |
|
|
|
|
|
async function getApiKeys() { |
|
const response = await fetch('/v1/api-keys', { |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
const data = await response.json(); |
|
return data.success ? data.apiKeys : []; |
|
} |
|
|
|
|
|
document.getElementById('refreshCookieBtn').addEventListener('click', async function() { |
|
const refreshBtn = this; |
|
const apiKey = document.getElementById('refreshApiKey').value; |
|
const statusContainer = document.getElementById('refreshStatusContainer'); |
|
const statusText = document.getElementById('refreshStatus'); |
|
const progressBar = document.getElementById('refreshProgress'); |
|
|
|
|
|
refreshBtn.disabled = true; |
|
statusContainer.style.display = 'block'; |
|
statusText.textContent = '正在发送刷新请求...'; |
|
progressBar.value = 10; |
|
|
|
try { |
|
|
|
let url = '/v1/refresh-cookies'; |
|
if (apiKey) { |
|
url += `?apiKey=${encodeURIComponent(apiKey)}`; |
|
} |
|
|
|
|
|
statusText.textContent = '正在发送刷新请求...'; |
|
progressBar.value = 20; |
|
|
|
const response = await fetch(url, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
|
|
statusText.textContent = '刷新请求已发送,请耐心等待2-12分钟...'; |
|
progressBar.value = 50; |
|
showMessage('refreshCookieMessage', '刷新请求已发送,由于需要访问Cursor官网获取新Cookie,整个过程可能需要2-12分钟,请耐心等待。您可以关闭此页面,稍后再来查看结果。', 'info'); |
|
|
|
|
|
let checkInterval = setInterval(async () => { |
|
try { |
|
const statusResponse = await fetch('/v1/refresh-status', { |
|
method: 'GET', |
|
headers: { |
|
'Cache-Control': 'no-cache' |
|
} |
|
}); |
|
|
|
if (!statusResponse.ok) { |
|
throw new Error(`HTTP错误: ${statusResponse.status} ${statusResponse.statusText}`); |
|
} |
|
|
|
const statusData = await statusResponse.json(); |
|
const refreshData = statusData.data; |
|
|
|
|
|
statusText.textContent = refreshData.message || '正在刷新...'; |
|
|
|
|
|
if (refreshData.status === 'completed') { |
|
|
|
progressBar.value = 100; |
|
statusText.textContent = `刷新完成: ${refreshData.message}`; |
|
clearInterval(checkInterval); |
|
|
|
|
|
await loadApiKeys(); |
|
await populateRefreshApiKeySelect(); |
|
|
|
|
|
showMessage('refreshCookieMessage', `刷新完成: ${refreshData.message}`, 'success'); |
|
|
|
|
|
refreshBtn.disabled = false; |
|
|
|
|
|
setTimeout(() => { |
|
statusContainer.style.display = 'none'; |
|
}, 3000); |
|
} else if (refreshData.status === 'failed') { |
|
|
|
progressBar.value = 0; |
|
statusText.textContent = `刷新失败: ${refreshData.message}`; |
|
clearInterval(checkInterval); |
|
|
|
|
|
showMessage('refreshCookieMessage', `刷新失败: ${refreshData.message}`, 'error'); |
|
|
|
|
|
refreshBtn.disabled = false; |
|
} else if (refreshData.status === 'running') { |
|
|
|
progressBar.value = 75; |
|
} else if (!refreshData.isRunning) { |
|
|
|
clearInterval(checkInterval); |
|
refreshBtn.disabled = false; |
|
} |
|
} catch (error) { |
|
console.error('检查刷新状态失败:', error); |
|
} |
|
}, 5000); |
|
|
|
|
|
setTimeout(() => { |
|
if (checkInterval) { |
|
clearInterval(checkInterval); |
|
refreshBtn.disabled = false; |
|
statusContainer.style.display = 'none'; |
|
} |
|
}, 720000); |
|
} catch (error) { |
|
console.error('刷新Cookie失败:', error); |
|
statusText.textContent = '刷新请求发送失败'; |
|
progressBar.value = 0; |
|
showMessage('refreshCookieMessage', `刷新请求发送失败: ${error.message}`, 'error'); |
|
refreshBtn.disabled = false; |
|
} |
|
}); |
|
|
|
|
|
function checkAuth() { |
|
const token = localStorage.getItem('adminToken'); |
|
if (!token) { |
|
window.location.href = '/login.html'; |
|
return; |
|
} |
|
|
|
|
|
fetch('/v1/admin/verify', { |
|
headers: { |
|
'Authorization': `Bearer ${token}` |
|
} |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (!data.success) { |
|
localStorage.removeItem('adminToken'); |
|
window.location.href = '/login.html'; |
|
} else { |
|
|
|
document.getElementById('adminUsername').textContent = `管理员:${data.username}`; |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('验证失败:', error); |
|
localStorage.removeItem('adminToken'); |
|
window.location.href = '/login.html'; |
|
}); |
|
} |
|
|
|
|
|
document.getElementById('logoutBtn').addEventListener('click', () => { |
|
localStorage.removeItem('adminToken'); |
|
window.location.href = '/login.html'; |
|
}); |
|
|
|
|
|
function addAuthHeader(headers = {}) { |
|
const token = localStorage.getItem('adminToken'); |
|
return { |
|
...headers, |
|
'Authorization': `Bearer ${token}` |
|
}; |
|
} |
|
|
|
|
|
const originalFetch = window.fetch; |
|
window.fetch = function(url, options = {}) { |
|
|
|
if (url.includes('/v1/api-keys') || |
|
url.includes('/v1/invalid-cookies') || |
|
url.includes('/v1/refresh-cookies')) { |
|
options.headers = addAuthHeader(options.headers); |
|
} |
|
return originalFetch(url, options); |
|
}; |
|
</script> |
|
</body> |
|
</html> |