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
f355a68b
Unverified
Commit
f355a68b
authored
Mar 06, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 06, 2026
Browse files
Merge pull request #796 from touwaeriol/feature/apikey-quota-limit
feat: add configurable spending limit for API Key accounts
parents
ae5d9c8b
95e366b6
Changes
30
Hide whitespace changes
Inline
Side-by-side
frontend/src/api/admin/accounts.ts
View file @
f355a68b
...
...
@@ -240,6 +240,18 @@ export async function clearRateLimit(id: number): Promise<Account> {
return
data
}
/**
* Reset account quota usage
* @param id - Account ID
* @returns Updated account
*/
export
async
function
resetAccountQuota
(
id
:
number
):
Promise
<
Account
>
{
const
{
data
}
=
await
apiClient
.
post
<
Account
>
(
`/admin/accounts/
${
id
}
/reset-quota`
)
return
data
}
/**
* Get temporary unschedulable status
* @param id - Account ID
...
...
@@ -576,6 +588,7 @@ export const accountsAPI = {
getTodayStats
,
getBatchTodayStats
,
clearRateLimit
,
resetAccountQuota
,
getTempUnschedulableStatus
,
resetTempUnschedulable
,
setSchedulable
,
...
...
frontend/src/components/account/AccountCapacityCell.vue
View file @
f355a68b
...
...
@@ -71,6 +71,24 @@
<span
class=
"text-[9px] opacity-60"
>
{{
rpmStrategyTag
}}
</span>
</span>
</div>
<!-- API Key 账号配额限制 -->
<div
v-if=
"showQuotaLimit"
class=
"flex items-center gap-1"
>
<span
:class=
"[
'inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[10px] font-medium',
quotaClass
]"
:title=
"quotaTooltip"
>
<svg
class=
"h-2.5 w-2.5"
fill=
"none"
viewBox=
"0 0 24 24"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
stroke-linecap=
"round"
stroke-linejoin=
"round"
d=
"M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"
/>
</svg>
<span
class=
"font-mono"
>
$
{{
formatCost
(
currentQuotaUsed
)
}}
</span>
<span
class=
"text-gray-400 dark:text-gray-500"
>
/
</span>
<span
class=
"font-mono"
>
$
{{
formatCost
(
account
.
quota_limit
)
}}
</span>
</span>
</div>
</div>
</
template
>
...
...
@@ -286,6 +304,48 @@ const rpmTooltip = computed(() => {
}
})
// 是否显示配额限制(仅 apikey 类型且设置了 quota_limit)
const
showQuotaLimit
=
computed
(()
=>
{
return
(
props
.
account
.
type
===
'
apikey
'
&&
props
.
account
.
quota_limit
!==
undefined
&&
props
.
account
.
quota_limit
!==
null
&&
props
.
account
.
quota_limit
>
0
)
})
// 当前已用配额
const
currentQuotaUsed
=
computed
(()
=>
props
.
account
.
quota_used
??
0
)
// 配额状态样式
const
quotaClass
=
computed
(()
=>
{
if
(
!
showQuotaLimit
.
value
)
return
''
const
used
=
currentQuotaUsed
.
value
const
limit
=
props
.
account
.
quota_limit
||
0
if
(
used
>=
limit
)
{
return
'
bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400
'
}
if
(
used
>=
limit
*
0.8
)
{
return
'
bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400
'
}
return
'
bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400
'
})
// 配额提示文字
const
quotaTooltip
=
computed
(()
=>
{
if
(
!
showQuotaLimit
.
value
)
return
''
const
used
=
currentQuotaUsed
.
value
const
limit
=
props
.
account
.
quota_limit
||
0
if
(
used
>=
limit
)
{
return
t
(
'
admin.accounts.capacity.quota.exceeded
'
)
}
return
t
(
'
admin.accounts.capacity.quota.normal
'
)
})
// 格式化费用显示
const
formatCost
=
(
value
:
number
|
null
|
undefined
)
=>
{
if
(
value
===
null
||
value
===
undefined
)
return
'
0
'
...
...
frontend/src/components/account/CreateAccountModal.vue
View file @
f355a68b
...
...
@@ -1227,6 +1227,9 @@
<
/div
>
<!--
API
Key
账号配额限制
-->
<
QuotaLimitCard
v
-
if
=
"
form.type === 'apikey'
"
v
-
model
=
"
editQuotaLimit
"
/>
<!--
Temp
Unschedulable
Rules
-->
<
div
class
=
"
border-t border-gray-200 pt-4 dark:border-dark-600 space-y-4
"
>
<
div
class
=
"
mb-3 flex items-center justify-between
"
>
...
...
@@ -2337,6 +2340,7 @@ import Icon from '@/components/icons/Icon.vue'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
import
GroupSelector
from
'
@/components/common/GroupSelector.vue
'
import
ModelWhitelistSelector
from
'
@/components/account/ModelWhitelistSelector.vue
'
import
QuotaLimitCard
from
'
@/components/account/QuotaLimitCard.vue
'
import
{
applyInterceptWarmup
}
from
'
@/components/account/credentialsBuilder
'
import
{
formatDateTimeLocalInput
,
parseDateTimeLocalInput
}
from
'
@/utils/format
'
import
{
createStableObjectKeyResolver
}
from
'
@/utils/stableObjectKey
'
...
...
@@ -2460,6 +2464,7 @@ const accountCategory = ref<'oauth-based' | 'apikey'>('oauth-based') // UI selec
const
addMethod
=
ref
<
AddMethod
>
(
'
oauth
'
)
// For oauth-based: 'oauth' or 'setup-token'
const
apiKeyBaseUrl
=
ref
(
'
https://api.anthropic.com
'
)
const
apiKeyValue
=
ref
(
''
)
const
editQuotaLimit
=
ref
<
number
|
null
>
(
null
)
const
modelMappings
=
ref
<
ModelMapping
[]
>
([])
const
modelRestrictionMode
=
ref
<
'
whitelist
'
|
'
mapping
'
>
(
'
whitelist
'
)
const
allowedModels
=
ref
<
string
[]
>
([])
...
...
@@ -3120,6 +3125,7 @@ const resetForm = () => {
addMethod
.
value
=
'
oauth
'
apiKeyBaseUrl
.
value
=
'
https://api.anthropic.com
'
apiKeyValue
.
value
=
''
editQuotaLimit
.
value
=
null
modelMappings
.
value
=
[]
modelRestrictionMode
.
value
=
'
whitelist
'
allowedModels
.
value
=
[...
claudeModels
]
// Default fill related models
...
...
@@ -3533,13 +3539,18 @@ const createAccountAndFinish = async (
if
(
!
applyTempUnschedConfig
(
credentials
))
{
return
}
// Inject quota_limit for apikey accounts
let
finalExtra
=
extra
if
(
type
===
'
apikey
'
&&
editQuotaLimit
.
value
!=
null
&&
editQuotaLimit
.
value
>
0
)
{
finalExtra
=
{
...(
extra
||
{
}
),
quota_limit
:
editQuotaLimit
.
value
}
}
await
doCreateAccount
({
name
:
form
.
name
,
notes
:
form
.
notes
,
platform
,
type
,
credentials
,
extra
,
extra
:
finalExtra
,
proxy_id
:
form
.
proxy_id
,
concurrency
:
form
.
concurrency
,
priority
:
form
.
priority
,
...
...
frontend/src/components/account/EditAccountModal.vue
View file @
f355a68b
...
...
@@ -759,6 +759,9 @@
<
/div
>
<
/div
>
<!--
API
Key
账号配额限制
-->
<
QuotaLimitCard
v
-
if
=
"
account?.type === 'apikey'
"
v
-
model
=
"
editQuotaLimit
"
/>
<!--
OpenAI
OAuth
Codex
官方客户端限制开关
-->
<
div
v
-
if
=
"
account?.platform === 'openai' && account?.type === 'oauth'
"
...
...
@@ -1269,6 +1272,7 @@ import Icon from '@/components/icons/Icon.vue'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
import
GroupSelector
from
'
@/components/common/GroupSelector.vue
'
import
ModelWhitelistSelector
from
'
@/components/account/ModelWhitelistSelector.vue
'
import
QuotaLimitCard
from
'
@/components/account/QuotaLimitCard.vue
'
import
{
applyInterceptWarmup
}
from
'
@/components/account/credentialsBuilder
'
import
{
formatDateTimeLocalInput
,
parseDateTimeLocalInput
}
from
'
@/utils/format
'
import
{
createStableObjectKeyResolver
}
from
'
@/utils/stableObjectKey
'
...
...
@@ -1386,6 +1390,7 @@ const openaiOAuthResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OFF
const
openaiAPIKeyResponsesWebSocketV2Mode
=
ref
<
OpenAIWSMode
>
(
OPENAI_WS_MODE_OFF
)
const
codexCLIOnlyEnabled
=
ref
(
false
)
const
anthropicPassthroughEnabled
=
ref
(
false
)
const
editQuotaLimit
=
ref
<
number
|
null
>
(
null
)
const
openAIWSModeOptions
=
computed
(()
=>
[
{
value
:
OPENAI_WS_MODE_OFF
,
label
:
t
(
'
admin.accounts.openai.wsModeOff
'
)
}
,
// TODO: ctx_pool 选项暂时隐藏,待测试完成后恢复
...
...
@@ -1541,6 +1546,14 @@ watch(
anthropicPassthroughEnabled
.
value
=
extra
?.
anthropic_passthrough
===
true
}
// Load quota limit for apikey accounts
if
(
newAccount
.
type
===
'
apikey
'
)
{
const
quotaVal
=
extra
?.
quota_limit
as
number
|
undefined
editQuotaLimit
.
value
=
(
quotaVal
&&
quotaVal
>
0
)
?
quotaVal
:
null
}
else
{
editQuotaLimit
.
value
=
null
}
// Load antigravity model mapping (Antigravity 只支持映射模式)
if
(
newAccount
.
platform
===
'
antigravity
'
)
{
const
credentials
=
newAccount
.
credentials
as
Record
<
string
,
unknown
>
|
undefined
...
...
@@ -2283,6 +2296,19 @@ const handleSubmit = async () => {
updatePayload
.
extra
=
newExtra
}
// For apikey accounts, handle quota_limit in extra
if
(
props
.
account
.
type
===
'
apikey
'
)
{
const
currentExtra
=
(
updatePayload
.
extra
as
Record
<
string
,
unknown
>
)
||
(
props
.
account
.
extra
as
Record
<
string
,
unknown
>
)
||
{
}
const
newExtra
:
Record
<
string
,
unknown
>
=
{
...
currentExtra
}
if
(
editQuotaLimit
.
value
!=
null
&&
editQuotaLimit
.
value
>
0
)
{
newExtra
.
quota_limit
=
editQuotaLimit
.
value
}
else
{
delete
newExtra
.
quota_limit
}
updatePayload
.
extra
=
newExtra
}
const
canContinue
=
await
ensureAntigravityMixedChannelConfirmed
(
async
()
=>
{
await
submitUpdateAccount
(
accountID
,
updatePayload
)
}
)
...
...
frontend/src/components/account/QuotaLimitCard.vue
0 → 100644
View file @
f355a68b
<
script
setup
lang=
"ts"
>
import
{
ref
,
watch
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
const
{
t
}
=
useI18n
()
const
props
=
defineProps
<
{
modelValue
:
number
|
null
}
>
()
const
emit
=
defineEmits
<
{
'
update:modelValue
'
:
[
value
:
number
|
null
]
}
>
()
const
enabled
=
ref
(
props
.
modelValue
!=
null
&&
props
.
modelValue
>
0
)
// Sync enabled state when modelValue changes externally (e.g. account load)
watch
(
()
=>
props
.
modelValue
,
(
val
)
=>
{
enabled
.
value
=
val
!=
null
&&
val
>
0
}
)
// When toggle is turned off, clear the value
watch
(
enabled
,
(
val
)
=>
{
if
(
!
val
)
{
emit
(
'
update:modelValue
'
,
null
)
}
})
const
onInput
=
(
e
:
Event
)
=>
{
const
raw
=
(
e
.
target
as
HTMLInputElement
).
valueAsNumber
emit
(
'
update:modelValue
'
,
Number
.
isNaN
(
raw
)
?
null
:
raw
)
}
</
script
>
<
template
>
<div
class=
"border-t border-gray-200 pt-4 dark:border-dark-600 space-y-4"
>
<div
class=
"mb-3"
>
<h3
class=
"input-label mb-0 text-base font-semibold"
>
{{
t
(
'
admin.accounts.quotaLimit
'
)
}}
</h3>
<p
class=
"mt-1 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.accounts.quotaLimitHint
'
)
}}
</p>
</div>
<div
class=
"rounded-lg border border-gray-200 p-4 dark:border-dark-600"
>
<div
class=
"mb-3 flex items-center justify-between"
>
<div>
<label
class=
"input-label mb-0"
>
{{
t
(
'
admin.accounts.quotaLimitToggle
'
)
}}
</label>
<p
class=
"mt-1 text-xs text-gray-500 dark:text-gray-400"
>
{{
t
(
'
admin.accounts.quotaLimitToggleHint
'
)
}}
</p>
</div>
<button
type=
"button"
@
click=
"enabled = !enabled"
:class=
"[
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
enabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
]"
>
<span
:class=
"[
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
enabled ? 'translate-x-5' : 'translate-x-0'
]"
/>
</button>
</div>
<div
v-if=
"enabled"
class=
"space-y-3"
>
<div>
<label
class=
"input-label"
>
{{
t
(
'
admin.accounts.quotaLimitAmount
'
)
}}
</label>
<div
class=
"relative"
>
<span
class=
"absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400"
>
$
</span>
<input
:value=
"modelValue"
@
input=
"onInput"
type=
"number"
min=
"0"
step=
"0.01"
class=
"input pl-7"
:placeholder=
"t('admin.accounts.quotaLimitPlaceholder')"
/>
</div>
<p
class=
"input-hint"
>
{{
t
(
'
admin.accounts.quotaLimitAmountHint
'
)
}}
</p>
</div>
</div>
</div>
</div>
</
template
>
frontend/src/components/admin/account/AccountActionMenu.vue
View file @
f355a68b
...
...
@@ -41,6 +41,10 @@
<Icon
name=
"clock"
size=
"sm"
/>
{{ t('admin.accounts.clearRateLimit') }}
</button>
<button
v-if=
"hasQuotaLimit"
@
click=
"$emit('reset-quota', account); $emit('close')"
class=
"flex w-full items-center gap-2 px-4 py-2 text-sm text-teal-600 hover:bg-gray-100 dark:hover:bg-dark-700"
>
<Icon
name=
"refresh"
size=
"sm"
/>
{{ t('admin.accounts.resetQuota') }}
</button>
</template>
</div>
</div>
...
...
@@ -55,7 +59,7 @@ import { Icon } from '@/components/icons'
import
type
{
Account
}
from
'
@/types
'
const
props
=
defineProps
<
{
show
:
boolean
;
account
:
Account
|
null
;
position
:
{
top
:
number
;
left
:
number
}
|
null
}
>
()
const
emit
=
defineEmits
([
'
close
'
,
'
test
'
,
'
stats
'
,
'
schedule
'
,
'
reauth
'
,
'
refresh-token
'
,
'
reset-status
'
,
'
clear-rate-limit
'
])
const
emit
=
defineEmits
([
'
close
'
,
'
test
'
,
'
stats
'
,
'
schedule
'
,
'
reauth
'
,
'
refresh-token
'
,
'
reset-status
'
,
'
clear-rate-limit
'
,
'
reset-quota
'
])
const
{
t
}
=
useI18n
()
const
isRateLimited
=
computed
(()
=>
{
if
(
props
.
account
?.
rate_limit_reset_at
&&
new
Date
(
props
.
account
.
rate_limit_reset_at
)
>
new
Date
())
{
...
...
@@ -71,6 +75,12 @@ const isRateLimited = computed(() => {
return
false
})
const
isOverloaded
=
computed
(()
=>
props
.
account
?.
overload_until
&&
new
Date
(
props
.
account
.
overload_until
)
>
new
Date
())
const
hasQuotaLimit
=
computed
(()
=>
{
return
props
.
account
?.
type
===
'
apikey
'
&&
props
.
account
?.
quota_limit
!==
undefined
&&
props
.
account
?.
quota_limit
!==
null
&&
props
.
account
?.
quota_limit
>
0
})
const
handleKeydown
=
(
event
:
KeyboardEvent
)
=>
{
if
(
event
.
key
===
'
Escape
'
)
emit
(
'
close
'
)
...
...
frontend/src/i18n/locales/en.ts
View file @
f355a68b
...
...
@@ -1734,6 +1734,10 @@ export default {
stickyExemptWarning
:
'
RPM limit (Sticky Exempt) - Approaching limit
'
,
stickyExemptOver
:
'
RPM limit (Sticky Exempt) - Over limit, sticky only
'
},
quota
:
{
exceeded
:
'
Quota exceeded, account paused
'
,
normal
:
'
Quota normal
'
},
},
tempUnschedulable
:
{
title
:
'
Temp Unschedulable
'
,
...
...
@@ -1779,6 +1783,14 @@ export default {
}
},
clearRateLimit
:
'
Clear Rate Limit
'
,
resetQuota
:
'
Reset Quota
'
,
quotaLimit
:
'
Quota Limit
'
,
quotaLimitPlaceholder
:
'
0 means unlimited
'
,
quotaLimitHint
:
'
Set max spending limit (USD). Account will be paused when reached. Changing limit won
\'
t reset usage.
'
,
quotaLimitToggle
:
'
Enable Quota Limit
'
,
quotaLimitToggleHint
:
'
When enabled, account will be paused when usage reaches the set limit
'
,
quotaLimitAmount
:
'
Limit Amount
'
,
quotaLimitAmountHint
:
'
Maximum spending limit (USD). Account will be auto-paused when reached. Changing limit won
\'
t reset usage.
'
,
testConnection
:
'
Test Connection
'
,
reAuthorize
:
'
Re-Authorize
'
,
refreshToken
:
'
Refresh Token
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
f355a68b
...
...
@@ -1784,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
:
'
刷新令牌
'
,
...
...
frontend/src/types/index.ts
View file @
f355a68b
...
...
@@ -705,6 +705,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
// 当前活跃会话数
...
...
frontend/src/views/admin/AccountsView.vue
View file @
f355a68b
...
...
@@ -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
)
=>
{
...
...
Prev
1
2
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