Spaces:
XCAPI
/
Sleeping

XCAPI commited on
Commit
d4972f2
·
verified ·
1 Parent(s): 43f7389

Update admin.html

Browse files
Files changed (1) hide show
  1. admin.html +151 -189
admin.html CHANGED
@@ -24,20 +24,13 @@
24
  border-radius: 4px;
25
  background-color: #f9f9f9;
26
  }
27
- .endpoint input[type="text"] {
28
  flex: 1;
29
  margin-right: 10px;
30
  padding: 8px;
31
  border: 1px solid #ddd;
32
  border-radius: 4px;
33
  }
34
- .endpoint input[type="number"] {
35
- width: 80px;
36
- margin: 0 10px;
37
- padding: 8px;
38
- border: 1px solid #ddd;
39
- border-radius: 4px;
40
- }
41
  .endpoint input[type="checkbox"] {
42
  margin: 0 10px;
43
  transform: scale(1.2);
@@ -77,76 +70,81 @@
77
  background-color: #f44336;
78
  color: white;
79
  }
80
- h2 {
81
- color: #333;
82
- border-bottom: 2px solid #4CAF50;
83
- padding-bottom: 10px;
84
- }
85
- .controls {
86
- margin: 20px 0;
87
- padding: 10px;
88
- background-color: #f5f5f5;
89
- border-radius: 4px;
90
- text-align: right;
91
  }
92
  </style>
93
  </head>
94
  <body>
95
- <h1>代理端点管理</h1>
96
-
97
- <div class="endpoint-group">
98
- <h2>Models 端点</h2>
99
- <div id="modelsEndpoints"></div>
100
- <button onclick="addEndpoint('models')">添加 Models 端点</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  </div>
102
-
103
- <div class="endpoint-group">
104
- <h2>Chat 端点</h2>
105
- <div id="chatEndpoints"></div>
106
- <button onclick="addEndpoint('chat')">添加 Chat 端点</button>
 
 
 
 
107
  </div>
 
108
 
109
- <div class="endpoint-group">
110
- <h2>IP 黑名单</h2>
111
- <div class="endpoint">
112
- <input type="text" id="ipInput" placeholder="输入要封禁的IP地址">
113
- <button onclick="addToBlacklist(document.getElementById('ipInput').value)">添加到黑名单</button>
114
- </div>
115
- <div id="blacklistEntries"></div>
116
- </div>
117
 
118
- <div class="controls">
119
- <button onclick="saveConfig()">保存所有配置</button>
120
- <button onclick="logout()" style="background-color: #f44336;">退出登录</button>
121
- </div>
122
 
123
- <div id="status" class="status"></div>
124
 
125
- <script>
126
- let apiKey = localStorage.getItem('apiKey');
127
 
128
- if (!apiKey) {
129
- apiKey = prompt('请输入访问密钥:');
130
- if (apiKey) {
131
- localStorage.setItem('apiKey', apiKey);
132
- } else {
133
- window.location.href = '/';
134
- }
135
- }
136
-
137
- function showStatus(message, isError = false) {
138
- const status = document.getElementById('status');
139
- status.textContent = message;
140
- status.className = 'status ' + (isError ? 'error' : 'success');
141
- status.style.display = 'block';
142
- setTimeout(() => status.style.display = 'none', 3000);
143
  }
144
-
145
- function addEndpoint(type, url = '', weight = 1, enabled = true) {
146
- const container = document.getElementById(type + 'Endpoints');
147
- const div = document.createElement('div');
148
- div.className = 'endpoint';
149
- div.innerHTML = `
 
 
 
 
 
 
 
 
 
150
  <input type="text" placeholder="输入端点URL" value="${url}">
151
  <input type="number" placeholder="权重" value="${weight}" min="1">
152
  <label>
@@ -155,153 +153,117 @@
155
  </label>
156
  <button class="delete" onclick="this.parentElement.remove()">删除</button>
157
  `;
158
- container.appendChild(div);
159
- }
160
 
