NikaMimi commited on
Commit
78f0757
·
verified ·
1 Parent(s): d5e7dde

Upload 4 files

Browse files
Files changed (4) hide show
  1. index.html +31 -0
  2. login.html +253 -0
  3. static/app.js +116 -2
  4. static/styles.css +91 -0
index.html CHANGED
@@ -19,6 +19,9 @@
19
  <h1>CatGPT Model Manager</h1>
20
  </div>
21
  <div class="header-stats">
 
 
 
22
  <button class="theme-toggle" id="themeToggle" title="Toggle dark mode">
23
  <i class="fas fa-moon" id="themeIcon"></i>
24
  </button>
@@ -179,6 +182,34 @@
179
  </div>
180
  </div>
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  <footer class="app-footer">
183
  <p>For aRfy's Cat Bot</p>
184
  </footer>
 
19
  <h1>CatGPT Model Manager</h1>
20
  </div>
21
  <div class="header-stats">
22
+ <button class="logout-btn" id="logoutBtn" title="Logout" onclick="modelManager.logout()">
23
+ <i class="fas fa-sign-out-alt"></i>
24
+ </button>
25
  <button class="theme-toggle" id="themeToggle" title="Toggle dark mode">
26
  <i class="fas fa-moon" id="themeIcon"></i>
27
  </button>
 
182
  </div>
183
  </div>
184
 
185
+ <!-- Login Modal -->
186
+ <div class="modal-overlay" id="loginModal">
187
+ <div class="modal">
188
+ <div class="modal-header">
189
+ <h3><i class="fas fa-lock"></i> Admin Login</h3>
190
+ <button class="modal-close" id="loginModalClose">
191
+ <i class="fas fa-times"></i>
192
+ </button>
193
+ </div>
194
+ <div class="modal-body">
195
+ <form id="loginForm">
196
+ <div class="form-group">
197
+ <label for="adminKey">Admin Key</label>
198
+ <input type="password" id="adminKey" name="adminKey" required placeholder="Enter admin key">
199
+ </div>
200
+ <div id="loginError" class="error-message" style="display: none;"></div>
201
+ </form>
202
+ </div>
203
+ <div class="modal-footer">
204
+ <button class="btn btn-primary" id="loginSubmit">
205
+ <i class="fas fa-sign-in-alt"></i>
206
+ Login
207
+ </button>
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+
213
  <footer class="app-footer">
214
  <p>For aRfy's Cat Bot</p>
215
  </footer>
