fix: 兼容 new-api 后台创建的 token 格式
validateKey 正则从只认 sk-xp-+32位hex, 改为同时支持 sk-+48位字母数字(new-api 后台 token)。 同步更新 placeholder、错误提示、FAQ 文字。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -669,7 +669,7 @@
|
|||||||
<p class="section-desc" style="margin:0 auto;">粘贴你的 API Key,实时查询剩余额度</p>
|
<p class="section-desc" style="margin:0 auto;">粘贴你的 API Key,实时查询剩余额度</p>
|
||||||
<div class="balance-card">
|
<div class="balance-card">
|
||||||
<div class="bal-input-row">
|
<div class="bal-input-row">
|
||||||
<input type="text" id="balKey" class="bal-input" placeholder="sk-xp-..." spellcheck="false" autocomplete="off">
|
<input type="text" id="balKey" class="bal-input" placeholder="sk-xp-... 或 sk-KKnD..." spellcheck="false" autocomplete="off">
|
||||||
<button class="btn-query" id="queryBtn" onclick="doQueryBalance()">查询余额</button>
|
<button class="btn-query" id="queryBtn" onclick="doQueryBalance()">查询余额</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="bal-err" id="balErr"></div>
|
<div class="bal-err" id="balErr"></div>
|
||||||
@@ -776,7 +776,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="faq-item">
|
<div class="faq-item">
|
||||||
<div class="faq-q">Key 格式是什么?可以自己生成吗?</div>
|
<div class="faq-q">Key 格式是什么?可以自己生成吗?</div>
|
||||||
<div class="faq-a">格式为 sk-xp- 开头 + 32位随机字符串。点"帮我生成一个"自动生成,也可以粘贴已有的 Key。</div>
|
<div class="faq-a">有两种格式:前端自动生成的是 sk-xp- 开头;在 new-api 后台创建的 token 是 sk- 开头 + 48位字母数字。两种都支持查询余额和使用 API。</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -868,7 +868,27 @@ function selectAmt(amount) {
|
|||||||
document.getElementById('recharge').scrollIntoView({behavior: 'smooth'});
|
document.getElementById('recharge').scrollIntoView({behavior: 'smooth'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 频率限制:60秒内最多5次查询 ──
|
||||||
|
const _rl = { count: 0, reset: 0 };
|
||||||
|
function checkRateLimit() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now > _rl.reset) { _rl.count = 0; _rl.reset = now + 60000; }
|
||||||
|
if (_rl.count >= 5) {
|
||||||
|
const wait = Math.ceil((_rl.reset - now) / 1000);
|
||||||
|
throw new Error('查询过于频繁,请 ' + wait + ' 秒后再试');
|
||||||
|
}
|
||||||
|
_rl.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Key 格式校验:兼容两种格式 ──
|
||||||
|
// 格式1:sk-xp- + 32位十六进制(前端自生成)
|
||||||
|
// 格式2:sk- + 48位字母数字(new-api 后台创建的 token)
|
||||||
|
function validateKey(key) {
|
||||||
|
return /^sk-xp-[0-9a-f]{32}$/.test(key) || /^sk-[A-Za-z0-9]{48}$/.test(key);
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchBalance(key) {
|
async function fetchBalance(key) {
|
||||||
|
checkRateLimit();
|
||||||
const headers = {Authorization: 'Bearer ' + key};
|
const headers = {Authorization: 'Bearer ' + key};
|
||||||
const today = new Date().toISOString().slice(0,10);
|
const today = new Date().toISOString().slice(0,10);
|
||||||
const [subResp, usageResp] = await Promise.all([
|
const [subResp, usageResp] = await Promise.all([
|
||||||
@@ -884,6 +904,7 @@ async function fetchBalance(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function queryBalance(key) {
|
async function queryBalance(key) {
|
||||||
|
if (!validateKey(key)) return; // 输入中自动触发的,格式不对直接静默忽略
|
||||||
const line = document.getElementById('balanceLine');
|
const line = document.getElementById('balanceLine');
|
||||||
line.textContent = '查询中...';
|
line.textContent = '查询中...';
|
||||||
try {
|
try {
|
||||||
@@ -899,8 +920,8 @@ async function doQueryBalance() {
|
|||||||
const btn = document.getElementById('queryBtn');
|
const btn = document.getElementById('queryBtn');
|
||||||
const errEl = document.getElementById('balErr');
|
const errEl = document.getElementById('balErr');
|
||||||
const resEl = document.getElementById('balResult');
|
const resEl = document.getElementById('balResult');
|
||||||
if (!key || !key.startsWith('sk-')) {
|
if (!validateKey(key)) {
|
||||||
errEl.textContent = '请输入有效的 API Key(以 sk- 开头)';
|
errEl.textContent = 'Key 格式不正确,请输入有效的 API Key(sk- 开头)';
|
||||||
errEl.classList.add('show'); resEl.classList.remove('show'); return;
|
errEl.classList.add('show'); resEl.classList.remove('show'); return;
|
||||||
}
|
}
|
||||||
btn.disabled = true; btn.textContent = '查询中...';
|
btn.disabled = true; btn.textContent = '查询中...';
|
||||||
@@ -927,9 +948,9 @@ document.getElementById('balKey').addEventListener('keydown', function(e) {
|
|||||||
document.getElementById('keyInput').addEventListener('input', function() {
|
document.getElementById('keyInput').addEventListener('input', function() {
|
||||||
const key = this.value.trim();
|
const key = this.value.trim();
|
||||||
updateHint(key);
|
updateHint(key);
|
||||||
if (key.length > 10) {
|
if (validateKey(key)) {
|
||||||
clearTimeout(this._t);
|
clearTimeout(this._t);
|
||||||
this._t = setTimeout(() => queryBalance(key), 800);
|
this._t = setTimeout(() => queryBalance(key), 1000);
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('balanceLine').textContent = '粘贴 Key 后自动查询余额';
|
document.getElementById('balanceLine').textContent = '粘贴 Key 后自动查询余额';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -532,7 +532,7 @@ curl "https://api.u-claw.org/v1/dashboard/billing/usage?start_date=2020-01-01&en
|
|||||||
<div class="live-test">
|
<div class="live-test">
|
||||||
<h4>🔬 余额查询测试</h4>
|
<h4>🔬 余额查询测试</h4>
|
||||||
<div class="test-input-row">
|
<div class="test-input-row">
|
||||||
<input type="text" id="testKey" class="test-input" placeholder="sk-xp-..." spellcheck="false">
|
<input type="text" id="testKey" class="test-input" placeholder="sk-xp-... 或 sk-KKnD..." spellcheck="false">
|
||||||
<button class="btn-test" id="testBtn" onclick="runTest()">发送请求</button>
|
<button class="btn-test" id="testBtn" onclick="runTest()">发送请求</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="test-result" id="testResult">等待输入 Key...</div>
|
<div class="test-result" id="testResult">等待输入 Key...</div>
|
||||||
@@ -625,14 +625,38 @@ window.addEventListener('scroll', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── 频率限制:60秒内最多5次 ──
|
||||||
|
const _rl = { count: 0, reset: 0 };
|
||||||
|
function checkRateLimit() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now > _rl.reset) { _rl.count = 0; _rl.reset = now + 60000; }
|
||||||
|
if (_rl.count >= 5) {
|
||||||
|
const wait = Math.ceil((_rl.reset - now) / 1000);
|
||||||
|
throw new Error('请求过于频繁,请 ' + wait + ' 秒后再试');
|
||||||
|
}
|
||||||
|
_rl.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Key 格式严格校验 ──
|
||||||
|
// 格式1:sk-xp- + 32位十六进制(前端自生成)
|
||||||
|
// 格式2:sk- + 48位字母数字(new-api 后台创建)
|
||||||
|
function validateKey(key) {
|
||||||
|
return /^sk-xp-[0-9a-f]{32}$/.test(key) || /^sk-[A-Za-z0-9]{48}$/.test(key);
|
||||||
|
}
|
||||||
|
|
||||||
// 在线测试
|
// 在线测试
|
||||||
async function runTest() {
|
async function runTest() {
|
||||||
const key = document.getElementById('testKey').value.trim();
|
const key = document.getElementById('testKey').value.trim();
|
||||||
const btn = document.getElementById('testBtn');
|
const btn = document.getElementById('testBtn');
|
||||||
const out = document.getElementById('testResult');
|
const out = document.getElementById('testResult');
|
||||||
if (!key || !key.startsWith('sk-')) {
|
if (!validateKey(key)) {
|
||||||
out.className = 'test-result err';
|
out.className = 'test-result err';
|
||||||
out.textContent = '错误:请输入有效的 API Key(以 sk- 开头)';
|
out.textContent = '错误:请输入有效的 API Key(sk- 开头)';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try { checkRateLimit(); } catch(e) {
|
||||||
|
out.className = 'test-result err';
|
||||||
|
out.textContent = e.message;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
btn.disabled = true; btn.textContent = '请求中...';
|
btn.disabled = true; btn.textContent = '请求中...';
|
||||||
|
|||||||
Reference in New Issue
Block a user