Spaces:
XCAPI
/
Sleeping

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

Update admin.html

Browse files
Files changed (1) hide show
  1. admin.html +307 -249
admin.html CHANGED
@@ -1,249 +1,307 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>代理端点管理</title>
5
- <style>
6
- body {
7
- font-family: Arial, sans-serif;
8
- max-width: 1000px;
9
- margin: 20px auto;
10
- padding: 0 20px;
11
- }
12
- .endpoint-group {
13
- margin: 20px 0;
14
- padding: 15px;
15
- border: 1px solid #ddd;
16
- border-radius: 8px;
17
- }
18
- .endpoint {
19
- display: flex;
20
- align-items: center;
21
- margin: 10px 0;
22
- padding: 10px;
23
- border: 1px solid #eee;
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);
44
- }
45
- button {
46
- padding: 8px 15px;
47
- margin: 5px;
48
- cursor: pointer;
49
- border: none;
50
- border-radius: 4px;
51
- background-color: #4CAF50;
52
- color: white;
53
- }
54
- button:hover {
55
- background-color: #45a049;
56
- }
57
- button.delete {
58
- background-color: #f44336;
59
- }
60
- button.delete:hover {
61
- background-color: #da190b;
62
- }
63
- .status {
64
- position: fixed;
65
- top: 20px;
66
- right: 20px;
67
- padding: 15px;
68
- border-radius: 4px;
69
- display: none;
70
- z-index: 1000;
71
- }
72
- .success {
73
- background-color: #4CAF50;
74
- color: white;
75
- }
76
- .error {
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="controls">
110
- <button onclick="saveConfig()">保存所有配置</button>
111
- <button onclick="logout()" style="background-color: #f44336;">退出登录</button>
112
- </div>
113
-
114
- <div id="status" class="status"></div>
115
-
116
- <script>
117
- // 获取存储的密钥
118
- let apiKey = localStorage.getItem('apiKey');
119
-
120
- // 如果没有密钥,提示输入
121
- if (!apiKey) {
122
- apiKey = prompt('请输入访问密钥:');
123
- if (apiKey) {
124
- localStorage.setItem('apiKey', apiKey);
125
- } else {
126
- window.location.href = '/';
127
- }
128
- }
129
-
130
- // 显示状态信息
131
- function showStatus(message, isError = false) {
132
- const status = document.getElementById('status');
133
- status.textContent = message;
134
- status.className = 'status ' + (isError ? 'error' : 'success');
135
- status.style.display = 'block';
136
- setTimeout(() => status.style.display = 'none', 3000);
137
- }
138
-
139
- // 添加端点
140
- function addEndpoint(type, url = '', weight = 1, enabled = true) {
141
- const container = document.getElementById(type + 'Endpoints');
142
- const div = document.createElement('div');
143
- div.className = 'endpoint';
144
- div.innerHTML = `
145
- <input type="text" placeholder="输入端点URL" value="${url}">
146
- <input type="number" placeholder="权重" value="${weight}" min="1">
147
- <label>
148
- <input type="checkbox" ${enabled ? 'checked' : ''}>
149
- 启用
150
- </label>
151
- <button class="delete" onclick="this.parentElement.remove()">删除</button>
152
- `;
153
- container.appendChild(div);
154
- }
155
-
156
- // 获取端点配置
157
- function getEndpointsConfig(type) {
158
- const endpoints = [];
159
- document.querySelectorAll(`#${type}Endpoints .endpoint`).forEach(el => {
160
- endpoints.push({
161
- url: el.querySelector('input[type="text"]').value.trim(),
162
- weight: parseInt(el.querySelector('input[type="number"]').value) || 1,
163
- enabled: el.querySelector('input[type="checkbox"]').checked
164
- });
165
- });
166
- return endpoints;
167
- }
168
-
169
- // 带认证的fetch
170
- async function fetchWithAuth(url, options = {}) {
171
- const headers = {
172
- ...options.headers,
173
- 'Authorization': apiKey
174
- };
175
-
176
- const response = await fetch(url, { ...options, headers });
177
-
178
- if (response.status === 401) {
179
- localStorage.removeItem('apiKey');
180
- window.location.reload();
181
- return null;
182
- }
183
-
184
- return response;
185
- }
186
-
187
- // 保存配置
188
- async function saveConfig() {
189
- const config = {
190
- models: getEndpointsConfig('models'),
191
- chat: getEndpointsConfig('chat')
192
- };
193
-
194
- try {
195
- const response = await fetchWithAuth('/admin/config', {
196
- method: 'POST',
197
- headers: {'Content-Type': 'application/json'},
198
- body: JSON.stringify(config)
199
- });
200
-
201
- if (!response) return;
202
-
203
- if (response.ok) {
204
- showStatus('配置已保存');
205
- } else {
206
- showStatus('保存失败: ' + await response.text(), true);
207
- }
208
- } catch (error) {
209
- showStatus('保存失败: ' + error.message, true);
210
- }
211
- }
212
-
213
- // 加载配置
214
- async function loadConfig() {
215
- try {
216
- const response = await fetchWithAuth('/admin/config');
217
- if (!response) return;
218
-
219
- const config = await response.json();
220
-
221
- // 清空现有配置
222
- document.getElementById('modelsEndpoints').innerHTML = '';
223
- document.getElementById('chatEndpoints').innerHTML = '';
224
-
225
- // 加载Models端点
226
- config.models.forEach(ep => {
227
- addEndpoint('models', ep.url, ep.weight, ep.enabled);
228
- });
229
-
230
- // 加载Chat端点
231
- config.chat.forEach(ep => {
232
- addEndpoint('chat', ep.url, ep.weight, ep.enabled);
233
- });
234
- } catch (error) {
235
- showStatus('加载配置失败: ' + error.message, true);
236
- }
237
- }
238
-
239
- // 退出登录
240
- function logout() {
241
- localStorage.removeItem('apiKey');
242
- window.location.reload();
243
- }
244
-
245
- // 页面加载时获取配置
246
- document.addEventListener('DOMContentLoaded', loadConfig);
247
- </script>
248
- </body>
249
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>代理端点管理</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ max-width: 1000px;
9
+ margin: 20px auto;
10
+ padding: 0 20px;
11
+ }
12
+ .endpoint-group {
13
+ margin: 20px 0;
14
+ padding: 15px;
15
+ border: 1px solid #ddd;
16
+ border-radius: 8px;
17
+ }
18
+ .endpoint {
19
+ display: flex;
20
+ align-items: center;
21
+ margin: 10px 0;
22
+ padding: 10px;
23
+ border: 1px solid #eee;
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);
44
+ }
45
+ button {
46
+ padding: 8px 15px;
47
+ margin: 5px;
48
+ cursor: pointer;
49
+ border: none;
50
+ border-radius: 4px;
51
+ background-color: #4CAF50;
52
+ color: white;
53
+ }
54
+ button:hover {
55
+ background-color: #45a049;
56
+ }
57
+ button.delete {
58
+ background-color: #f44336;
59
+ }
60
+ button.delete:hover {
61
+ background-color: #da190b;
62
+ }
63
+ .status {
64
+ position: fixed;
65
+ top: 20px;
66
+ right: 20px;
67
+ padding: 15px;
68
+ border-radius: 4px;
69
+ display: none;
70
+ z-index: 1000;
71
+ }
72
+ .success {
73
+ background-color: #4CAF50;
74
+ color: white;
75
+ }
76
+ .error {
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>
153
+ <input type="checkbox" ${enabled ? 'checked' : ''}>
154
+ 启用
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>