Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
ee4bfcbb
Commit
ee4bfcbb
authored
Mar 06, 2026
by
Elysia
Browse files
Merge remote-tracking branch 'origin/main'
parents
32d619a5
cac23020
Changes
65
Show whitespace changes
Inline
Side-by-side
frontend/src/i18n/locales/zh.ts
View file @
ee4bfcbb
...
...
@@ -133,6 +133,8 @@ export default {
requests
:
'
请求数
'
,
inputTokens
:
'
输入 Tokens
'
,
outputTokens
:
'
输出 Tokens
'
,
cacheCreationTokens
:
'
缓存创建
'
,
cacheReadTokens
:
'
缓存读取
'
,
totalTokens
:
'
总 Tokens
'
,
cost
:
'
费用
'
,
// Status
...
...
@@ -155,11 +157,19 @@ export default {
subscriptionExpires
:
'
订阅到期
'
,
// Usage stat cells
todayRequests
:
'
今日请求
'
,
todayInputTokens
:
'
今日输入
'
,
todayOutputTokens
:
'
今日输出
'
,
todayTokens
:
'
今日 Tokens
'
,
todayCacheCreation
:
'
今日缓存创建
'
,
todayCacheRead
:
'
今日缓存读取
'
,
todayCost
:
'
今日费用
'
,
rpmTpm
:
'
RPM / TPM
'
,
totalRequests
:
'
累计请求
'
,
totalInputTokens
:
'
累计输入
'
,
totalOutputTokens
:
'
累计输出
'
,
totalTokensLabel
:
'
累计 Tokens
'
,
totalCacheCreation
:
'
累计缓存创建
'
,
totalCacheRead
:
'
累计缓存读取
'
,
totalCost
:
'
累计费用
'
,
avgDuration
:
'
平均耗时
'
,
// Messages
...
...
@@ -1774,8 +1784,20 @@ export default {
stickyExemptWarning
:
'
RPM 限制 (粘性豁免) - 接近阈值
'
,
stickyExemptOver
:
'
RPM 限制 (粘性豁免) - 超限,仅粘性会话
'
},
quota
:
{
exceeded
:
'
配额已用完,账号暂停调度
'
,
normal
:
'
配额正常
'
},
},
clearRateLimit
:
'
清除速率限制
'
,
resetQuota
:
'
重置配额
'
,
quotaLimit
:
'
配额限制
'
,
quotaLimitPlaceholder
:
'
0 表示不限制
'
,
quotaLimitHint
:
'
设置最大使用额度(美元),达到后账号暂停调度。修改限额不会重置已用额度。
'
,
quotaLimitToggle
:
'
启用配额限制
'
,
quotaLimitToggleHint
:
'
开启后,当账号用量达到设定额度时自动暂停调度
'
,
quotaLimitAmount
:
'
限额金额
'
,
quotaLimitAmountHint
:
'
账号最大可用额度(美元),达到后自动暂停。修改限额不会重置已用额度。
'
,
testConnection
:
'
测试连接
'
,
reAuthorize
:
'
重新授权
'
,
refreshToken
:
'
刷新令牌
'
,
...
...
@@ -2123,10 +2145,12 @@ export default {
proxy
:
'
代理
'
,
noProxy
:
'
无代理
'
,
concurrency
:
'
并发数
'
,
loadFactor
:
'
负载因子
'
,
loadFactorHint
:
'
提高负载因子可以提高对账号的调度频率
'
,
priority
:
'
优先级
'
,
priorityHint
:
'
优先级越小的账号优先使用
'
,
billingRateMultiplier
:
'
账号计费倍率
'
,
billingRateMultiplierHint
:
'
>=0,0 表示该账号计费为 0;
仅影响账号计费
口径
'
,
billingRateMultiplierHint
:
'
0 表示不计费,
仅影响账号计费
'
,
expiresAt
:
'
过期时间
'
,
expiresAtHint
:
'
留空表示不过期
'
,
higherPriorityFirst
:
'
数值越小优先级越高
'
,
...
...
@@ -2142,6 +2166,7 @@ export default {
accountUpdated
:
'
账号更新成功
'
,
failedToCreate
:
'
创建账号失败
'
,
failedToUpdate
:
'
更新账号失败
'
,
pleaseSelectStatus
:
'
请选择有效的账号状态
'
,
mixedChannelWarningTitle
:
'
混合渠道警告
'
,
mixedChannelWarning
:
'
警告:分组 "{groupName}" 中同时包含 {currentPlatform} 和 {otherPlatform} 账号。混合使用不同渠道可能导致 thinking block 签名验证问题,会自动回退到非 thinking 模式。确定要继续吗?
'
,
pleaseEnterAccountName
:
'
请输入账号名称
'
,
...
...
frontend/src/types/index.ts
View file @
ee4bfcbb
...
...
@@ -653,6 +653,7 @@ export interface Account {
}
&
Record
<
string
,
unknown
>
)
proxy_id
:
number
|
null
concurrency
:
number
load_factor
?:
number
|
null
current_concurrency
?:
number
// Real-time concurrency count from Redis
priority
:
number
rate_multiplier
?:
number
// Account billing multiplier (>=0, 0 means free)
...
...
@@ -705,6 +706,10 @@ export interface Account {
cache_ttl_override_enabled
?:
boolean
|
null
cache_ttl_override_target
?:
string
|
null
// API Key 账号配额限制
quota_limit
?:
number
|
null
quota_used
?:
number
|
null
// 运行时状态(仅当启用对应限制时返回)
current_window_cost
?:
number
|
null
// 当前窗口费用
active_sessions
?:
number
|
null
// 当前活跃会话数
...
...
@@ -783,6 +788,7 @@ export interface CreateAccountRequest {
extra
?:
Record
<
string
,
unknown
>
proxy_id
?:
number
|
null
concurrency
?:
number
load_factor
?:
number
|
null
priority
?:
number
rate_multiplier
?:
number
// Account billing multiplier (>=0, 0 means free)
group_ids
?:
number
[]
...
...
@@ -799,6 +805,7 @@ export interface UpdateAccountRequest {
extra
?:
Record
<
string
,
unknown
>
proxy_id
?:
number
|
null
concurrency
?:
number
load_factor
?:
number
|
null
priority
?:
number
rate_multiplier
?:
number
// Account billing multiplier (>=0, 0 means free)
schedulable
?:
boolean
...
...
@@ -1098,7 +1105,8 @@ export interface TrendDataPoint {
requests
:
number
input_tokens
:
number
output_tokens
:
number
cache_tokens
:
number
cache_creation_tokens
:
number
cache_read_tokens
:
number
total_tokens
:
number
cost
:
number
// 标准计费
actual_cost
:
number
// 实际扣除
...
...
@@ -1109,6 +1117,8 @@ export interface ModelStat {
requests
:
number
input_tokens
:
number
output_tokens
:
number
cache_creation_tokens
:
number
cache_read_tokens
:
number
total_tokens
:
number
cost
:
number
// 标准计费
actual_cost
:
number
// 实际扣除
...
...
frontend/src/views/KeyUsageView.vue
View file @
ee4bfcbb
...
...
@@ -302,6 +302,8 @@
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.requests') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.inputTokens') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.outputTokens') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.cacheCreationTokens') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.cacheReadTokens') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.totalTokens') }}
</th>
<th
class=
"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-dark-400"
>
{{ t('keyUsage.cost') }}
</th>
</tr>
...
...
@@ -316,6 +318,8 @@
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.requests) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.input_tokens) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.output_tokens) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.cache_creation_tokens) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.cache_read_tokens) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right text-gray-700 dark:text-dark-200"
>
{{ fmtNum(m.total_tokens) }}
</td>
<td
class=
"px-4 py-3 text-sm tabular-nums text-right font-medium text-gray-900 dark:text-white"
>
{{ usd(m.actual_cost != null ? m.actual_cost : m.cost) }}
</td>
</tr>
...
...
@@ -694,11 +698,19 @@ const usageStatCells = computed<StatCell[]>(() => {
return
[
{
label
:
t
(
'
keyUsage.todayRequests
'
),
value
:
fmtNum
(
today
.
requests
)
},
{
label
:
t
(
'
keyUsage.todayInputTokens
'
),
value
:
fmtNum
(
today
.
input_tokens
)
},
{
label
:
t
(
'
keyUsage.todayOutputTokens
'
),
value
:
fmtNum
(
today
.
output_tokens
)
},
{
label
:
t
(
'
keyUsage.todayTokens
'
),
value
:
fmtNum
(
today
.
total_tokens
)
},
{
label
:
t
(
'
keyUsage.todayCacheCreation
'
),
value
:
fmtNum
(
today
.
cache_creation_tokens
)
},
{
label
:
t
(
'
keyUsage.todayCacheRead
'
),
value
:
fmtNum
(
today
.
cache_read_tokens
)
},
{
label
:
t
(
'
keyUsage.todayCost
'
),
value
:
usd
(
today
.
actual_cost
)
},
{
label
:
t
(
'
keyUsage.rpmTpm
'
),
value
:
`
${
usage
.
rpm
||
0
}
/
${
usage
.
tpm
||
0
}
`
},
{
label
:
t
(
'
keyUsage.totalRequests
'
),
value
:
fmtNum
(
total
.
requests
)
},
{
label
:
t
(
'
keyUsage.totalInputTokens
'
),
value
:
fmtNum
(
total
.
input_tokens
)
},
{
label
:
t
(
'
keyUsage.totalOutputTokens
'
),
value
:
fmtNum
(
total
.
output_tokens
)
},
{
label
:
t
(
'
keyUsage.totalTokensLabel
'
),
value
:
fmtNum
(
total
.
total_tokens
)
},
{
label
:
t
(
'
keyUsage.totalCacheCreation
'
),
value
:
fmtNum
(
total
.
cache_creation_tokens
)
},
{
label
:
t
(
'
keyUsage.totalCacheRead
'
),
value
:
fmtNum
(
total
.
cache_read_tokens
)
},
{
label
:
t
(
'
keyUsage.totalCost
'
),
value
:
usd
(
total
.
actual_cost
)
},
{
label
:
t
(
'
keyUsage.avgDuration
'
),
value
:
usage
.
average_duration_ms
?
`
${
Math
.
round
(
usage
.
average_duration_ms
)}
ms`
:
'
-
'
},
]
...
...
frontend/src/views/admin/AccountsView.vue
View file @
ee4bfcbb
...
...
@@ -261,7 +261,7 @@
<
AccountTestModal
:
show
=
"
showTest
"
:
account
=
"
testingAcc
"
@
close
=
"
closeTestModal
"
/>
<
AccountStatsModal
:
show
=
"
showStats
"
:
account
=
"
statsAcc
"
@
close
=
"
closeStatsModal
"
/>
<
ScheduledTestsPanel
:
show
=
"
showSchedulePanel
"
:
account
-
id
=
"
scheduleAcc?.id ?? null
"
:
model
-
options
=
"
scheduleModelOptions
"
@
close
=
"
closeSchedulePanel
"
/>
<
AccountActionMenu
:
show
=
"
menu.show
"
:
account
=
"
menu.acc
"
:
position
=
"
menu.pos
"
@
close
=
"
menu.show = false
"
@
test
=
"
handleTest
"
@
stats
=
"
handleViewStats
"
@
schedule
=
"
handleSchedule
"
@
reauth
=
"
handleReAuth
"
@
refresh
-
token
=
"
handleRefresh
"
@
reset
-
status
=
"
handleResetStatus
"
@
clear
-
rate
-
limit
=
"
handleClearRateLimit
"
/>
<
AccountActionMenu
:
show
=
"
menu.show
"
:
account
=
"
menu.acc
"
:
position
=
"
menu.pos
"
@
close
=
"
menu.show = false
"
@
test
=
"
handleTest
"
@
stats
=
"
handleViewStats
"
@
schedule
=
"
handleSchedule
"
@
reauth
=
"
handleReAuth
"
@
refresh
-
token
=
"
handleRefresh
"
@
reset
-
status
=
"
handleResetStatus
"
@
clear
-
rate
-
limit
=
"
handleClearRateLimit
"
@
reset
-
quota
=
"
handleResetQuota
"
/>
<
SyncFromCrsModal
:
show
=
"
showSync
"
@
close
=
"
showSync = false
"
@
synced
=
"
reload
"
/>
<
ImportDataModal
:
show
=
"
showImportData
"
@
close
=
"
showImportData = false
"
@
imported
=
"
handleDataImported
"
/>
<
BulkEditAccountModal
:
show
=
"
showBulkEdit
"
:
account
-
ids
=
"
selIds
"
:
selected
-
platforms
=
"
selPlatforms
"
:
selected
-
types
=
"
selTypes
"
:
proxies
=
"
proxies
"
:
groups
=
"
groups
"
@
close
=
"
showBulkEdit = false
"
@
updated
=
"
handleBulkUpdated
"
/>
...
...
@@ -1125,6 +1125,16 @@ const handleClearRateLimit = async (a: Account) => {
console
.
error
(
'
Failed to clear rate limit:
'
,
error
)
}
}
const
handleResetQuota
=
async
(
a
:
Account
)
=>
{
try
{
const
updated
=
await
adminAPI
.
accounts
.
resetAccountQuota
(
a
.
id
)
patchAccountInList
(
updated
)
enterAutoRefreshSilentWindow
()
appStore
.
showSuccess
(
t
(
'
common.success
'
))
}
catch
(
error
)
{
console
.
error
(
'
Failed to reset quota:
'
,
error
)
}
}
const
handleDelete
=
(
a
:
Account
)
=>
{
deletingAcc
.
value
=
a
;
showDeleteDialog
.
value
=
true
}
const
confirmDelete
=
async
()
=>
{
if
(
!
deletingAcc
.
value
)
return
;
try
{
await
adminAPI
.
accounts
.
delete
(
deletingAcc
.
value
.
id
);
showDeleteDialog
.
value
=
false
;
deletingAcc
.
value
=
null
;
reload
()
}
catch
(
error
)
{
console
.
error
(
'
Failed to delete account:
'
,
error
)
}
}
const
handleToggleSchedulable
=
async
(
a
:
Account
)
=>
{
...
...
frontend/src/views/user/UsageView.vue
View file @
ee4bfcbb
...
...
@@ -113,6 +113,9 @@
<!-- Actions -->
<div
class=
"ml-auto flex items-center gap-3"
>
<button
@
click=
"applyFilters"
:disabled=
"loading"
class=
"btn btn-secondary"
>
{{
t
(
'
common.refresh
'
)
}}
</button>
<button
@
click=
"resetFilters"
class=
"btn btn-secondary"
>
{{
t
(
'
common.reset
'
)
}}
</button>
...
...
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment