File size: 4,923 Bytes
b6b69a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<!DOCTYPE html>
<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>