Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AuthorBot Admin</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| background: #0d0f1a; color: #e2e8f0; min-height: 100vh; padding: 32px 20px; | |
| } | |
| .wrap { max-width: 720px; margin: 0 auto; } | |
| h1 { font-size: 1.75rem; margin-bottom: 8px; } | |
| .sub { color: #94a3b8; margin-bottom: 28px; } | |
| .panel { | |
| background: #13161f; border: 1px solid rgba(255,255,255,0.08); | |
| border-radius: 16px; padding: 24px; margin-bottom: 20px; | |
| } | |
| label { display: block; font-size: 13px; color: #94a3b8; margin-bottom: 6px; } | |
| input { | |
| width: 100%; padding: 10px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.12); | |
| background: #0d0f1a; color: #e2e8f0; margin-bottom: 12px; | |
| } | |
| button, a.btn { | |
| display: inline-block; padding: 10px 18px; border-radius: 8px; border: none; | |
| background: #6366f1; color: white; font-weight: 600; cursor: pointer; text-decoration: none; | |
| } | |
| button:hover, a.btn:hover { background: #4f46e5; } | |
| .links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; } | |
| .links a { color: #a5b4fc; text-decoration: none; font-size: 14px; } | |
| .msg { margin-top: 12px; font-size: 14px; color: #4ade80; } | |
| .err { color: #f87171; } | |
| #dashboard { display: none; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>AuthorBot Admin</h1> | |
| <p class="sub">Manage books, uploads, and settings via the API.</p> | |
| <div class="panel" id="login-panel"> | |
| <h2 style="margin-bottom:16px;font-size:1.1rem">Sign in</h2> | |
| <label>Email</label> | |
| <input id="email" type="email" placeholder="you@example.com"> | |
| <label>Password</label> | |
| <input id="password" type="password" placeholder="Password"> | |
| <button type="button" id="login-btn">Login</button> | |
| <p id="login-msg"></p> | |
| <p style="margin-top:12px;font-size:13px;color:#64748b"> | |
| No account? Register via <a href="/docs#/Authentication/register_api_v1_auth_register_post" style="color:#a5b4fc">API docs</a>. | |
| </p> | |
| </div> | |
| <div class="panel" id="dashboard"> | |
| <p class="msg" id="welcome"></p> | |
| <div class="links"> | |
| <a href="/ui">Open chat UI</a> | |
| <a href="/widget">Open widget</a> | |
| <a href="/docs">API docs</a> | |
| <a href="/health">Health</a> | |
| </div> | |
| <div class="links" style="margin-top:20px"> | |
| <a href="#" id="books-link">List books (JSON)</a> | |
| <a href="#" id="logout-link">Logout</a> | |
| </div> | |
| <pre id="output" style="margin-top:16px;background:#0d0f1a;padding:12px;border-radius:8px;overflow:auto;font-size:12px;display:none"></pre> | |
| </div> | |
| </div> | |
| <script> | |
| const API = window.location.origin + '/api/v1'; | |
| const token = localStorage.getItem('access_token'); | |
| function showDashboard(email) { | |
| document.getElementById('login-panel').style.display = 'none'; | |
| document.getElementById('dashboard').style.display = 'block'; | |
| document.getElementById('welcome').textContent = 'Signed in as ' + email; | |
| } | |
| if (token) showDashboard(localStorage.getItem('user_email') || 'author'); | |
| document.getElementById('login-btn').onclick = async () => { | |
| const msg = document.getElementById('login-msg'); | |
| msg.textContent = ''; | |
| msg.className = ''; | |
| try { | |
| const res = await fetch(API + '/auth/login', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| email: document.getElementById('email').value, | |
| password: document.getElementById('password').value, | |
| }), | |
| }); | |
| const data = await res.json(); | |
| if (!res.ok) throw new Error(data.detail || 'Login failed'); | |
| localStorage.setItem('access_token', data.access_token); | |
| localStorage.setItem('refresh_token', data.refresh_token); | |
| localStorage.setItem('user_email', document.getElementById('email').value); | |
| showDashboard(document.getElementById('email').value); | |
| } catch (e) { | |
| msg.textContent = e.message; | |
| msg.className = 'err'; | |
| } | |
| }; | |
| document.getElementById('books-link').onclick = async (e) => { | |
| e.preventDefault(); | |
| const out = document.getElementById('output'); | |
| out.style.display = 'block'; | |
| const res = await fetch(API + '/books/', { | |
| headers: { Authorization: 'Bearer ' + localStorage.getItem('access_token') }, | |
| }); | |
| out.textContent = JSON.stringify(await res.json(), null, 2); | |
| }; | |
| document.getElementById('logout-link').onclick = (e) => { | |
| e.preventDefault(); | |
| localStorage.clear(); | |
| location.reload(); | |
| }; | |
| </script> | |
| </body> | |
| </html> | |