Spaces:
Running
Running
| {% extends "base.html" %} | |
| {% from "components/alerts.html" import api_key_status, error_alert, no_accounts_alert %} | |
| {% from "components/account_table.html" import account_table %} | |
| {% block title %}系统管理 - Gemini Business API{% endblock %} | |
| {% block extra_css %} | |
| <link rel="stylesheet" href="/static/css/admin.css"> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="container"> | |
| <div class="header"> | |
| <div class="header-info"> | |
| <h1>Gemini-Business2api</h1> | |
| <div class="subtitle">多账户代理面板</div> | |
| </div> | |
| <div class="header-actions"> | |
| <a href="/public/uptime/html" class="btn" target="_blank">📊 状态监控</a> | |
| <a href="/public/log/html" class="btn" target="_blank">📄 公开日志</a> | |
| <a href="/{{ admin_path_segment }}/log/html" class="btn" target="_blank">🔧 管理日志</a> | |
| <button class="btn" onclick="document.getElementById('fileInput').click()">📥 批量上传</button> | |
| <input type="file" id="fileInput" accept=".json" multiple style="display:none" onchange="handleFileUpload(event)"> | |
| <button class="btn" onclick="showEditConfig()" id="edit-btn">✏️ 编辑配置</button> | |
| </div> | |
| </div> | |
| <!-- Tabs Navigation --> | |
| <div class="tabs-nav"> | |
| <button class="tab-button active" onclick="switchTab('accounts')">📋 账户管理</button> | |
| <button class="tab-button" onclick="switchTab('api')">📚 API文档</button> | |
| <button class="tab-button" onclick="switchTab('config')">⚙️ 系统配置</button> | |
| <button class="tab-button" onclick="switchTab('settings')">🔧 系统设置</button> | |
| </div> | |
| <!-- Tab 1: 账户管理 --> | |
| <div id="tab-accounts" class="tab-content active"> | |
| {{ api_key_status(has_api_key) }} | |
| {{ error_alert(error_count, admin_path_segment) }} | |
| {% if multi_account_mgr.accounts|length == 0 %} | |
| {{ no_accounts_alert() }} | |
| {% endif %} | |
| <div class="alert alert-primary"> | |
| <div class="alert-icon">🔗</div> | |
| <div class="alert-content"> | |
| <strong>API 接口</strong> | |
| <div style="margin-top: 6px; color: #86868b; font-size: 11px;">根据客户端选择对应接口</div> | |
| <div style="margin-top: 10px; display: grid; gap: 10px;"> | |
| <div class="api-item"> | |
| <span class="api-item-label">基础端点</span> | |
| <div class="api-item-content" style="display: flex; gap: 8px; flex: 1;"> | |
| <code class="api-item-code">{{ api_base_url }}</code> | |
| <button class="btn-copy" onclick="navigator.clipboard.writeText('{{ api_base_url }}').then(() => { this.innerHTML = '✅'; setTimeout(() => this.innerHTML = '📋', 2000); })">📋</button> | |
| </div> | |
| </div> | |
| <div class="api-item"> | |
| <span class="api-item-label">SDK 接口</span> | |
| <div class="api-item-content" style="display: flex; gap: 8px; flex: 1;"> | |
| <code class="api-item-code">{{ api_base_v1 }}</code> | |
| <button class="btn-copy" onclick="navigator.clipboard.writeText('{{ api_base_v1 }}').then(() => { this.innerHTML = '✅'; setTimeout(() => this.innerHTML = '📋', 2000); })">📋</button> | |
| </div> | |
| </div> | |
| <div class="api-item"> | |
| <span class="api-item-label">完整接口</span> | |
| <div class="api-item-content" style="display: flex; gap: 8px; flex: 1;"> | |
| <code class="api-item-code">{{ api_endpoint }}</code> | |
| <button class="btn-copy" onclick="navigator.clipboard.writeText('{{ api_endpoint }}').then(() => { this.innerHTML = '✅'; setTimeout(() => this.innerHTML = '📋', 2000); })">📋</button> | |
| </div> | |
| </div> | |
| <div class="api-item"> | |
| <span class="api-item-label">API 密钥</span> | |
| <div class="api-item-content" style="display: flex; gap: 8px; flex: 1;"> | |
| <code class="api-item-code">{% if main.API_KEY %}{{ main.API_KEY }}{% else %}<span style="color: #ff9500;">未设置</span>{% endif %}</code> | |
| {% if main.API_KEY %} | |
| <button class="btn-copy" onclick="navigator.clipboard.writeText('{{ main.API_KEY }}').then(() => { this.innerHTML = '✅'; setTimeout(() => this.innerHTML = '📋', 2000); })">📋</button> | |
| {% endif %} | |
| </div> | |
| </div> | |
| </div> | |
| <div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(0,0,0,0.06);"> | |
| <div style="font-size: 11px; color: #6b6b6b; margin-bottom: 6px;">支持的模型</div> | |
| <div style="display: flex; flex-wrap: wrap; gap: 6px;"> | |
| <span style="background: #f0f0f2; color: #1d1d1f; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;">gemini-auto</span> | |
| <span style="background: #f0f0f2; color: #1d1d1f; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;">gemini-2.5-flash</span> | |
| <span style="background: #f0f0f2; color: #1d1d1f; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;">gemini-2.5-pro</span> | |
| <span style="background: #f0f0f2; color: #1d1d1f; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;">gemini-3-flash-preview</span> | |
| <span style="background: #f0f0f2; color: #1d1d1f; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;">gemini-3-pro-preview</span> | |
| </div> | |
| <div style="margin-top: 8px; font-size: 11px; color: #86868b;"> | |
| 📸 图片生成可在"系统设置"自定义配置 | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-title">账户状态 ({{ multi_account_mgr.accounts|length }} 个)</div> | |
| <div style="color: #6b6b6b; font-size: 12px; margin-bottom: 12px; padding-left: 4px;"> | |
| 默认过期时间12小时,注意北京时间 • 批量上传使用 <code style="font-size: 11px; background: rgba(0,0,0,0.05); padding: 2px 6px; border-radius: 4px;">script/download-config.js</code> 油猴脚本 | |
| </div> | |
| {{ account_table(accounts_data) }} | |
| </div> | |
| </div> | |
| <!-- Tab 2: API文档 --> | |
| <div id="tab-api" class="tab-content"> | |
| <div class="section"> | |
| <div class="section-title">API 端点列表</div> | |
| <div class="current-url-row"> | |
| <span style="font-size:12px; font-weight:600; color:#0071e3; margin-right:8px;">当前页面:</span> | |
| <code style="background:none; padding:0; color:#1d1d1f;">{{ current_url }}</code> | |
| </div> | |
| <table class="ep-table"> | |
| <tr> | |
| <td width="70"><span class="method m-post">POST</span></td> | |
| <td><span class="ep-path">/{{ api_path_segment }}v1/chat/completions</span></td> | |
| <td><span class="ep-desc">OpenAI 兼容对话接口</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ api_path_segment }}v1/models</span></td> | |
| <td><span class="ep-desc">获取模型列表</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}</span></td> | |
| <td><span class="ep-desc">管理首页 (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}/health</span></td> | |
| <td><span class="ep-desc">健康检查 (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}/accounts</span></td> | |
| <td><span class="ep-desc">账户状态 JSON (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}/log</span></td> | |
| <td><span class="ep-desc">获取日志 JSON (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}/log/html</span></td> | |
| <td><span class="ep-desc">日志查看器 HTML (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-del">DEL</span></td> | |
| <td><span class="ep-path">/{{ admin_path_segment }}/log?confirm=yes</span></td> | |
| <td><span class="ep-desc">清空系统日志 (需登录)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/public/stats</span></td> | |
| <td><span class="ep-desc">公开统计数据</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/public/log</span></td> | |
| <td><span class="ep-desc">公开日志 (JSON, 脱敏)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/public/log/html</span></td> | |
| <td><span class="ep-desc">公开日志查看器 (HTML)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/public/uptime</span></td> | |
| <td><span class="ep-desc">实时状态监控 (JSON)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/public/uptime/html</span></td> | |
| <td><span class="ep-desc">实时状态监控页面 (HTML)</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/docs</span></td> | |
| <td><span class="ep-desc">Swagger API 文档</span></td> | |
| </tr> | |
| <tr> | |
| <td><span class="method m-get">GET</span></td> | |
| <td><span class="ep-path">/redoc</span></td> | |
| <td><span class="ep-desc">ReDoc API 文档</span></td> | |
| </tr> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Tab 3: 系统配置 --> | |
| <div id="tab-config" class="tab-content"> | |
| <div class="section"> | |
| <div class="section-title">当前配置状态</div> | |
| <div class="grid-env"> | |
| <div class="stack-col"> | |
| <div class="card"> | |
| <h3>环境变量 <span class="badge badge-required">ENV</span></h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="env-var"> | |
| <div><div class="env-name">ADMIN_KEY</div><div class="env-desc">管理员密钥</div></div> | |
| <div class="env-value">已设置</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">PATH_PREFIX</div><div class="env-desc">API路径前缀</div></div> | |
| <div class="env-value">{{ main.PATH_PREFIX or '未设置' }}</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h3>基础配置 <span class="badge badge-optional">YAML</span></h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="env-var"> | |
| <div><div class="env-name">API_KEY</div><div class="env-desc">API访问密钥</div></div> | |
| <div class="env-value">{% if main.API_KEY %}已设置{% else %}未设置(公开访问){% endif %}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">BASE_URL</div><div class="env-desc">服务器URL</div></div> | |
| <div class="env-value">{% if main.BASE_URL %}已设置{% else %}自动检测{% endif %}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">PROXY</div><div class="env-desc">代理地址</div></div> | |
| <div class="env-value">{% if main.PROXY %}已设置{% else %}未设置{% endif %}</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h3>重试策略 <span class="badge badge-optional">YAML</span></h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="env-var"> | |
| <div><div class="env-name">max_new_session_tries</div><div class="env-desc">新会话尝试数</div></div> | |
| <div class="env-value">{{ main.MAX_NEW_SESSION_TRIES }}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">max_request_retries</div><div class="env-desc">请求重试次数</div></div> | |
| <div class="env-value">{{ main.MAX_REQUEST_RETRIES }}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">max_account_switch_tries</div><div class="env-desc">账户切换次数</div></div> | |
| <div class="env-value">{{ main.MAX_ACCOUNT_SWITCH_TRIES }}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">account_failure_threshold</div><div class="env-desc">失败阈值</div></div> | |
| <div class="env-value">{{ main.ACCOUNT_FAILURE_THRESHOLD }} 次</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">rate_limit_cooldown_seconds</div><div class="env-desc">429冷却时间</div></div> | |
| <div class="env-value">{{ main.RATE_LIMIT_COOLDOWN_SECONDS }} 秒</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">session_cache_ttl_seconds</div><div class="env-desc">会话缓存时间</div></div> | |
| <div class="env-value">{{ main.SESSION_CACHE_TTL_SECONDS }} 秒</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h3>公开展示 <span class="badge badge-optional">YAML</span></h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="env-var"> | |
| <div><div class="env-name">LOGO_URL</div><div class="env-desc">Logo图片</div></div> | |
| <div class="env-value">{% if main.LOGO_URL %}已设置{% else %}未设置{% endif %}</div> | |
| </div> | |
| <div class="env-var"> | |
| <div><div class="env-name">CHAT_URL</div><div class="env-desc">对话链接</div></div> | |
| <div class="env-value">{% if main.CHAT_URL %}已设置{% else %}未设置{% endif %}</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tab 4: 系统设置 --> | |
| <div id="tab-settings" class="tab-content"> | |
| <div class="section"> | |
| <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; gap: 16px; flex-wrap: wrap;"> | |
| <div style="flex: 1; min-width: 200px;"> | |
| <div class="section-title" style="margin-bottom: 4px;">系统设置</div> | |
| <div style="color: #6b6b6b; font-size: 11px; padding-left: 4px;"> | |
| ✅ 配置修改后立即生效,无需重启 • 📋 优先级:YAML > 环境变量 > 默认值 | |
| </div> | |
| </div> | |
| <div style="display: flex; gap: 10px; flex-shrink: 0;"> | |
| <button class="btn" onclick="loadSettings()">重置</button> | |
| <button class="btn" onclick="saveSettings()">保存设置</button> | |
| </div> | |
| </div> | |
| <div class="grid-env"> | |
| <div class="stack-col"> | |
| <!-- 基础配置 --> | |
| <div class="card"> | |
| <h3>基础配置</h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="setting-item"> | |
| <label>API 访问密钥</label> | |
| <input type="text" id="setting-api-key" placeholder="留空则公开访问" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>服务器 URL</label> | |
| <input type="text" id="setting-base-url" placeholder="留空则自动检测" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>代理地址</label> | |
| <input type="text" id="setting-proxy" placeholder="如 http://127.0.0.1:7890" /> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 图片生成配置 --> | |
| <div class="card"> | |
| <h3>📸 图片生成配置</h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="setting-item"> | |
| <label style="display: flex; align-items: center; gap: 8px;"> | |
| <input type="checkbox" id="setting-image-enabled" style="width: auto;" /> | |
| 启用图片生成 | |
| </label> | |
| </div> | |
| <div class="setting-item"> | |
| <label>支持的模型</label> | |
| <div id="setting-image-models" style="display: flex; flex-direction: column; gap: 6px; margin-top: 6px;"> | |
| <label style="display: flex; align-items: center; gap: 6px; font-weight: normal; font-size: 12px;"> | |
| <input type="checkbox" value="gemini-3-pro-preview" style="width: auto;" /> gemini-3-pro-preview | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; font-weight: normal; font-size: 12px;"> | |
| <input type="checkbox" value="gemini-2.5-pro" style="width: auto;" /> gemini-2.5-pro | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; font-weight: normal; font-size: 12px;"> | |
| <input type="checkbox" value="gemini-2.5-flash" style="width: auto;" /> gemini-2.5-flash | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; font-weight: normal; font-size: 12px;"> | |
| <input type="checkbox" value="gemini-3-flash-preview" style="width: auto;" /> gemini-3-flash-preview | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; font-weight: normal; font-size: 12px;"> | |
| <input type="checkbox" value="gemini-auto" style="width: auto;" /> gemini-auto | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stack-col"> | |
| <!-- 重试策略配置 --> | |
| <div class="card"> | |
| <h3>🔄 重试策略配置</h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="setting-item"> | |
| <label>新会话尝试账户数</label> | |
| <input type="number" id="setting-max-new-session" min="1" max="20" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>请求失败重试次数</label> | |
| <input type="number" id="setting-max-retries" min="1" max="10" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>账户切换尝试次数</label> | |
| <input type="number" id="setting-max-switch" min="1" max="20" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>账户失败阈值(次)</label> | |
| <input type="number" id="setting-failure-threshold" min="1" max="10" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>429 冷却时间(秒)</label> | |
| <input type="number" id="setting-cooldown" min="60" max="3600" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>会话缓存时间(秒)</label> | |
| <input type="number" id="setting-cache-ttl" min="300" max="86400" /> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 公开展示配置 --> | |
| <div class="card"> | |
| <h3>🎨 公开展示配置</h3> | |
| <div style="margin-top: 12px;"> | |
| <div class="setting-item"> | |
| <label>Logo URL</label> | |
| <input type="text" id="setting-logo-url" placeholder="留空则不显示" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>开始对话链接</label> | |
| <input type="text" id="setting-chat-url" placeholder="留空则不显示" /> | |
| </div> | |
| <div class="setting-item"> | |
| <label>Session 过期时间(小时)</label> | |
| <input type="number" id="setting-session-hours" min="1" max="168" /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- JSON 编辑器模态框 --> | |
| <div id="jsonModal" class="modal"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <div class="modal-title">编辑账户配置</div> | |
| <button class="modal-close" onclick="closeModal()">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <textarea id="jsonEditor" class="json-editor" placeholder="在此编辑 JSON 配置..."></textarea> | |
| <div id="jsonError" class="json-error"></div> | |
| <div style="margin-top: 12px; font-size: 12px; color: #6b6b6b;"> | |
| <strong>提示:</strong>编辑完成后点击"保存"按钮。JSON 格式错误时无法保存。<br> | |
| 配置立即生效。重启后将从环境变量重新加载,建议同步更新 ACCOUNTS_CONFIG。 | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <button class="btn btn-secondary" onclick="closeModal()">取消</button> | |
| <button class="btn btn-primary" onclick="saveConfig()">保存配置</button> | |
| </div> | |
| </div> | |
| </div> | |
| {% endblock %} | |
| {% block extra_js %} | |
| <script> | |
| // Define global variables for admin.js | |
| window.ADMIN_PATH = '{{ admin_path_segment }}'; | |
| </script> | |
| <script src="/static/js/admin.js"></script> | |
| {% endblock %} | |