租户配额与计费:按用户月度 USD 上限 + 建任务/分支/重试准入 402 + 管理/自查端点 + 控制台展示
This commit is contained in:
1 parent
2a7cdd582f
commit
970b181bab
5 files changed
+496
-1
No files matched your search
+22
-1
@@ -87,6 +87,7 @@
|
||||
<button id="clearKey">清除</button>
|
||||
<span id="keyState" class="muted"></span>
|
||||
</div>
|
||||
<div><span id="quotaState" class="muted">未加载</span></div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
@@ -292,6 +293,7 @@ async function api(path, method, body) {
|
||||
var msg = (data && data.error) ? data.error : ("HTTP " + r.status);
|
||||
if (r.status === 401) msg += "(key 未填或无效)";
|
||||
if (r.status === 429) msg += "(触发限流,稍后再试)";
|
||||
if (r.status === 402) msg += "(本月配额已用完,联系管理员调整)";
|
||||
throw new Error(msg);
|
||||
}
|
||||
return data;
|
||||
@@ -324,6 +326,23 @@ async function loadHealth() {
|
||||
} catch (e) { $("#health").textContent = "加载失败:" + e.message; }
|
||||
}
|
||||
|
||||
async function loadQuota() {
|
||||
// 本月配额与用量:只读展示,管理员改配额走 API(/admin/quotas)。
|
||||
var el = $("#quotaState");
|
||||
if (!getKey()) { el.textContent = "未填 key,配额不显示"; return; }
|
||||
try {
|
||||
var d = await api("/users/me/quota", "GET");
|
||||
var used = (d.used_usd_micros / 1000000).toFixed(2);
|
||||
if (d.quota_usd_micros === null || d.quota_usd_micros === undefined) {
|
||||
el.textContent = "本月用量 $" + used + "(不限额)";
|
||||
} else {
|
||||
var q = (d.quota_usd_micros / 1000000).toFixed(2);
|
||||
var rem = (d.remaining_usd_micros / 1000000).toFixed(2);
|
||||
el.textContent = "本月用量 $" + used + " / $" + q + "(剩余 $" + rem + ")";
|
||||
}
|
||||
} catch (e) { el.textContent = "配额加载失败:" + e.message; }
|
||||
}
|
||||
|
||||
async function loadProjects() {
|
||||
try {
|
||||
var d = await api("/projects", "GET");
|
||||
@@ -610,6 +629,7 @@ $("#saveKey").onclick = function () {
|
||||
loadWorkers();
|
||||
if ($("#projectSelect").value) { loadTasks(); loadEnvironments(); }
|
||||
if (getKey()) loadRelayModels();
|
||||
loadQuota();
|
||||
};
|
||||
$("#clearKey").onclick = function () { localStorage.removeItem(KEY_STORE); setKeyState(); };
|
||||
$("#refreshHealth").onclick = loadHealth;
|
||||
@@ -789,11 +809,12 @@ setKeyState();
|
||||
loadHealth();
|
||||
loadProjects();
|
||||
loadWorkers();
|
||||
loadQuota();
|
||||
// 回访用户 key 已在 localStorage 里:初始化也要加载模型列表,
|
||||
// 否则下拉永远只有默认项(此前只在点“保存”时调一次,
|
||||
// 2026-10-27 实测:存过 key 后重开页面模型选不了)。
|
||||
if (getKey()) loadRelayModels();
|
||||
setInterval(function () { if ($("#projectSelect").value) { loadTasks(); loadEnvironments(); } loadHealth(); }, 10000);
|
||||
setInterval(function () { if ($("#projectSelect").value) { loadTasks(); loadEnvironments(); } loadHealth(); loadQuota(); }, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user