初始化提交

This commit is contained in:
h88782481 2026-03-09 14:18:42 +08:00
commit 202731df74
28 changed files with 3140 additions and 0 deletions

127
static/admin.html Normal file
View file

@ -0,0 +1,127 @@
<!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>
</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')">&#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>