login.html ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CatGPT Admin Login</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 20px;
23
+ }
24
+
25
+ .login-container {
26
+ background: white;
27
+ border-radius: 12px;
28
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
29
+ padding: 40px;
30
+ width: 100%;
31
+ max-width: 400px;
32
+ text-align: center;
33
+ }
34
+
35
+ .logo {
36
+ margin-bottom: 30px;
37
+ }
38
+
39
+ .logo i {
40
+ font-size: 3rem;
41
+ color: #667eea;
42
+ margin-bottom: 15px;
43
+ }
44
+
45
+ .logo h1 {
46
+ font-size: 1.5rem;
47
+ color: #333;
48
+ margin-bottom: 10px;
49
+ }
50
+
51
+ .logo p {
52
+ color: #666;
53
+ font-size: 0.9rem;
54
+ }
55
+
56
+ .login-form {
57
+ margin-top: 30px;
58
+ }
59
+
60
+ .form-group {
61
+ margin-bottom: 20px;
62
+ text-align: left;
63
+ }
64
+
65
+ .form-group label {
66
+ display: block;
67
+ margin-bottom: 8px;
68
+ color: #333;
69
+ font-weight: 500;
70
+ font-size: 0.9rem;
71
+ }
72
+
73
+ .form-group input {
74
+ width: 100%;
75
+ padding: 12px 16px;
76
+ border: 2px solid #e1e5e9;
77
+ border-radius: 8px;
78
+ font-size: 1rem;
79
+ transition: all 0.3s ease;
80
+ background: white;
81
+ }
82
+
83
+ .form-group input:focus {
84
+ outline: none;
85
+ border-color: #667eea;
86
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
87
+ }
88
+
89
+ .login-btn {
90
+ width: 100%;
91
+ padding: 14px;
92
+ background: #667eea;
93
+ color: white;
94
+ border: none;
95
+ border-radius: 8px;
96
+ font-size: 1rem;
97
+ font-weight: 600;
98
+ cursor: pointer;
99
+ transition: all 0.3s ease;
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ gap: 8px;
104
+ }
105
+
106
+ .login-btn:hover {
107
+ background: #5a67d8;
108
+ transform: translateY(-1px);
109
+ }
110
+
111
+ .login-btn:active {
112
+ transform: translateY(0);
113
+ }
114
+
115
+ .login-btn:disabled {
116
+ background: #cbd5e0;
117
+ cursor: not-allowed;
118
+ transform: none;
119
+ }
120
+
121
+ .error-message {
122
+ background: #fed7d7;
123
+ color: #c53030;
124
+ padding: 12px;
125
+ border-radius: 8px;
126
+ margin-bottom: 20px;
127
+ font-size: 0.9rem;
128
+ display: none;
129
+ }
130
+
131
+ .loading {
132
+ opacity: 0.7;
133
+ pointer-events: none;
134
+ }
135
+
136
+ .spinner {
137
+ animation: spin 1s linear infinite;
138
+ }
139
+
140
+ @keyframes spin {
141
+ from { transform: rotate(0deg); }
142
+ to { transform: rotate(360deg); }
143
+ }
144
+ </style>
145
+ </head>
146
+ <body>
147
+ <div class="login-container">
148
+ <div class="logo">
149
+ <i class="fas fa-robot"></i>
150
+ <h1>CatGPT Admin</h1>
151
+ <p>Please login to access the model manager</p>
152
+ </div>
153
+
154
+ <div id="errorMessage" class="error-message"></div>
155
+
156
+ <form class="login-form" id="loginForm">
157
+ <div class="form-group">
158
+ <label for="adminKey">Admin Key</label>
159
+ <input
160
+ type="password"
161
+ id="adminKey"
162
+ name="adminKey"
163
+ placeholder="Enter your admin key"
164
+ required
165
+ autocomplete="current-password"
166
+ >
167
+ </div>
168
+
169
+ <button type="submit" class="login-btn" id="loginBtn">
170
+ <i class="fas fa-sign-in-alt"></i>
171
+ <span>Login</span>
172
+ </button>
173
+ </form>
174
+ </div>
175
+
176
+ <script>
177
+ const loginForm = document.getElementById('loginForm');
178
+ const adminKeyInput = document.getElementById('adminKey');
179
+ const loginBtn = document.getElementById('loginBtn');
180
+ const errorMessage = document.getElementById('errorMessage');
181
+
182
+ // Focus on the input field when page loads
183
+ adminKeyInput.focus();
184
+
185
+ // Handle form submission
186
+ loginForm.addEventListener('submit', async (e) => {
187
+ e.preventDefault();
188
+
189
+ const adminKey = adminKeyInput.value.trim();
190
+
191
+ if (!adminKey) {
192
+ showError('Please enter your admin key');
193
+ return;
194
+ }
195
+
196
+ // Show loading state
197
+ setLoading(true);
198
+ hideError();
199
+
200
+ try {
201
+ const response = await fetch('/api/login', {
202
+ method: 'POST',
203
+ headers: {
204
+ 'Content-Type': 'application/json',
205
+ },
206
+ body: JSON.stringify({ admin_key: adminKey })
207
+ });
208
+
209
+ if (response.ok) {
210
+ // Success - redirect to main app
211
+ window.location.href = '/';
212
+ } else {
213
+ const error = await response.json();
214
+ showError(error.detail || 'Invalid admin key');
215
+ }
216
+ } catch (error) {
217
+ console.error('Login error:', error);
218
+ showError('Network error. Please try again.');
219
+ } finally {
220
+ setLoading(false);
221
+ }
222
+ });
223
+
224
+ function showError(message) {
225
+ errorMessage.textContent = message;
226
+ errorMessage.style.display = 'block';
227
+ }
228
+
229
+ function hideError() {
230
+ errorMessage.style.display = 'none';
231
+ }
232
+
233
+ function setLoading(loading) {
234
+ if (loading) {
235
+ loginBtn.disabled = true;
236
+ loginBtn.classList.add('loading');
237
+ loginBtn.innerHTML = '<i class="fas fa-spinner spinner"></i><span>Logging in...</span>';
238
+ } else {
239
+ loginBtn.disabled = false;
240
+ loginBtn.classList.remove('loading');
241
+ loginBtn.innerHTML = '<i class="fas fa-sign-in-alt"></i><span>Login</span>';
242
+ }
243
+ }
244
+
245
+ // Handle Enter key
246
+ adminKeyInput.addEventListener('keypress', (e) => {
247
+ if (e.key === 'Enter') {
248
+ loginForm.dispatchEvent(new Event('submit'));
249
+ }
250
+ });
251
+ </script>
252
+ </body>
253
+ </html>
static/app.js CHANGED
@@ -5,14 +5,22 @@ class ModelManager {
5
  this.models = {};
6
  this.editingModel = null;
7
  this.imageCache = new Map(); // Cache for model images
 
8
 
9
  this.init();
10
  }
