127 lines
5.1 KiB
HTML
127 lines
5.1 KiB
HTML
<!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')">👁</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')">👁</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>
|
||
</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>自动检测</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')">👁</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>
|