161
- function getEndpointsConfig(type) {
162
- const endpoints = [];
163
- document.querySelectorAll(`#${type}Endpoints .endpoint`).forEach(el => {
164
- endpoints.push({
165
- url: el.querySelector('input[type="text"]').value.trim(),
166
- weight: parseInt(el.querySelector('input[type="number"]').value) || 1,
167
- enabled: el.querySelector('input[type="checkbox"]').checked
168
- });
169
- });
170
- return endpoints;
171
- }
172
 
173
- async function fetchWithAuth(url, options = {}) {
174
- const headers = {
175
- ...options.headers,
176
- 'Authorization': apiKey
177
- };
178
 
179
- const response = await fetch(url, { ...options, headers });
 
 
 
 
180
 
181
- if (response.status === 401) {
182
- localStorage.removeItem('apiKey');
183
- window.location.reload();
184
- return null;
185
- }
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- return response;
188
- }
189
 
190
- async function saveConfig() {
191
- const config = {
192
- models: getEndpointsConfig('models'),
193
- chat: getEndpointsConfig('chat')
194
- };
195
-
196
- try {
197
- const response = await fetchWithAuth('/admin/config', {
198
- method: 'POST',
199
- headers: {'Content-Type': 'application/json'},
200
- body: JSON.stringify(config)
201
- });
202
-
203
- if (!response) return;
204
-
205
- if (response.ok) {
206
- showStatus('配置已保存');
207
- } else {
208
- showStatus('保存失败: ' + await response.text(), true);
209
- }
210
- } catch (error) {
211
- showStatus('保存失败: ' + error.message, true);
212
  }
 
 
213
  }
 
214
 
215
- async function loadConfig() {
216
- try {
217
- const response = await fetchWithAuth('/admin/config');
218
- if (!response) return;
219
 
220
- const config = await response.json();
221
 
222
- document.getElementById('modelsEndpoints').innerHTML = '';
223
- document.getElementById('chatEndpoints').innerHTML = '';
224
 
225
- config.models.forEach(ep => {
226
- addEndpoint('models', ep.url, ep.weight, ep.enabled);
227
- });
228
 
229
- config.chat.forEach(ep => {
230
- addEndpoint('chat', ep.url, ep.weight, ep.enabled);
231
- });
232
 
233
- // 加载黑名单
234
- loadBlacklist();
235
- } catch (error) {
236
- showStatus('加载配置失败: ' + error.message, true);
237
- }
238
- }
239
 
240
- async function loadBlacklist() {
241
- try {
242
- const response = await fetchWithAuth('/admin/blacklist');
243
- const blacklist = await response.json();
244
-
245
- const container = document.getElementById('blacklistEntries');
246
- container.innerHTML = '';
247
-
248
- blacklist.forEach(ip => {
249
- const div = document.createElement('div');
250
- div.className = 'endpoint';
251
- div.innerHTML = `
252
- <span>${ip}</span>
253
- <button class="delete" onclick="removeFromBlacklist('${ip}')">解除封禁</button>
254
- `;
255
- container.appendChild(div);
256
- });
257
- } catch (error) {
258
- showStatus('加载黑名单失败: ' + error.message, true);
259
- }
260
  }
 
261
 
262
- async function addToBlacklist(ip) {
263
- if (!ip) return;
 
264
 
265
- try {
266
- const response = await fetchWithAuth('/admin/blacklist', {
267
- method: 'POST',
268
- headers: {'Content-Type': 'application/json'},
269
- body: JSON.stringify({ip: ip, action: 'add'})
270
- });
271
 
272
- if (response.ok) {
273
- showStatus('IP已添加到黑名单');
274
- document.getElementById('ipInput').value = '';
275
- loadBlacklist();
276
- }
277
- } catch (error) {
278
- showStatus('添加失败: ' + error.message, true);
279
- }
280
- }
281
 
282
- async function removeFromBlacklist(ip) {
283
- try {
284
- const response = await fetchWithAuth('/admin/blacklist', {
285
- method: 'POST',
286
- headers: {'Content-Type': 'application/json'},
287
- body: JSON.stringify({ip: ip, action: 'remove'})
288
- });
289
-
290
- if (response.ok) {
291
- showStatus('IP已从黑名单移除');
292
- loadBlacklist();
293
  }
294
- } catch (error) {
295
- showStatus('移除失败: ' + error.message, true);
296
  }
297
- }
298
 