11
 
12
- init() {
13
  this.initTheme();
14
  this.bindEvents();
15
- this.loadAllModels();
 
 
 
 
 
 
 
16
  }
17
 
18
  bindEvents() {
@@ -56,6 +64,47 @@ class ModelManager {
56
  document.getElementById('modalCancel').addEventListener('click', () => {
57
  this.closeModal();
58
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  document.getElementById('modalSave').addEventListener('click', () => {
61
  this.saveModelEdit();
@@ -1282,6 +1331,71 @@ class ModelManager {
1282
  themeIcon.parentElement.title = 'Switch to dark mode';
1283
  }
1284
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1285
  }
1286
 
1287
  // Initialize the application
 
5
  this.models = {};
6
  this.editingModel = null;
7
  this.imageCache = new Map(); // Cache for model images
8
+ this.isAuthenticated = false;
9
 
10
  this.init();
11
  }
12
 
13
+ async init() {
14
  this.initTheme();
15
  this.bindEvents();
16
+ await this.checkAuthStatus();
17
+
18
+ if (this.isAuthenticated) {
19
+ this.loadAllModels();
20
+ } else {
21
+ // Redirect to login page if not authenticated
22
+ window.location.href = '/login';
23
+ }
24
  }
25
 
26
  bindEvents() {
 
64
  document.getElementById('modalCancel').addEventListener('click', () => {
65
  this.closeModal();
66
  });
67
+
68
+ // Login form events
69
+ document.getElementById('loginSubmit').addEventListener('click', async () => {
70
+ const adminKey = document.getElementById('adminKey').value;
71
+ const loginError = document.getElementById('loginError');
72
+
73
+ if (!adminKey) {
74
+ loginError.textContent = 'Please enter admin key';
75
+ loginError.style.display = 'block';
76
+ return;
77
+ }
78
+
79
+ const result = await this.login(adminKey);
80
+ if (!result.success) {
81
+ loginError.textContent = result.message;
82
+ loginError.style.display = 'block';
83
+ } else {
84
+ loginError.style.display = 'none';
85
+ document.getElementById('adminKey').value = '';
86
+ }
87
+ });
88
+
89
+ // Handle Enter key in login form
90
+ document.getElementById('adminKey').addEventListener('keypress', (e) => {
91
+ if (e.key === 'Enter') {
92
+ document.getElementById('loginSubmit').click();
93
+ }
94
+ });
95
+
96
+
97
+ // Login modal close events
98
+ document.getElementById('loginModalClose').addEventListener('click', () => {
99
+ this.hideLoginModal();
100
+ });
101
+
102
+ // Close login modal when clicking outside
103
+ document.getElementById('loginModal').addEventListener('click', (e) => {
104
+ if (e.target === document.getElementById('loginModal')) {
105
+ this.hideLoginModal();
106
+ }
107
+ });
108
 
109
  document.getElementById('modalSave').addEventListener('click', () => {
110
  this.saveModelEdit();
 
1331
  themeIcon.parentElement.title = 'Switch to dark mode';
1332
  }
1333
  }
1334
+
1335
+ // Authentication Methods
1336
+ async checkAuthStatus() {
1337
+ try {
1338
+ const response = await fetch('/api/auth-status');
1339
+ const data = await response.json();
1340
+ this.isAuthenticated = data.authenticated;
1341
+ } catch (error) {
1342
+ console.error('Auth check failed:', error);
1343
+ this.isAuthenticated = false;
1344
+ }
1345
+ }
1346
+
1347
+
1348
+ async login(adminKey) {
1349
+ try {
1350
+ const response = await fetch('/api/login', {
1351
+ method: 'POST',
1352
+ headers: {
1353
+ 'Content-Type': 'application/json',
1354
+ },
1355
+ body: JSON.stringify({ admin_key: adminKey })
1356
+ });
1357
+
1358
+ if (response.ok) {
1359
+ this.isAuthenticated = true;
1360
+ this.hideAuthOverlay();
1361
+ this.hideLoginModal();
1362
+ this.loadAllModels();
1363
+ return { success: true };
1364
+ } else {
1365
+ const error = await response.json();
1366
+ return { success: false, message: error.detail || 'Login failed' };
1367
+ }
1368
+ } catch (error) {
1369
+ console.error('Login error:', error);
1370
+ return { success: false, message: 'Network error' };
1371
+ }
1372
+ }
1373
+
1374
+ async logout() {
1375
+ try {
1376
+ await fetch('/api/logout', { method: 'POST' });
1377
+ this.isAuthenticated = false;
1378
+ // Redirect to login page
1379
+ window.location.href = '/login';
1380
+ } catch (error) {
1381
+ console.error('Logout error:', error);
1382
+ }
1383
+ }
1384
+
1385
+ showLoginModal() {
1386
+ document.getElementById('loginModal').classList.add('active');
1387
+ // Clear any previous errors
1388
+ document.getElementById('loginError').style.display = 'none';
1389
+ document.getElementById('adminKey').value = '';
1390
+ // Auto-focus the admin key input
1391
+ setTimeout(() => {
1392
+ document.getElementById('adminKey').focus();
1393
+ }, 100);
1394
+ }
1395
+
1396
+ hideLoginModal() {
1397
+ document.getElementById('loginModal').classList.remove('active');
1398
+ }
1399
  }
1400
 
1401
  // Initialize the application
static/styles.css CHANGED
@@ -882,6 +882,97 @@ body {
882
  font-weight: 500;
883
  }
884
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
885
  /* Model Content */
886
  .model-content {
887
  padding: 1rem;
 
882
  font-weight: 500;
883
  }
884
 
885
+ /* Authentication Styles */
886
+ .auth-overlay {
887
+ position: fixed;
888
+ top: 0;
889
+ left: 0;
890
+ right: 0;
891
+ bottom: 0;
892
+ background: var(--background);
893
+ display: none;
894
+ align-items: center;
895
+ justify-content: center;
896
+ z-index: 9999;
897
+ }
898
+
899
+ .auth-overlay.active {
900
+ display: flex;
901
+ }
902
+
903
+ .auth-content {
904
+ text-align: center;
905
+ padding: 2rem;
906
+ max-width: 400px;
907
+ width: 100%;
908
+ }
909
+
910
+ .auth-logo {
911
+ margin-bottom: 2rem;
912
+ }
913
+
914
+ .auth-logo i {
915
+ font-size: 4rem;
916
+ color: var(--primary-color);
917
+ margin-bottom: 1rem;
918
+ }
919
+
920
+ .auth-logo h2 {
921
+ color: var(--text-primary);
922
+ margin-bottom: 0.5rem;
923
+ font-size: 1.5rem;
924
+ }
925
+
926
+ .auth-logo p {
927
+ color: var(--text-secondary);
928
+ margin-bottom: 2rem;
929
+ line-height: 1.5;
930
+ }
931
+
932
+ .error-message {
933
+ background: rgba(239, 68, 68, 0.1);
934
+ color: var(--danger-color);
935
+ padding: 8px 12px;
936
+ border-radius: var(--radius);
937
+ font-size: 0.875rem;
938
+ margin-top: 8px;
939
+ }
940
+
941
+ /* Login Modal Styles */
942
+ #loginModal {
943
+ display: none;
944
+ }
945
+
946
+ #loginModal.active {
947
+ display: flex;
948
+ }
949
+
950
+ #loginModal .modal-header h3 {
951
+ display: flex;
952
+ align-items: center;
953
+ gap: 8px;
954
+ }
955
+
956
+ #loginModal .form-group input {
957
+ width: 100%;
958
+ }
959
+
960
+ /* Logout Button */
961
+ .logout-btn {
962
+ background: none;
963
+ border: none;
964
+ color: var(--text-secondary);
965
+ cursor: pointer;
966
+ padding: 8px;
967
+ border-radius: var(--radius);
968
+ transition: all 0.2s ease;
969
+ }
970
+
971
+ .logout-btn:hover {
972
+ background: var(--surface-variant);
973
+ color: var(--text-primary);
974
+ }
975
+
976
  /* Model Content */
977
  .model-content {
978
  padding: 1rem;