Codex 重置功能和显示修复

This commit is contained in:
ZheFox
2026-07-16 17:23:41 +08:00
parent 1333efdad5
commit 5cf38d1b35
5 changed files with 199 additions and 48 deletions
@@ -279,7 +279,7 @@
</div>
</div>
<div
v-if="getCodexResetCreditsDisplay(key)"
v-if="getCodexResetCreditAvailableCount(key) !== null"
class="mt-3 border-t border-border/60 pt-2"
>
<div class="flex flex-wrap items-center gap-x-1 gap-y-1 text-[10px] leading-4 text-muted-foreground">
@@ -1014,6 +1014,7 @@ import {
} from '@/utils/providerKeyStatus'
import { getGeminiCliAccountCreditsText } from '@/utils/providerKeyQuota'
import {
createCodexResetCreditIdempotencyKey,
formatCodexResetCreditCount as formatCodexResetCreditCountLabel,
formatCodexResetCreditDays,
getCodexResetCreditAvailableCount as getCodexResetCreditAvailableCountFromSnapshot,
@@ -1670,13 +1671,6 @@ async function handleClearOAuthInvalid(key: EndpointAPIKey) {
}
}
function createCodexResetCreditIdempotencyKey(): string {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID()
}
throw new Error('浏览器不支持 crypto.randomUUID,无法生成安全幂等 ID')
}
function codexResetCreditOutcomeFeedback(
result: Awaited<ReturnType<typeof consumeCodexResetCredit>>,
): { tone: 'success' | 'warning'; message: string } {
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import {
createCodexResetCreditIdempotencyKey,
formatCodexResetCreditCount,
formatCodexResetCreditDays,
getCodexResetCreditAvailableCount,
@@ -108,4 +109,22 @@ describe('codex reset credit display helpers', () => {
expect(formatCodexResetCreditDays(1)).toBe('1天')
expect(formatCodexResetCreditDays(86_401)).toBe('2天')
})
it('generates a UUID v4 with secure random bytes when randomUUID is unavailable', () => {
const idempotencyKey = createCodexResetCreditIdempotencyKey({
getRandomValues(array) {
array.set(Array.from({ length: 16 }, (_, index) => index))
return array
},
})
expect(idempotencyKey).toBe('00010203-0405-4607-8809-0a0b0c0d0e0f')
})
it('prefers the browser randomUUID implementation when available', () => {
expect(createCodexResetCreditIdempotencyKey({
randomUUID: () => 'existing-random-uuid',
getRandomValues: array => array,
})).toBe('existing-random-uuid')
})
})
@@ -61,6 +61,27 @@ export function formatCodexResetCreditCount(count: number | null | undefined): s
return `共 ${count ?? 0} 次机会`
}
interface CodexResetCreditCrypto {
randomUUID?: () => string
getRandomValues: (array: Uint8Array) => Uint8Array
}
export function createCodexResetCreditIdempotencyKey(
cryptoSource: CodexResetCreditCrypto | undefined = globalThis.crypto,
): string {
const randomUUID = cryptoSource?.randomUUID?.bind(cryptoSource)
if (randomUUID) return randomUUID()
if (!cryptoSource) {
throw new Error('浏览器不支持安全随机数,无法生成幂等 ID')
}
const bytes = cryptoSource.getRandomValues(new Uint8Array(16))
bytes[6] = (bytes[6] & 0x0f) | 0x40
bytes[8] = (bytes[8] & 0x3f) | 0x80
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('')
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
}
function codexResetCreditRemainingSeconds(
item: QuotaResetCreditSnapshot,
snapshot: QuotaResetCreditsSnapshot,