299
- function logout() {
300
- localStorage.removeItem('apiKey');
301
- window.location.reload();
 
 
 
 
 
302
  }
303
 
304
- document.addEventListener('DOMContentLoaded', loadConfig);
305
- </script>
 
 
 
 
 
306
  </body>
307
  </html>
 
24
  border-radius: 4px;
25
  background-color: #f9f9f9;
26
  }
27
+ .endpoint input[type="text"], .endpoint input[type="number"] {
28
  flex: 1;
29
  margin-right: 10px;
30
  padding: 8px;
31
  border: 1px solid #ddd;
32
  border-radius: 4px;
33
  }
 
 
 
 
 
 
 
34
  .endpoint input[type="checkbox"] {
35
  margin: 0 10px;
36
  transform: scale(1.2);
 
70
  background-color: #f44336;
71
  color: white;
72
  }
73
+ .warning {
74
+ color: #ff9800;
 
 
 
 
 
 
 
 
 
75
  }
76
  </style>
77
  </head>
78
  <body>
79
+ <h1>代理端点管理</h1>
80
+
81
+ <div class="endpoint-group">
82
+ <h2>Models 端点</h2>
83
+ <div id="modelsEndpoints"></div>
84
+ <button onclick="addEndpoint('models')">添加 Models 端点</button>
85
+ </div>
86
+
87
+ <div class="endpoint-group">
88
+ <h2>Chat 端点</h2>
89
+ <div id="chatEndpoints"></div>
90
+ <button onclick="addEndpoint('chat')">添加 Chat 端点</button>
91
+ </div>
92
+
93
+ <div class="endpoint-group">
94
+ <h2>IP 黑名单</h2>
95
+ <div class="endpoint">
96
+ <input type="text" id="ipInput" placeholder="输入要封禁的IP地址">
97
+ <button onclick="addToBlacklist(document.getElementById('ipInput').value)">添加到黑名单</button>
98
  </div>
99
+ <div id="blacklistEntries"></div>
100
+ </div>
101
+
102
+ <div class="endpoint-group">
103
+ <h2>全局设置</h2>
104
+ <div class="endpoint">
105
+ <label>冷却时间(秒):
106
+ <input type="number" id="cooldownTime" min="0" step="1" value="180">
107
+ </label>
108
  </div>
109
+ </div>
110
 
111
+ <div class="endpoint-group">
112
+ <h2>端点状态</h2>
113
+ <div id="endpointStatus"></div>
114
+ </div>
 
 
 
 
115
 
116
+ <div class="controls">
117
+ <button onclick="saveConfig()">保存所有配置</button>
118
+ <button onclick="logout()" style="background-color: #f44336;">退出登录</button>
119
+ </div>
120
 
121
+ <div id="status" class="status"></div>
122
 
123
+ <script>
124
+ let apiKey = localStorage.getItem('apiKey');
125
 
126
+ if (!apiKey) {
127
+ apiKey = prompt('请输入访问密钥:');
128
+ if (apiKey) {
129
+ localStorage.setItem('apiKey', apiKey);
130
+ } else {
131
+ window.location.href = '/';
 
 
 
 
 
 
 
 
 
132
  }
133
+ }
134
+
135
+ function showStatus(message, isError = false) {
136
+ const status = document.getElementById('status');
137
+ status.textContent = message;
138
+ status.className = 'status ' + (isError ? 'error' : 'success');
139
+ status.style.display = 'block';
140
+ setTimeout(() => status.style.display = 'none', 3000);
141
+ }
142
+
143
+ function addEndpoint(type, url = '', weight = 1, enabled = true) {
144
+ const container = document.getElementById(type + 'Endpoints');
145
+ const div = document.createElement('div');
146
+ div.className = 'endpoint';
147
+ div.innerHTML = `
148
  <input type="text" placeholder="输入端点URL" value="${url}">
149
  <input type="number" placeholder="权重" value="${weight}" min="1">
150
  <label>
 
153
  </label>
154
  <button class="delete" onclick="this.parentElement.remove()">删除</button>
155
  `;
156
+ container.appendChild(div);
157
+ }
158
 
