| <!DOCTYPE html>
|
| <html>
|
| <head>
|
| <title>代理端点管理</title>
|
| <style>
|
| body {
|
| font-family: Arial, sans-serif;
|
| max-width: 1000px;
|
| margin: 20px auto;
|
| padding: 0 20px;
|
| }
|
| .endpoint-group {
|
| margin: 20px 0;
|
| padding: 15px;
|
| border: 1px solid #ddd;
|
| border-radius: 8px;
|
| }
|
| .endpoint {
|
| display: flex;
|
| align-items: center;
|
| margin: 10px 0;
|
| padding: 10px;
|
| border: 1px solid #eee;
|
| border-radius: 4px;
|
| background-color: #f9f9f9;
|
| }
|
| .endpoint input[type="text"] {
|
| flex: 1;
|
| margin-right: 10px;
|
| padding: 8px;
|
| border: 1px solid #ddd;
|
| border-radius: 4px;
|
| }
|
| .endpoint input[type="number"] {
|
| width: 80px;
|
| margin: 0 10px;
|
| padding: 8px;
|
| border: 1px solid #ddd;
|
| border-radius: 4px;
|
| }
|
| .endpoint input[type="checkbox"] {
|
| margin: 0 10px;
|
| transform: scale(1.2);
|
| }
|
| button {
|
| padding: 8px 15px;
|
| margin: 5px;
|
| cursor: pointer;
|
| border: none;
|
| border-radius: 4px;
|
| background-color: #4CAF50;
|
| color: white;
|
| }
|
| button:hover {
|
| background-color: #45a049;
|
| }
|
| button.delete {
|
| background-color: #f44336;
|
| }
|
| button.delete:hover {
|
| background-color: #da190b;
|
| }
|
| .status {
|
| position: fixed;
|
| top: 20px;
|
| right: 20px;
|
| padding: 15px;
|
| border-radius: 4px;
|
| display: none;
|
| z-index: 1000;
|
| }
|
| .success {
|
| background-color: #4CAF50;
|
| color: white;
|
| }
|
| .error {
|
| background-color: #f44336;
|
| color: white;
|
| }
|
| h2 {
|
| color: #333;
|
| border-bottom: 2px solid #4CAF50;
|
| padding-bottom: 10px;
|
| }
|
| .controls {
|
| margin: 20px 0;
|
| padding: 10px;
|
| background-color: #f5f5f5;
|
| border-radius: 4px;
|
| text-align: right;
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <h1>代理端点管理</h1>
|
|
|
| <div class="endpoint-group">
|
| <h2>Models 端点</h2>
|
| <div id="modelsEndpoints"></div>
|
| <button onclick="addEndpoint('models')">添加 Models 端点</button>
|
| </div>
|
|
|
| <div class="endpoint-group">
|
| <h2>Chat 端点</h2>
|
| <div id="chatEndpoints"></div>
|
| <button onclick="addEndpoint('chat')">添加 Chat 端点</button>
|
| </div>
|
|
|
| <div class="controls">
|
| <button onclick="saveConfig()">保存所有配置</button>
|
| <button onclick="logout()" style="background-color: #f44336;">退出登录</button>
|
| </div>
|
|
|
| <div id="status" class="status"></div>
|
|
|
| <script>
|
|
|
| let apiKey = localStorage.getItem('apiKey');
|
|
|
|
|
| if (!apiKey) {
|
| apiKey = prompt('请输入访问密钥:');
|
| if (apiKey) {
|
| localStorage.setItem('apiKey', apiKey);
|
| } else {
|
| window.location.href = '/';
|
| }
|
| }
|
|
|
|
|
| function showStatus(message, isError = false) {
|
| const status = document.getElementById('status');
|
| status.textContent = message;
|
| status.className = 'status ' + (isError ? 'error' : 'success');
|
| status.style.display = 'block';
|
| setTimeout(() => status.style.display = 'none', 3000);
|
| }
|
|
|
|
|
| function addEndpoint(type, url = '', weight = 1, enabled = true) {
|
| const container = document.getElementById(type + 'Endpoints');
|
| const div = document.createElement('div');
|
| div.className = 'endpoint';
|
| div.innerHTML = `
|
| <input type="text" placeholder="输入端点URL" value="${url}">
|
| <input type="number" placeholder="权重" value="${weight}" min="1">
|
| <label>
|
| <input type="checkbox" ${enabled ? 'checked' : ''}>
|
| 启用
|
| </label>
|
| <button class="delete" onclick="this.parentElement.remove()">删除</button>
|
| `;
|
| container.appendChild(div);
|
| }
|
|
|
|
|
| function getEndpointsConfig(type) {
|
| const endpoints = [];
|
| document.querySelectorAll(`#${type}Endpoints .endpoint`).forEach(el => {
|
| endpoints.push({
|
| url: el.querySelector('input[type="text"]').value.trim(),
|
| weight: parseInt(el.querySelector('input[type="number"]').value) || 1,
|
| enabled: el.querySelector('input[type="checkbox"]').checked
|
| });
|
| });
|
| return endpoints;
|
| }
|
|
|
|
|
| async function fetchWithAuth(url, options = {}) {
|
| const headers = {
|
| ...options.headers,
|
| 'Authorization': apiKey
|
| };
|
|
|
| const response = await fetch(url, { ...options, headers });
|
|
|
| if (response.status === 401) {
|
| localStorage.removeItem('apiKey');
|
| window.location.reload();
|
| return null;
|
| }
|
|
|
| return response;
|
| }
|
|
|
|
|
| async function saveConfig() {
|
| const config = {
|
| models: getEndpointsConfig('models'),
|
| chat: getEndpointsConfig('chat')
|
| };
|
|
|
| try {
|
| const response = await fetchWithAuth('/admin/config', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify(config)
|
| });
|
|
|
| if (!response) return;
|
|
|
| if (response.ok) {
|
| showStatus('配置已保存');
|
| } else {
|
| showStatus('保存失败: ' + await response.text(), true);
|
| }
|
| } catch (error) {
|
| showStatus('保存失败: ' + error.message, true);
|
| }
|
| }
|
|
|
|
|
| async function loadConfig() {
|
| try {
|
| const response = await fetchWithAuth('/admin/config');
|
| if (!response) return;
|
|
|
| const config = await response.json();
|
|
|
|
|
| document.getElementById('modelsEndpoints').innerHTML = '';
|
| document.getElementById('chatEndpoints').innerHTML = '';
|
|
|
|
|
| config.models.forEach(ep => {
|
| addEndpoint('models', ep.url, ep.weight, ep.enabled);
|
| });
|
|
|
|
|
| config.chat.forEach(ep => {
|
| addEndpoint('chat', ep.url, ep.weight, ep.enabled);
|
| });
|
| } catch (error) {
|
| showStatus('加载配置失败: ' + error.message, true);
|
| }
|
| }
|
|
|
|
|
| function logout() {
|
| localStorage.removeItem('apiKey');
|
| window.location.reload();
|
| }
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', loadConfig);
|
| </script>
|
| </body>
|
| </html>
|
|
|