api2cursor/static/admin.html
2026-03-09 19:43:51 +08:00

129 lines
5.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 2 Cursor - 管理面板</title>
<link rel="stylesheet" href="/static/admin.css">
</head>
<body>
<!-- 登录 -->
<div id="login">
<div class="login-card">
<h1>API 2 Cursor</h1>
<p>模型映射管理面板</p>
<div class="field">
<label>ACCESS_API_KEY</label>
<div class="input-wrap">
<input type="password" id="loginKey" placeholder="请输入访问密钥" onkeydown="if(event.key==='Enter')doLogin()">
<button class="eye" onclick="togglePwd('loginKey')">&#128065;</button>
</div>
</div>
<button class="btn btn-primary btn-block" onclick="doLogin()">登 录</button>
</div>
</div>
<!-- 仪表盘 -->
<div id="dashboard">
<header>
<div class="container inner">
<h1>API 2 Cursor</h1>
<div class="right">
<span class="status" id="statusBadge">已连接</span>
<button class="btn btn-ghost btn-sm" onclick="doLogout()">退出</button>
</div>
</div>
</header>
<main class="container">
<!-- 全局设置 -->
<div class="card">
<div class="card-header">
<h2>全局设置</h2>
</div>
<div class="field">
<label>中转站地址 (Proxy Target URL)</label>
<div class="input-wrap"><input type="text" id="targetUrl" placeholder="https://your-relay.com"></div>
<div class="hint" id="envUrl"></div>
</div>
<div class="field">
<label>中转站 API Key</label>
<div class="input-wrap">
<input type="password" id="proxyKey" placeholder="sk-xxx 或 Bearer token">
<button class="eye" onclick="togglePwd('proxyKey')">&#128065;</button>
</div>
<div class="hint" id="envKey"></div>
</div>
<button class="btn btn-primary" onclick="saveSettings()">保存设置</button>
</div>
<!-- 模型映射 -->
<div class="card">
<div class="card-header">
<h2>模型映射</h2>
<button class="btn btn-green btn-sm" onclick="openAddModal()">+ 添加映射</button>
</div>
<div class="hint" style="margin-top:-12px;margin-bottom:16px">
Cursor 发送请求时会带上模型名 → 代理根据映射表找到上游实际模型名和后端协议进行转发。
<br>提示:建议在 Cursor 里使用 Claude 风格的模型名(如 <code>claude-sonnet-4-5-20250929</code>),这样 Cursor 会走 <code>/v1/chat/completions</code> 格式GPT 风格的模型名会走 <code>/v1/responses</code> 格式,两种都已支持。
</div>
<div id="mappingList"></div>
</div>
</main>
</div>
<!-- 弹窗 -->
<div class="modal-overlay" id="modal">
<div class="modal">
<h3 id="modalTitle">添加模型映射</h3>
<div class="field">
<label>Cursor 模型名 <span style="color:var(--red)">*</span></label>
<div class="input-wrap"><input type="text" id="mName" placeholder="例: claude-sonnet-4-5-20250929"></div>
<div class="hint">在 Cursor 自定义模型中添加的名称</div>
</div>
<div class="field">
<label>上游实际模型名 <span style="color:var(--red)">*</span></label>
<div class="input-wrap"><input type="text" id="mUpstream" placeholder="例: gpt-5.4 或 claude-sonnet-4-5-20250929"></div>
<div class="hint">发送到中转站的真实模型名称</div>
</div>
<div class="field">
<label>后端类型</label>
<div class="input-wrap">
<select id="mBackend">
<option value="auto">自动检测</option>
<option value="anthropic">Anthropic (/v1/messages)</option>
<option value="openai">OpenAI (/v1/chat/completions)</option>
<option value="responses">OpenAI Responses (/v1/responses)</option>
</select>
</div>
<div class="hint">
<b>anthropic</b>:转换为 Anthropic Messages 格式 — 适用于中转站通过 <code>/v1/messages</code> 提供 Claude 模型<br>
<b>openai</b>:保持 OpenAI Chat Completions 格式 — 适用于 GPT、DeepSeek、Codex 或通过 <code>/v1/chat/completions</code> 提供所有模型的中转站<br>
<b>responses</b>:保持 OpenAI Responses 格式 — 适用于中转站仅通过 <code>/v1/responses</code> 提供模型能力<br>
<b>自动检测</b>:根据上游模型名判断(含 claude → anthropic其他 → openai
</div>
</div>
<div class="field">
<label>自定义地址 <span style="color:var(--muted)">(可选,留空使用全局设置)</span></label>
<div class="input-wrap"><input type="text" id="mUrl" placeholder="留空则使用全局中转站地址"></div>
</div>
<div class="field">
<label>自定义 API Key <span style="color:var(--muted)">(可选,留空使用全局设置)</span></label>
<div class="input-wrap">
<input type="password" id="mKey" placeholder="留空则使用全局 API Key">
<button class="eye" onclick="togglePwd('mKey')">&#128065;</button>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal()">取消</button>
<button class="btn btn-primary" id="modalSaveBtn" onclick="saveMapping()">保存</button>
</div>
</div>
</div>
<div class="toast-area" id="toasts"></div>
<script src="/static/admin.js"></script>
</body>
</html>