Skip to content

Commit 592c62b

Browse files
committed
feat(frontend): UI for affinity_mode + per-key limits
Settings page (#150 backend): - New "Session affinity" select with bounded / off / strict options. Defaults to bounded; loaded from /api/admin/settings. API Keys page (#146 backend): - New collapsible "Advanced limits" section in both create and edit modals, auto-expanded when any limit is configured. - Model allowlist/denylist via ChipInput, sourced from current /api/admin/models so users can pick existing model IDs but also type custom ones. - Six numeric fields: rpm, rpd, cost_limit_5h/7d, token_limit_5h/7d with appropriate suffix labels ($, tk, /min, /day) and step. - limitsFromAPIKey / limitsFormToPayload bridge between APIKeyLimits (snake_case backend type) and the form state. - types.ts: APIKeyLimits + APIKeyRow.limits. Verified end-to-end against 2004 instance.
1 parent a425911 commit 592c62b

5 files changed

Lines changed: 339 additions & 3 deletions

File tree

frontend/src/locales/en.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,11 @@
998998
"schedulerModeDesc": "Round Robin distributes requests evenly across accounts. Remaining Quota prefers accounts with lower usage percentage.",
999999
"schedulerModeRoundRobin": "Round Robin",
10001000
"schedulerModeRemainingQuota": "Remaining Quota",
1001+
"affinityMode": "Session Affinity",
1002+
"affinityModeDesc": "Bounded (default): same session sticks to one account but auto-rotates after 50 requests, 5 minutes, or when the bound account becomes unhealthy. Off: every request re-picks via scheduler. Strict: legacy behavior, sticks long-term.",
1003+
"affinityModeBounded": "Bounded (recommended)",
1004+
"affinityModeOff": "Off (re-pick every request)",
1005+
"affinityModeStrict": "Strict (long-term sticky)",
10011006
"allowRemoteMigration": "Allow Remote Migration",
10021007
"allowRemoteMigrationDesc": "When enabled, other codex2api instances can pull healthy accounts from this instance.",
10031008
"allowRemoteMigrationRequiresSecret": "Set an admin secret first. Remote migration cannot be enabled without admin authentication.",
@@ -1250,6 +1255,27 @@
12501255
"hideKey": "Hide key",
12511256
"allowedGroups": "Allowed Groups",
12521257
"allowedGroupsHint": "Leaving this empty allows the key to use all account groups.",
1258+
"limits": {
1259+
"title": "Advanced limits",
1260+
"active": "active",
1261+
"show": "Expand",
1262+
"hide": "Collapse",
1263+
"desc": "Apply per-key model scope and sliding-window quota. Leave a field empty to skip it.",
1264+
"modelAllow": "Model allowlist",
1265+
"modelAllowPlaceholder": "Pick or type a model, press Enter",
1266+
"modelAllowHint": "When non-empty, only listed models are allowed; otherwise the denylist applies.",
1267+
"modelDeny": "Model denylist",
1268+
"modelDenyPlaceholder": "Pick or type a model to deny",
1269+
"modelDenyHint": "Used only when the allowlist is empty.",
1270+
"rpm": "Requests per minute",
1271+
"rpmSuffix": "/min",
1272+
"rpd": "Requests per day",
1273+
"rpdSuffix": "/day",
1274+
"cost5h": "Cost limit (5h)",
1275+
"cost7d": "Cost limit (7d)",
1276+
"tokens5h": "Token limit (5h)",
1277+
"tokens7d": "Token limit (7d)"
1278+
},
12531279
"allowedGroupsLabel": "Allowed Account Groups",
12541280
"allowedGroupsPlaceholder": "Choose allowed groups",
12551281
"allowedGroupsAll": "All groups",

frontend/src/locales/zh.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,11 @@
998998
"schedulerModeDesc": "轮询模式在所有账号间平均分配请求。剩余配额模式优先使用用量较低的账号。",
999999
"schedulerModeRoundRobin": "轮询",
10001000
"schedulerModeRemainingQuota": "剩余配额",
1001+
"affinityMode": "会话粘性",
1002+
"affinityModeDesc": "Bounded(默认):同一会话粘到同一账号,但累计 50 次请求或绑定 5 分钟后自动换号,绑定账号变为非健康也会换号。Off:每次请求都按调度模式重新挑号,严格分散。Strict:旧行为,长期粘到底。",
1003+
"affinityModeBounded": "Bounded(推荐)",
1004+
"affinityModeOff": "关闭(每次重新挑号)",
1005+
"affinityModeStrict": "Strict(长期粘连)",
10011006
"allowRemoteMigration": "允许远程迁移",
10021007
"allowRemoteMigrationDesc": "开启后,其他 codex2api 实例可通过导出接口拉取本实例的健康账号。",
10031008
"allowRemoteMigrationRequiresSecret": "请先设置管理密钥,未设置密码时不能启用远程迁移。",
@@ -1250,6 +1255,27 @@
12501255
"hideKey": "隐藏密钥",
12511256
"allowedGroups": "允许分组",
12521257
"allowedGroupsHint": "不选择表示该密钥可使用全部账号分组。",
1258+
"limits": {
1259+
"title": "高级限制",
1260+
"active": "已启用",
1261+
"show": "展开",
1262+
"hide": "收起",
1263+
"desc": "对该密钥单独施加模型范围与时间窗口配额。空字段表示不限。",
1264+
"modelAllow": "模型白名单",
1265+
"modelAllowPlaceholder": "选择或输入模型名,Enter 添加",
1266+
"modelAllowHint": "白名单非空时,只允许调用其中的模型;为空则使用黑名单。",
1267+
"modelDeny": "模型黑名单",
1268+
"modelDenyPlaceholder": "选择或输入要拒绝的模型",
1269+
"modelDenyHint": "白名单为空时生效。",
1270+
"rpm": "每分钟请求",
1271+
"rpmSuffix": "/min",
1272+
"rpd": "每天请求",
1273+
"rpdSuffix": "/day",
1274+
"cost5h": "5h 成本上限",
1275+
"cost7d": "7d 成本上限",
1276+
"tokens5h": "5h Token 上限",
1277+
"tokens7d": "7d Token 上限"
1278+
},
12531279
"allowedGroupsLabel": "允许账号分组",
12541280
"allowedGroupsPlaceholder": "选择允许调用的分组",
12551281
"allowedGroupsAll": "全部分组",

0 commit comments

Comments
 (0)