mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-21 14:19:18 +08:00
fix(keys): reactivate exhausted keys set to unlimited
This commit is contained in:
@@ -574,8 +574,8 @@ func (s *APIKeyService) Update(ctx context.Context, id int64, userID int64, req
|
||||
// Update quota fields
|
||||
if req.Quota != nil {
|
||||
apiKey.Quota = *req.Quota
|
||||
// If quota is increased and status was quota_exhausted, reactivate
|
||||
if apiKey.Status == StatusAPIKeyQuotaExhausted && *req.Quota > apiKey.QuotaUsed {
|
||||
// If quota now has room, or is changed to unlimited, reactivate exhausted keys.
|
||||
if apiKey.Status == StatusAPIKeyQuotaExhausted && (*req.Quota <= 0 || *req.Quota > apiKey.QuotaUsed) {
|
||||
apiKey.Status = StatusActive
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ type apiKeyRepoStub struct {
|
||||
apiKey *APIKey // GetKeyAndOwnerID 的返回值
|
||||
getByIDErr error // GetKeyAndOwnerID 的错误返回值
|
||||
deleteErr error // Delete 的错误返回值
|
||||
updateErr error // Update 的错误返回值
|
||||
deletedIDs []int64 // 记录已删除的 API Key ID 列表
|
||||
updatedKeys []APIKey
|
||||
allowListByUserID bool
|
||||
listByUserIDKeys []APIKey
|
||||
listByUserIDErr error
|
||||
@@ -74,7 +76,10 @@ func (s *apiKeyRepoStub) GetByKeyForAuth(ctx context.Context, key string) (*APIK
|
||||
}
|
||||
|
||||
func (s *apiKeyRepoStub) Update(ctx context.Context, key *APIKey) error {
|
||||
panic("unexpected Update call")
|
||||
if key != nil {
|
||||
s.updatedKeys = append(s.updatedKeys, *key)
|
||||
}
|
||||
return s.updateErr
|
||||
}
|
||||
|
||||
// Delete 记录被删除的 API Key ID 并返回预设的错误。
|
||||
|
||||
@@ -174,3 +174,27 @@ func TestAPIKeyService_UpdateQuotaUsed_UsesAtomicStatePath(t *testing.T) {
|
||||
require.Equal(t, 0, repo.getByIDCalls, "fast path should not re-read API key by id")
|
||||
require.Equal(t, []string{svc.authCacheKey("sk-test-quota")}, cache.deleteAuthKeys)
|
||||
}
|
||||
|
||||
func TestAPIKeyService_Update_ReactivatesQuotaExhaustedWhenQuotaUnlimited(t *testing.T) {
|
||||
repo := &apiKeyRepoStub{
|
||||
apiKey: &APIKey{
|
||||
ID: 10,
|
||||
UserID: 7,
|
||||
Key: "sk-test-unlimited",
|
||||
Status: StatusAPIKeyQuotaExhausted,
|
||||
Quota: 10,
|
||||
QuotaUsed: 12,
|
||||
},
|
||||
}
|
||||
svc := &APIKeyService{apiKeyRepo: repo}
|
||||
quota := 0.0
|
||||
|
||||
updated, err := svc.Update(context.Background(), 10, 7, UpdateAPIKeyRequest{Quota: "a})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, StatusActive, updated.Status)
|
||||
require.Equal(t, 0.0, updated.Quota)
|
||||
require.Len(t, repo.updatedKeys, 1)
|
||||
require.Equal(t, StatusActive, repo.updatedKeys[0].Status)
|
||||
require.Equal(t, 0.0, repo.updatedKeys[0].Quota)
|
||||
}
|
||||
|
||||
@@ -1068,7 +1068,7 @@ import TablePageLayout from '@/components/layout/TablePageLayout.vue'
|
||||
import EndpointPopover from '@/components/keys/EndpointPopover.vue'
|
||||
import GroupBadge from '@/components/common/GroupBadge.vue'
|
||||
import GroupOptionItem from '@/components/common/GroupOptionItem.vue'
|
||||
import type { ApiKey, Group, PublicSettings, SubscriptionType, GroupPlatform } from '@/types'
|
||||
import type { ApiKey, Group, PublicSettings, SubscriptionType, GroupPlatform, UpdateApiKeyRequest } from '@/types'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import type { BatchApiKeyUsageStats } from '@/api/usage'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
@@ -1211,6 +1211,13 @@ const statusOptions = computed(() => [
|
||||
{ value: 'inactive', label: t('common.inactive') }
|
||||
])
|
||||
|
||||
const shouldSubmitEditStatus = (key: ApiKey, status: 'active' | 'inactive') => {
|
||||
if (key.status === 'quota_exhausted' || key.status === 'expired') {
|
||||
return status === 'active'
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Filter dropdown options
|
||||
const groupFilterOptions = computed(() => [
|
||||
{ value: '', label: t('keys.allGroups') },
|
||||
@@ -1542,10 +1549,9 @@ const handleSubmit = async () => {
|
||||
submitting.value = true
|
||||
try {
|
||||
if (showEditModal.value && selectedKey.value) {
|
||||
await keysAPI.update(selectedKey.value.id, {
|
||||
const updates: UpdateApiKeyRequest = {
|
||||
name: formData.value.name,
|
||||
group_id: formData.value.group_id,
|
||||
status: formData.value.status,
|
||||
ip_whitelist: ipWhitelist,
|
||||
ip_blacklist: ipBlacklist,
|
||||
quota: quota,
|
||||
@@ -1553,7 +1559,11 @@ const handleSubmit = async () => {
|
||||
rate_limit_5h: rateLimitData.rate_limit_5h,
|
||||
rate_limit_1d: rateLimitData.rate_limit_1d,
|
||||
rate_limit_7d: rateLimitData.rate_limit_7d,
|
||||
})
|
||||
}
|
||||
if (shouldSubmitEditStatus(selectedKey.value, formData.value.status)) {
|
||||
updates.status = formData.value.status
|
||||
}
|
||||
await keysAPI.update(selectedKey.value.id, updates)
|
||||
appStore.showSuccess(t('keys.keyUpdatedSuccess'))
|
||||
} else {
|
||||
const customKey = formData.value.use_custom_key ? formData.value.custom_key : undefined
|
||||
|
||||
Reference in New Issue
Block a user