159
+ async function fetchWithAuth(url, options = {}) {
160
+ const headers = {
161
+ ...options.headers,
162
+ 'Authorization': apiKey
163
+ };
 
 
 
 
 
 
164
 
165
+ const response = await fetch(url, { ...options, headers });
 
 
 
 
166
 
167
+ if (response.status === 401) {
168
+ localStorage.removeItem('apiKey');
169
+ window.location.reload();
170
+ return null;
171
+ }
172
 
173
+ return response;
174
+ }
175
+
176
+ async function saveConfig() {
177
+ const config = {
178
+ models: getEndpointsConfig('models'),
179
+ chat: getEndpointsConfig('chat'),
180
+ cooldownTime: parseFloat(document.getElementById('cooldownTime').value) || 180
181
+ };
182
+
183
+ try {
184
+ const response = await fetchWithAuth('/admin/config', {
185
+ method: 'POST',
186
+ headers: {'Content-Type': 'application/json'},
187
+ body: JSON.stringify(config)
188
+ });
189
 
190
+ if (!response) return;
 
191
 
192
+ if (response.ok) {
193
+ showStatus('配置已保存');
194
+ await loadConfig(); // 重新加载以更新状态
195
+ } else {
196
+ showStatus('保存失败: ' + await response.text(), true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
+ } catch (error) {
199
+ showStatus('保存失败: ' + error.message, true);
200
  }
201
+ }
202
 
203
+ async function loadConfig() {
204
+ try {
205
+ const response = await fetchWithAuth('/admin/config');
206
+ if (!response) return;
207
 
208
+ const config = await response.json();
209
 
210
+ document.getElementById('modelsEndpoints').innerHTML = '';
211
+ document.getElementById('chatEndpoints').innerHTML = '';
212
 
213
+ config.models.forEach(ep => {
214
+ addEndpoint('models', ep.url, ep.weight, ep.enabled);
215
+ });
216
 
217
+ config.chat.forEach(ep => {
218
+ addEndpoint('chat', ep.url, ep.weight, ep.enabled);
219
+ });
220
 
221
+ document.getElementById('cooldownTime').value = config.cooldownTime || 180;
 
 
 
 
 
222
 
223
+ // 更新端点状态显示
224
+ updateEndpointStatus(config);
225
+ } catch (error) {
226
+ showStatus('加载配置失败: ' + error.message, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
+ }
229
 
230
+ function updateEndpointStatus(config) {
231
+ const statusDiv = document.getElementById('endpointStatus');
232
+ statusDiv.innerHTML = '';
233
 
234
+ function addStatusEntry(endpoint, type) {
235
+ const div = document.createElement('div');
236
+ div.className = 'endpoint';
 
 
 
237
 
238
+ let status = endpoint.enabled ? '启用' : '禁用';
239
+ let statusClass = endpoint.enabled ? 'success' : 'error';
240
+
241
+ if (endpoint.cooldown) {
242
+ const remainingTime = new Date(endpoint.cooldown.StartTime).getTime() +
243
+ (endpoint.cooldown.Duration * 1000) - Date.now();
 
 
 
244
 
245
+ if (remainingTime > 0) {
246
+ status = `冷却中 (${Math.ceil(remainingTime/1000)}秒)`;
247
+ statusClass = 'warning';
 
 
 
 
 
 
 
 
248
  }
 
 
249
  }
 
250
 
251
+ div.innerHTML = `
252
+ <span>${type}: ${endpoint.url}</span>
253
+ <span class="${statusClass}">${status}</span>
254
+ ${endpoint.cooldown ?
255
+ `<span class="error">原因: ${endpoint.cooldown.Reason}</span>` : ''}
256
+ `;
257
+
258
+ statusDiv.appendChild(div);
259
  }
260
 
261
+ config.models.forEach(ep => addStatusEntry(ep, 'Models'));
262
+ config.chat.forEach(ep => addStatusEntry(ep, 'Chat'));
263
+ }
264
+
265
+ // 定期更新状态
266
+ //setInterval(() => loadConfig(), 5000);
267
+ </script>
268
  </body>
269
  </html>