diff --git a/admin-frontend/src/api.ts b/admin-frontend/src/api.ts
index 83be9ec..16dbb38 100644
--- a/admin-frontend/src/api.ts
+++ b/admin-frontend/src/api.ts
@@ -168,6 +168,7 @@ export const api = {
owner_email?: string;
tier?: string;
rate_limit_per_min?: number;
+ quota_total?: number;
}) =>
request<{ key: string; item: import("./types").ApiKey; warning: string }>(
"/keys",
diff --git a/admin-frontend/src/components/ApiKeysPage.tsx b/admin-frontend/src/components/ApiKeysPage.tsx
index 6eeb021..4b02ff4 100644
--- a/admin-frontend/src/components/ApiKeysPage.tsx
+++ b/admin-frontend/src/components/ApiKeysPage.tsx
@@ -4,9 +4,10 @@ import type { ApiKey } from "../types";
import { Copy, KeyRound, Plus, Trash2 } from "lucide-react";
const TIERS = [
- { key: "free", label: "免费 (free)", rate: 120 },
- { key: "partner", label: "合作方 (partner)", rate: 600 },
- { key: "internal", label: "内部 (internal)", rate: 6000 },
+ { key: "free", label: "免费 (free)", rate: 120, quota: 1000 },
+ { key: "registered", label: "注册用户 (registered)", rate: 300, quota: 100000 },
+ { key: "partner", label: "合作方 (partner)", rate: 600, quota: 0 },
+ { key: "internal", label: "内部 (internal)", rate: 6000, quota: 0 },
];
function tierLabel(tier: string): string {
@@ -111,6 +112,7 @@ export default function ApiKeysPage() {
{rows.length === 0 ? (
- |
+ |
暂无密钥
|
@@ -139,6 +141,15 @@ export default function ApiKeysPage() {
{k.usage.today} / {k.usage.total}
|
+
+ {k.quota_total > 0 ? (
+ = k.quota_total ? "text-red-600" : ""}>
+ {k.usage.total.toLocaleString()} / {k.quota_total.toLocaleString()}
+
+ ) : (
+ 不限
+ )}
+ |
{k.revoked_at ? (
@@ -182,13 +193,17 @@ function CreateKeyForm({
const [ownerEmail, setOwnerEmail] = useState("");
const [tier, setTier] = useState("free");
const [rate, setRate] = useState(120);
+ const [quota, setQuota] = useState(1000);
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
function pickTier(t: string) {
setTier(t);
const def = TIERS.find((x) => x.key === t);
- if (def) setRate(def.rate);
+ if (def) {
+ setRate(def.rate);
+ setQuota(def.quota);
+ }
}
async function submit() {
@@ -204,6 +219,7 @@ function CreateKeyForm({
owner_email: ownerEmail.trim() || undefined,
tier,
rate_limit_per_min: rate,
+ quota_total: quota,
});
onCreated(res.key);
} catch (e) {
@@ -260,6 +276,16 @@ function CreateKeyForm({
onChange={(e) => setRate(Math.max(1, parseInt(e.target.value || "1", 10)))}
/>
+
+
@@ -77,7 +87,8 @@ export default function App() {
{view.name === "contribute" && (
setView({ name: "home" })} />
)}
- {view.name === "api" && }
+ {view.name === "api" && setView({ name: "account" })} />}
+ {view.name === "account" && }
|