Spaces:
Running
Running
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Monica Proxy - 控制面板</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-primary: #0a0a0f; | |
| --bg-secondary: #12121a; | |
| --bg-card: rgba(255, 255, 255, 0.03); | |
| --border-color: rgba(255, 255, 255, 0.08); | |
| --text-primary: #f5f5f7; | |
| --text-secondary: #8e8e93; | |
| --accent-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| --success-color: #30d158; | |
| --error-color: #ff453a; | |
| --warning-color: #ffd60a; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background: var(--bg-primary); | |
| color: var(--text-primary); | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| background-image: | |
| radial-gradient(ellipse 80% 50% at 50% -20%, rgba(102, 126, 234, 0.15), transparent), | |
| radial-gradient(ellipse 60% 40% at 100% 100%, rgba(118, 75, 162, 0.1), transparent); | |
| } | |
| .container { | |
| width: 100%; | |
| max-width: 600px; | |
| } | |
| .card { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-color); | |
| border-radius: 20px; | |
| padding: 40px; | |
| backdrop-filter: blur(20px); | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); | |
| margin-bottom: 20px; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 36px; | |
| } | |
| .logo { | |
| font-size: 48px; | |
| margin-bottom: 16px; | |
| display: block; | |
| } | |
| h1 { | |
| font-size: 24px; | |
| font-weight: 600; | |
| background: var(--accent-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 8px; | |
| } | |
| .subtitle { | |
| color: var(--text-secondary); | |
| font-size: 14px; | |
| } | |
| .form-group { | |
| margin-bottom: 20px; | |
| } | |
| label { | |
| display: block; | |
| font-size: 13px; | |
| font-weight: 500; | |
| color: var(--text-secondary); | |
| margin-bottom: 8px; | |
| } | |
| input, | |
| textarea { | |
| width: 100%; | |
| padding: 14px 16px; | |
| background: var(--bg-secondary); | |
| border: 1px solid var(--border-color); | |
| border-radius: 12px; | |
| color: var(--text-primary); | |
| font-size: 14px; | |
| font-family: inherit; | |
| transition: all 0.2s ease; | |
| } | |
| textarea { | |
| resize: vertical; | |
| min-height: 80px; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| font-size: 12px; | |
| } | |
| input:focus, | |
| textarea:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15); | |
| } | |
| input::placeholder, | |
| textarea::placeholder { | |
| color: var(--text-secondary); | |
| opacity: 0.6; | |
| } | |
| .btn { | |
| width: 100%; | |
| padding: 16px; | |
| background: var(--accent-gradient); | |
| border: none; | |
| border-radius: 12px; | |
| color: white; | |
| font-size: 15px; | |
| font-weight: 600; | |
| font-family: inherit; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| margin-top: 8px; | |
| } | |
| .btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 24px rgba(102, 126, 234, 0.35); | |
| } | |
| .btn:active { | |
| transform: translateY(0); | |
| } | |
| .btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .btn-secondary { | |
| background: var(--bg-secondary); | |
| border: 1px solid var(--border-color); | |
| } | |
| .btn-secondary:hover { | |
| background: rgba(255, 255, 255, 0.1); | |
| box-shadow: none; | |
| } | |
| .btn-danger { | |
| background: linear-gradient(135deg, #ff453a 0%, #d63031 100%); | |
| } | |
| .status-panel { | |
| margin-top: 28px; | |
| padding: 20px; | |
| background: var(--bg-secondary); | |
| border-radius: 14px; | |
| border: 1px solid var(--border-color); | |
| } | |
| .status-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 16px; | |
| } | |
| .status-label { | |
| font-size: 13px; | |
| font-weight: 500; | |
| color: var(--text-secondary); | |
| } | |
| .status-badge { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 6px 12px; | |
| border-radius: 20px; | |
| font-size: 12px; | |
| font-weight: 600; | |
| } | |
| .status-badge.idle { | |
| background: rgba(142, 142, 147, 0.15); | |
| color: var(--text-secondary); | |
| } | |
| .status-badge.loading { | |
| background: rgba(255, 214, 10, 0.15); | |
| color: var(--warning-color); | |
| } | |
| .status-badge.success { | |
| background: rgba(48, 209, 88, 0.15); | |
| color: var(--success-color); | |
| } | |
| .status-badge.error { | |
| background: rgba(255, 69, 58, 0.15); | |
| color: var(--error-color); | |
| } | |
| .status-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: currentColor; | |
| } | |
| .status-badge.loading .status-dot { | |
| animation: pulse 1.5s ease-in-out infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, | |
| 100% { | |
| opacity: 1; | |
| } | |
| 50% { | |
| opacity: 0.4; | |
| } | |
| } | |
| .status-details { | |
| font-size: 13px; | |
| color: var(--text-secondary); | |
| line-height: 1.6; | |
| } | |
| .status-details pre { | |
| background: rgba(0, 0, 0, 0.3); | |
| padding: 12px; | |
| border-radius: 8px; | |
| overflow-x: auto; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| font-size: 12px; | |
| margin-top: 12px; | |
| white-space: pre-wrap; | |
| word-break: break-all; | |
| } | |
| .model-list { | |
| margin-top: 16px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| } | |
| .model-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 10px 14px; | |
| background: rgba(255, 255, 255, 0.03); | |
| border-radius: 10px; | |
| margin-bottom: 8px; | |
| border: 1px solid transparent; | |
| transition: all 0.2s ease; | |
| } | |
| .model-item:hover { | |
| border-color: var(--border-color); | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .model-icon { | |
| font-size: 18px; | |
| } | |
| .model-name { | |
| flex: 1; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| font-size: 13px; | |
| } | |
| .model-count { | |
| background: var(--accent-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| font-weight: 600; | |
| font-size: 14px; | |
| } | |
| .section-title { | |
| font-size: 16px; | |
| font-weight: 600; | |
| margin-bottom: 20px; | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .section-title .icon { | |
| font-size: 20px; | |
| } | |
| .tabs { | |
| display: flex; | |
| gap: 8px; | |
| margin-bottom: 24px; | |
| } | |
| .tab { | |
| flex: 1; | |
| padding: 12px; | |
| background: var(--bg-secondary); | |
| border: 1px solid var(--border-color); | |
| border-radius: 10px; | |
| color: var(--text-secondary); | |
| font-size: 13px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| text-align: center; | |
| } | |
| .tab:hover { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .tab.active { | |
| background: var(--accent-gradient); | |
| color: white; | |
| border-color: transparent; | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| .footer { | |
| text-align: center; | |
| margin-top: 24px; | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| } | |
| .footer a { | |
| color: #667eea; | |
| text-decoration: none; | |
| } | |
| .footer a:hover { | |
| text-decoration: underline; | |
| } | |
| .alert { | |
| padding: 14px 16px; | |
| border-radius: 10px; | |
| font-size: 13px; | |
| margin-bottom: 16px; | |
| display: none; | |
| } | |
| .alert.success { | |
| background: rgba(48, 209, 88, 0.15); | |
| border: 1px solid rgba(48, 209, 88, 0.3); | |
| color: var(--success-color); | |
| display: block; | |
| } | |
| .alert.error { | |
| background: rgba(255, 69, 58, 0.15); | |
| border: 1px solid rgba(255, 69, 58, 0.3); | |
| color: var(--error-color); | |
| display: block; | |
| } | |
| .info-box { | |
| background: rgba(102, 126, 234, 0.1); | |
| border: 1px solid rgba(102, 126, 234, 0.2); | |
| border-radius: 10px; | |
| padding: 14px 16px; | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| margin-bottom: 20px; | |
| } | |
| .info-box code { | |
| background: rgba(0, 0, 0, 0.3); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| } | |
| .guide-box { | |
| background: var(--bg-secondary); | |
| border: 1px solid var(--border-color); | |
| border-radius: 12px; | |
| margin-bottom: 20px; | |
| overflow: hidden; | |
| } | |
| .guide-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 14px 16px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 500; | |
| transition: background 0.2s ease; | |
| } | |
| .guide-header:hover { | |
| background: rgba(255, 255, 255, 0.03); | |
| } | |
| .guide-toggle { | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| transition: transform 0.2s ease; | |
| } | |
| .guide-toggle.open { | |
| transform: rotate(180deg); | |
| } | |
| .guide-content { | |
| display: none; | |
| padding: 0 16px 16px; | |
| border-top: 1px solid var(--border-color); | |
| } | |
| .guide-content.open { | |
| display: block; | |
| } | |
| .guide-steps { | |
| padding-top: 16px; | |
| } | |
| .guide-step { | |
| display: flex; | |
| gap: 14px; | |
| margin-bottom: 16px; | |
| } | |
| .guide-step:last-child { | |
| margin-bottom: 0; | |
| } | |
| .step-number { | |
| width: 28px; | |
| height: 28px; | |
| background: var(--accent-gradient); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 13px; | |
| font-weight: 600; | |
| flex-shrink: 0; | |
| } | |
| .step-content { | |
| flex: 1; | |
| } | |
| .step-content strong { | |
| display: block; | |
| font-size: 13px; | |
| margin-bottom: 4px; | |
| color: var(--text-primary); | |
| } | |
| .step-content p { | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| line-height: 1.5; | |
| } | |
| .step-content a { | |
| color: #667eea; | |
| text-decoration: none; | |
| } | |
| .step-content a:hover { | |
| text-decoration: underline; | |
| } | |
| .step-content code { | |
| background: rgba(0, 0, 0, 0.3); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| font-size: 11px; | |
| } | |
| .guide-note { | |
| margin-top: 16px; | |
| padding: 12px 14px; | |
| background: rgba(255, 214, 10, 0.08); | |
| border: 1px solid rgba(255, 214, 10, 0.15); | |
| border-radius: 8px; | |
| font-size: 12px; | |
| color: var(--text-secondary); | |
| line-height: 1.6; | |
| } | |
| .guide-note a { | |
| color: #667eea; | |
| text-decoration: none; | |
| } | |
| .guide-note a:hover { | |
| text-decoration: underline; | |
| } | |
| .guide-note code { | |
| background: rgba(0, 0, 0, 0.3); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| font-size: 11px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="card"> | |
| <div class="header"> | |
| <span class="logo">🤖</span> | |
| <h1>Monica Proxy</h1> | |
| <p class="subtitle">控制面板</p> | |
| </div> | |
| <div class="tabs"> | |
| <div class="tab active" onclick="switchTab('status')">📊 状态检测</div> | |
| <div class="tab" onclick="switchTab('cookie')">🔑 Cookie 管理</div> | |
| </div> | |
| <!-- Status Tab --> | |
| <div id="status-tab" class="tab-content active"> | |
| <div class="form-group"> | |
| <label for="apiUrl">API 地址</label> | |
| <input type="text" id="apiUrl" value="https://asem12345-monica-proxy.hf.space" | |
| placeholder="输入 API 地址"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="bearerToken">Bearer Token</label> | |
| <input type="password" id="bearerToken" placeholder="输入你的 Bearer Token"> | |
| </div> | |
| <button class="btn" id="checkBtn" onclick="checkStatus()"> | |
| 检测 Cookie 状态 | |
| </button> | |
| <div class="status-panel"> | |
| <div class="status-header"> | |
| <span class="status-label">状态</span> | |
| <div class="status-badge idle" id="statusBadge"> | |
| <span class="status-dot"></span> | |
| <span id="statusText">等待检测</span> | |
| </div> | |
| </div> | |
| <div class="status-details" id="statusDetails"> | |
| 点击上方按钮检测 Monica Proxy 的 Cookie 是否可用。 | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Cookie Management Tab --> | |
| <div id="cookie-tab" class="tab-content"> | |
| <!-- How to get cookie guide --> | |
| <div class="guide-box"> | |
| <div class="guide-header" onclick="toggleGuide()"> | |
| <span>📖 如何获取 Cookie?</span> | |
| <span class="guide-toggle" id="guideToggle">▼</span> | |
| </div> | |
| <div class="guide-content" id="guideContent"> | |
| <div class="guide-steps"> | |
| <div class="guide-step"> | |
| <span class="step-number">1</span> | |
| <div class="step-content"> | |
| <strong>打开 Monica 官网并登录</strong> | |
| <p>访问 <a href="https://monica.im" target="_blank">https://monica.im</a> 并登录你的账号</p> | |
| </div> | |
| </div> | |
| <div class="guide-step"> | |
| <span class="step-number">2</span> | |
| <div class="step-content"> | |
| <strong>打开开发者工具</strong> | |
| <p>按 <code>F12</code> 打开开发者工具</p> | |
| </div> | |
| </div> | |
| <div class="guide-step"> | |
| <span class="step-number">3</span> | |
| <div class="step-content"> | |
| <strong>找到 Cookie</strong> | |
| <p>切换到 <code>Application</code> → <code>Cookies</code> → <code>monica.im</code></p> | |
| </div> | |
| </div> | |
| <div class="guide-step"> | |
| <span class="step-number">4</span> | |
| <div class="step-content"> | |
| <strong>复制 session_id</strong> | |
| <p>找到 <code>session_id</code>,复制其值</p> | |
| </div> | |
| </div> | |
| <div class="guide-step"> | |
| <span class="step-number">5</span> | |
| <div class="step-content"> | |
| <strong>更新 Cookie</strong> | |
| <p>将复制的值粘贴到下方输入框,点击更新按钮</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="guide-note"> | |
| 💡 <strong>提示:</strong>Cookie 通常有效 7-30 天,过期后按上述流程重新获取。 | |
| <br><br> | |
| 🔗 也可以直接在 <a href="https://huggingface.co/spaces/ASEM12345/monica-proxy/settings" | |
| target="_blank">HF Space Settings</a> 中更新 <code>MONICA_COOKIE</code> Secret。 | |
| </div> | |
| </div> | |
| </div> | |
| <div class="info-box"> | |
| ⚠️ 更新 Cookie 需要提供 Bearer Token 进行身份验证。Cookie 更新后,代理服务将自动重启以应用新配置。 | |
| </div> | |
| <div id="cookieAlert" class="alert"></div> | |
| <div class="form-group"> | |
| <label for="cookieApiUrl">API 地址</label> | |
| <input type="text" id="cookieApiUrl" value="https://asem12345-monica-proxy.hf.space" | |
| placeholder="输入 API 地址"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="cookieBearerToken">Bearer Token(身份验证)</label> | |
| <input type="password" id="cookieBearerToken" placeholder="输入你的 Bearer Token"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="newCookie">新的 MONICA_COOKIE</label> | |
| <textarea id="newCookie" placeholder="粘贴新的 Monica Cookie 值..."></textarea> | |
| </div> | |
| <button class="btn btn-danger" id="updateCookieBtn" onclick="updateCookie()"> | |
| 🔄 更新 Cookie | |
| </button> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| Powered by <a href="https://github.com/ycvk/monica-proxy" target="_blank">ycvk/monica-proxy</a> | |
| </div> | |
| </div> | |
| <script> | |
| function switchTab(tabName) { | |
| // Update tab buttons | |
| document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| // Update tab content | |
| document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); | |
| document.getElementById(tabName + '-tab').classList.add('active'); | |
| } | |
| function toggleGuide() { | |
| const content = document.getElementById('guideContent'); | |
| const toggle = document.getElementById('guideToggle'); | |
| content.classList.toggle('open'); | |
| toggle.classList.toggle('open'); | |
| } | |
| async function checkStatus() { | |
| const apiUrl = document.getElementById('apiUrl').value.trim().replace(/\/$/, ''); | |
| const bearerToken = document.getElementById('bearerToken').value.trim(); | |
| const btn = document.getElementById('checkBtn'); | |
| const statusBadge = document.getElementById('statusBadge'); | |
| const statusText = document.getElementById('statusText'); | |
| const statusDetails = document.getElementById('statusDetails'); | |
| if (!apiUrl) { | |
| alert('请输入 API 地址'); | |
| return; | |
| } | |
| if (!bearerToken) { | |
| alert('请输入 Bearer Token'); | |
| return; | |
| } | |
| // Loading state | |
| btn.disabled = true; | |
| btn.textContent = '检测中...'; | |
| statusBadge.className = 'status-badge loading'; | |
| statusText.textContent = '检测中'; | |
| statusDetails.innerHTML = '正在连接到 Monica Proxy...'; | |
| try { | |
| const response = await fetch(`${apiUrl}/v1/models`, { | |
| method: 'GET', | |
| headers: { | |
| 'Authorization': `Bearer ${bearerToken}`, | |
| 'Content-Type': 'application/json' | |
| } | |
| }); | |
| const data = await response.json(); | |
| if (response.ok && data.data && data.data.length > 0) { | |
| // Success | |
| statusBadge.className = 'status-badge success'; | |
| statusText.textContent = 'Cookie 可用'; | |
| let html = ` | |
| <p>✅ Cookie 工作正常!</p> | |
| <div style="margin-top: 16px; display: flex; align-items: center; justify-content: space-between;"> | |
| <span class="section-title" style="margin: 0;"><span class="icon">🧠</span> 可用模型</span> | |
| <span class="model-count">${data.data.length} 个</span> | |
| </div> | |
| <div class="model-list"> | |
| `; | |
| data.data.forEach(model => { | |
| const icon = getModelIcon(model.id); | |
| html += `<div class="model-item"><span class="model-icon">${icon}</span><span class="model-name">${model.id}</span></div>`; | |
| }); | |
| html += '</div>'; | |
| statusDetails.innerHTML = html; | |
| } else if (response.status === 401) { | |
| // Unauthorized | |
| statusBadge.className = 'status-badge error'; | |
| statusText.textContent = 'Token 错误'; | |
| statusDetails.innerHTML = `<p>❌ Bearer Token 验证失败</p><pre>${JSON.stringify(data, null, 2)}</pre>`; | |
| } else { | |
| // Other error - possibly cookie issue | |
| statusBadge.className = 'status-badge error'; | |
| statusText.textContent = 'Cookie 失效'; | |
| statusDetails.innerHTML = ` | |
| <p>❌ Cookie 可能已失效或存在其他问题</p> | |
| <pre>${JSON.stringify(data, null, 2)}</pre> | |
| <p style="margin-top: 16px;">👉 请切换到 <strong>Cookie 管理</strong> 标签页更新 Cookie</p> | |
| `; | |
| } | |
| } catch (error) { | |
| statusBadge.className = 'status-badge error'; | |
| statusText.textContent = '连接失败'; | |
| statusDetails.innerHTML = `<p>❌ 无法连接到 API</p><pre>${error.message}</pre><p style="margin-top: 12px; color: var(--text-secondary);">可能原因:网络问题、CORS 限制、或服务不可用</p>`; | |
| } finally { | |
| btn.disabled = false; | |
| btn.textContent = '检测 Cookie 状态'; | |
| } | |
| } | |
| function getModelIcon(modelId) { | |
| const id = modelId.toLowerCase(); | |
| if (id.includes('gpt')) return '🟢'; | |
| if (id.includes('claude')) return '🟠'; | |
| if (id.includes('gemini')) return '🔵'; | |
| if (id.includes('grok')) return '⚫'; | |
| if (id.includes('llama')) return '🦙'; | |
| if (id.includes('qwen')) return '🟣'; | |
| if (id.includes('deepseek')) return '🔷'; | |
| if (id.includes('sonar')) return '📡'; | |
| if (id.includes('o1') || id.includes('o3')) return '🧪'; | |
| return '🤖'; | |
| } | |
| async function updateCookie() { | |
| const apiUrl = document.getElementById('cookieApiUrl').value.trim().replace(/\/$/, ''); | |
| const bearerToken = document.getElementById('cookieBearerToken').value.trim(); | |
| const newCookie = document.getElementById('newCookie').value.trim(); | |
| const btn = document.getElementById('updateCookieBtn'); | |
| const alertBox = document.getElementById('cookieAlert'); | |
| if (!apiUrl || !bearerToken || !newCookie) { | |
| showAlert('error', '请填写所有字段'); | |
| return; | |
| } | |
| btn.disabled = true; | |
| btn.textContent = '更新中...'; | |
| alertBox.className = 'alert'; | |
| try { | |
| const response = await fetch(`${apiUrl}/admin/update-cookie`, { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${bearerToken}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ cookie: newCookie }) | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| showAlert('success', '✅ Cookie 更新成功!服务将在几秒后重启。'); | |
| document.getElementById('newCookie').value = ''; | |
| } else { | |
| showAlert('error', `❌ 更新失败: ${data.error || data.message || '未知错误'}`); | |
| } | |
| } catch (error) { | |
| showAlert('error', `❌ 请求失败: ${error.message}`); | |
| } finally { | |
| btn.disabled = false; | |
| btn.textContent = '🔄 更新 Cookie'; | |
| } | |
| } | |
| function showAlert(type, message) { | |
| const alertBox = document.getElementById('cookieAlert'); | |
| alertBox.className = `alert ${type}`; | |
| alertBox.textContent = message; | |
| } | |
| // Allow Enter key to trigger check | |
| document.getElementById('bearerToken').addEventListener('keypress', function (e) { | |
| if (e.key === 'Enter') { | |
| checkStatus(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |