Spaces:
Running
Running
<html lang="zh-CN"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Sora API - 管理控制台</title> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> | |
<link rel="stylesheet" href="/static/admin/css/style.css"> | |
</head> | |
<body> | |
<!-- 登录页面 --> | |
<div id="login-container" style="display: flex;" class="login-container"> | |
<div class="card shadow-lg"> | |
<div class="card-header bg-primary text-white text-center py-3"> | |
<h4>Sora API 管理控制台</h4> | |
</div> | |
<div class="card-body p-4"> | |
<div class="text-center mb-4"> | |
<i class="bi bi-shield-lock" style="font-size: 3rem;"></i> | |
<h5 class="mt-2">管理员登录</h5> | |
</div> | |
<form id="login-form"> | |
<div class="mb-3"> | |
<label for="admin-key-input" class="form-label">管理员密钥</label> | |
<input type="password" class="form-control" id="admin-key-input" required> | |
<div class="form-text">请输入管理员密钥以访问管理控制台</div> | |
</div> | |
<div class="d-grid gap-2"> | |
<button type="submit" class="btn btn-primary">登录</button> | |
</div> | |
</form> | |
<div id="login-error" class="alert alert-danger mt-3" style="display: none;"></div> | |
</div> | |
</div> | |
</div> | |
<!-- 管理控制台 --> | |
<div id="admin-panel" style="display: none;" class="wrapper"> | |
<!-- 侧边栏导航 --> | |
<nav id="sidebar"> | |
<div class="sidebar-header d-flex align-items-center"> | |
<h3 class="mb-0">Sora API</h3> | |
</div> | |
<ul class="list-unstyled components"> | |
<li class="active"> | |
<a href="#" data-page="dashboard"><i class="bi bi-speedometer2 me-2"></i>仪表盘</a> | |
</li> | |
<li> | |
<a href="#" data-page="keys"><i class="bi bi-key-fill me-2"></i>API密钥管理</a> | |
</li> | |
<li> | |
<a href="#" data-page="stats"><i class="bi bi-graph-up me-2"></i>使用统计</a> | |
</li> | |
<li> | |
<a href="#" data-page="settings"><i class="bi bi-gear-fill me-2"></i>系统设置</a> | |
</li> | |
<li class="sidebar-footer"> | |
<a href="#" id="logout-btn"><i class="bi bi-box-arrow-right me-2"></i>退出登录</a> | |
</li> | |
</ul> | |
</nav> | |
<!-- 页面内容区 --> | |
<div id="content"> | |
<!-- 顶部导航栏 --> | |
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow-sm mb-4"> | |
<div class="container-fluid"> | |
<button type="button" id="sidebarCollapse" class="btn btn-primary"> | |
<i class="bi bi-list"></i> | |
</button> | |
<div class="ms-3 d-none d-md-block"> | |
<h5 class="mb-0" id="current-page-title">仪表盘</h5> | |
</div> | |
<div class="ms-auto d-flex align-items-center"> | |
<span class="badge bg-primary me-3" id="admin-key-display"></span> | |
<span class="text-muted d-none d-md-block">欢迎回来,管理员</span> | |
</div> | |
</div> | |
</nav> | |
<!-- 仪表盘页面 --> | |
<div id="dashboard-page" class="content-page active container-fluid"> | |
<div class="row mb-4"> | |
<div class="col-12"> | |
<h4 class="mb-4">系统概览</h4> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-primary text-white h-100"> | |
<div class="card-body"> | |
<h5 class="card-title">总API密钥</h5> | |
<h3 id="total-keys" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-success text-white h-100"> | |
<div class="card-body"> | |
<h5 class="card-title">激活密钥</h5> | |
<h3 id="active-keys" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-danger text-white h-100"> | |
<div class="card-body"> | |
<h5 class="card-title">禁用密钥</h5> | |
<h3 id="disabled-keys" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-info text-white h-100"> | |
<div class="card-body"> | |
<h5 class="card-title">今日请求</h5> | |
<h3 id="today-requests" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-lg-8 mb-4"> | |
<div class="card h-100"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">近期请求趋势</h5> | |
</div> | |
<div class="card-body"> | |
<canvas id="dashboard-requests-chart"></canvas> | |
</div> | |
</div> | |
</div> | |
<div class="col-lg-4 mb-4"> | |
<div class="card h-100"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">最新API密钥</h5> | |
</div> | |
<div class="card-body p-0"> | |
<div class="list-group list-group-flush" id="recent-keys-list"> | |
<!-- JS动态加载 --> | |
</div> | |
</div> | |
<div class="card-footer text-end"> | |
<a href="#" class="btn btn-sm btn-primary" data-page="keys">查看全部</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- API密钥管理页面 --> | |
<div id="keys-page" class="content-page container-fluid"> | |
<div class="row mb-4"> | |
<div class="col-12"> | |
<div class="card"> | |
<div class="card-body"> | |
<div class="d-flex justify-content-between flex-wrap mb-3"> | |
<div class="mb-2"> | |
<button id="add-key-btn" class="btn btn-primary me-2"> | |
<i class="bi bi-plus-circle me-1"></i> 添加密钥 | |
</button> | |
<div class="btn-group"> | |
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown"> | |
批量操作 | |
</button> | |
<ul class="dropdown-menu"> | |
<li><a class="dropdown-item batch-action" data-action="enable" href="#">启用选中</a></li> | |
<li><a class="dropdown-item batch-action" data-action="disable" href="#">禁用选中</a></li> | |
<li><hr class="dropdown-divider"></li> | |
<li><a class="dropdown-item batch-action text-danger" data-action="delete" href="#">删除选中</a></li> | |
</ul> | |
</div> | |
<button id="import-keys-btn" class="btn btn-outline-primary ms-2"> | |
<i class="bi bi-upload me-1"></i> 批量导入 | |
</button> | |
</div> | |
<div class="mb-2"> | |
<input type="text" id="search-keys" class="form-control" placeholder="搜索密钥..."> | |
</div> | |
</div> | |
<div class="table-responsive"> | |
<table class="table table-hover"> | |
<thead> | |
<tr> | |
<th><input type="checkbox" id="select-all"></th> | |
<th>名称</th> | |
<th>密钥值</th> | |
<th>状态</th> | |
<th>权重</th> | |
<th>速率限制</th> | |
<th>创建时间</th> | |
<th>最后使用</th> | |
<th>操作</th> | |
</tr> | |
</thead> | |
<tbody id="keys-table-body"> | |
<!-- JS动态加载 --> | |
</tbody> | |
</table> | |
</div> | |
<div id="pagination" class="d-flex justify-content-center mt-4"> | |
<!-- JS动态加载 --> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 使用统计页面 --> | |
<div id="stats-page" class="content-page container-fluid"> | |
<div class="row"> | |
<div class="col-12"> | |
<h4 class="mb-4">使用统计</h4> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-primary text-white"> | |
<div class="card-body"> | |
<h5 class="card-title">总请求次数</h5> | |
<h3 id="total-requests" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-success text-white"> | |
<div class="card-body"> | |
<h5 class="card-title">成功请求</h5> | |
<h3 id="successful-requests" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-danger text-white"> | |
<div class="card-body"> | |
<h5 class="card-title">失败请求</h5> | |
<h3 id="failed-requests" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 col-lg-3 mb-4"> | |
<div class="card bg-info text-white"> | |
<div class="card-body"> | |
<h5 class="card-title">成功率</h5> | |
<h3 id="success-rate" class="card-text">--</h3> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<!-- 请求趋势图 --> | |
<div class="col-lg-8 mb-4"> | |
<div class="card"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">请求趋势</h5> | |
</div> | |
<div class="card-body"> | |
<canvas id="requests-chart"></canvas> | |
</div> | |
</div> | |
</div> | |
<!-- 密钥使用分布 --> | |
<div class="col-lg-4 mb-4"> | |
<div class="card"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">密钥使用分布</h5> | |
</div> | |
<div class="card-body"> | |
<canvas id="keys-usage-chart"></canvas> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 设置页面 --> | |
<div id="settings-page" class="content-page container-fluid"> | |
<div class="row"> | |
<div class="col-12"> | |
<h4 class="mb-4">系统设置</h4> | |
</div> | |
<div class="col-md-6 mb-4"> | |
<div class="card"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">管理员密钥</h5> | |
</div> | |
<div class="card-body"> | |
<div class="mb-3"> | |
<label for="admin-key" class="form-label">管理员密钥</label> | |
<div class="input-group"> | |
<input type="password" class="form-control" id="admin-key" readonly> | |
<button class="btn btn-outline-secondary" type="button" id="show-admin-key"> | |
<i class="bi bi-eye"></i> | |
</button> | |
<button class="btn btn-outline-secondary" type="button" id="copy-admin-key"> | |
<i class="bi bi-clipboard"></i> | |
</button> | |
</div> | |
<div class="form-text">此密钥用于访问管理界面,请妥善保管</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-6 mb-4"> | |
<div class="card"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0">系统配置</h5> | |
</div> | |
<div class="card-body"> | |
<form id="settings-form"> | |
<div class="mb-3"> | |
<label for="proxy-host" class="form-label">代理主机</label> | |
<input type="text" class="form-control" id="proxy-host"> | |
</div> | |
<div class="mb-3"> | |
<label for="proxy-port" class="form-label">代理端口</label> | |
<input type="text" class="form-control" id="proxy-port"> | |
</div> | |
<div class="mb-3"> | |
<label for="proxy-user" class="form-label">代理用户名</label> | |
<input type="text" class="form-control" id="proxy-user"> | |
</div> | |
<div class="mb-3"> | |
<label for="proxy-pass" class="form-label">代理密码</label> | |
<input type="password" class="form-control" id="proxy-pass"> | |
<div class="form-text">如果代理服务器需要认证,请填写用户名和密码</div> | |
</div> | |
<hr> | |
<h6 class="mb-3">图片本地化设置</h6> | |
<div class="mb-3 form-check"> | |
<input type="checkbox" class="form-check-input" id="image-localization"> | |
<label class="form-check-label" for="image-localization">启用图片本地化</label> | |
<div class="form-text">启用后,Sora生成的图片将被下载并保存到本地服务器,避免客户端无法访问外部链接</div> | |
</div> | |
<div class="mb-3"> | |
<label for="base-url" class="form-label">Base URL</label> | |
<input type="text" class="form-control" id="base-url" placeholder="http://example.com"> | |
<div class="form-text">设置访问本地化图片的基础URL,例如:http://example.com 或 https://your-domain.com</div> | |
</div> | |
<div class="mb-3"> | |
<label for="image-save-dir" class="form-label">图片保存目录</label> | |
<input type="text" class="form-control" id="image-save-dir" placeholder="src/static/images"> | |
<div class="form-text">相对于工作目录的路径,必须确保目录存在且有写入权限</div> | |
</div> | |
<button type="submit" class="btn btn-primary">保存设置</button> | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 添加/编辑密钥对话框 --> | |
<div class="modal fade" id="keyModal" tabindex="-1"> | |
<div class="modal-dialog"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="keyModalLabel">添加新密钥</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
</div> | |
<div class="modal-body"> | |
<form id="key-form"> | |
<input type="hidden" id="key-id"> | |
<div class="mb-3"> | |
<label for="key-name" class="form-label">密钥名称</label> | |
<input type="text" class="form-control" id="key-name" required> | |
</div> | |
<div class="mb-3"> | |
<label for="key-value" class="form-label">密钥值 (Bearer Token)</label> | |
<input type="text" class="form-control" id="key-value" required> | |
<div class="form-text">输入OpenAI格式的API密钥,无需添加"Bearer "前缀,系统会自动处理</div> | |
</div> | |
<div class="mb-3"> | |
<label for="key-weight" class="form-label">权重 (1-10)</label> | |
<input type="number" class="form-control" id="key-weight" min="1" max="10" value="1"> | |
<div class="form-text">权重越高,被选中的概率越大</div> | |
</div> | |
<div class="mb-3"> | |
<label for="key-rate-limit" class="form-label">速率限制 (每分钟请求数)</label> | |
<input type="number" class="form-control" id="key-rate-limit" min="1" value="60"> | |
</div> | |
<div class="mb-3 form-check"> | |
<input type="checkbox" class="form-check-input" id="key-enabled" checked> | |
<label class="form-check-label" for="key-enabled">启用</label> | |
</div> | |
<div class="mb-3"> | |
<label for="key-notes" class="form-label">备注</label> | |
<textarea class="form-control" id="key-notes" rows="2"></textarea> | |
</div> | |
</form> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> | |
<button type="button" class="btn btn-primary" id="test-key-btn">测试连接</button> | |
<button type="button" class="btn btn-success" id="save-key-btn">保存</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 确认对话框 --> | |
<div class="modal fade" id="confirmModal" tabindex="-1"> | |
<div class="modal-dialog"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="confirmModalLabel">确认操作</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
</div> | |
<div class="modal-body" id="confirm-message"> | |
确定要执行此操作吗? | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> | |
<button type="button" class="btn btn-danger" id="confirm-btn">确认</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- 批量导入密钥对话框 --> | |
<div class="modal fade" id="importKeysModal" tabindex="-1"> | |
<div class="modal-dialog modal-lg"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h5 class="modal-title">批量导入API密钥</h5> | |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |
</div> | |
<div class="modal-body"> | |
<ul class="nav nav-tabs" id="importTabs" role="tablist"> | |
<li class="nav-item" role="presentation"> | |
<button class="nav-link active" id="paste-tab" data-bs-toggle="tab" data-bs-target="#paste-content" type="button" role="tab">粘贴内容</button> | |
</li> | |
<li class="nav-item" role="presentation"> | |
<button class="nav-link" id="file-tab" data-bs-toggle="tab" data-bs-target="#file-upload" type="button" role="tab">上传文件</button> | |
</li> | |
</ul> | |
<div class="tab-content mt-3" id="importTabsContent"> | |
<div class="tab-pane fade show active" id="paste-content" role="tabpanel"> | |
<div class="mb-3"> | |
<label for="keys-text" class="form-label">API密钥列表</label> | |
<textarea class="form-control" id="keys-text" rows="10" placeholder="支持两种格式: 1. 每行一个密钥 2. 每行格式:[名称],[API密钥],[权重(可选)],[速率限制(可选)] 例如: sk-abcdefg123456 我的密钥2,sk-hijklmn789012,5,100"></textarea> | |
</div> | |
</div> | |
<div class="tab-pane fade" id="file-upload" role="tabpanel"> | |
<div class="mb-3"> | |
<label for="keys-file" class="form-label">选择文件</label> | |
<input class="form-control" type="file" id="keys-file" accept=".txt,.csv"> | |
<div class="form-text">支持TXT或CSV文件,每行格式同上</div> | |
</div> | |
</div> | |
</div> | |
<div class="form-check mt-3"> | |
<input class="form-check-input" type="checkbox" id="auto-enable-keys" checked> | |
<label class="form-check-label" for="auto-enable-keys"> | |
自动启用导入的密钥 | |
</label> | |
</div> | |
<div class="alert alert-info mt-3"> | |
<i class="bi bi-info-circle me-2"></i> 批量导入将跳过重复的密钥值。如果名称重复但密钥值不同,将作为新密钥添加。 | |
</div> | |
<div id="import-preview" class="mt-3" style="display: none;"> | |
<h6>导入预览 (<span id="preview-count">0</span>个密钥)</h6> | |
<div class="table-responsive"> | |
<table class="table table-sm"> | |
<thead> | |
<tr> | |
<th>名称</th> | |
<th>密钥</th> | |
<th>权重</th> | |
<th>速率限制</th> | |
</tr> | |
</thead> | |
<tbody id="preview-table-body"> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> | |
<button type="button" class="btn btn-primary" id="preview-import-btn">预览</button> | |
<button type="button" class="btn btn-success" id="confirm-import-btn" disabled>导入</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Toast通知 --> | |
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 5"> | |
<div id="toast-container"></div> | |
</div> | |
<!-- JavaScript库 --> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<script src="/static/admin/js/main.js"></script> | |
</body> | |
</html> |