feat(quota): add subscription expiry handling and improve UI elements

This commit is contained in:
LTbinglingfeng
2026-06-12 17:03:19 +08:00
parent 4b3daa0010
commit 67c4041939
8 changed files with 139 additions and 21 deletions
+71 -8
View File
@@ -4,11 +4,24 @@
import type { AuthFileItem } from '@/types';
import {
normalizeNumberValue,
normalizeStringValue,
normalizePlanType,
parseIdTokenPayload
parseIdTokenPayload,
} from './parsers';
const toRecord = (value: unknown): Record<string, unknown> | null => {
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
return value as Record<string, unknown>;
};
const resolveCodexAuthInfo = (value: unknown): Record<string, unknown> | null => {
const payload = parseIdTokenPayload(value);
if (!payload) return null;
const nested = toRecord(payload['https://api.openai.com/auth']);
return nested ?? payload;
};
export function extractCodexChatgptAccountId(value: unknown): string | null {
const payload = parseIdTokenPayload(value);
if (!payload) return null;
@@ -67,7 +80,7 @@ export function resolveCodexPlanType(file: AuthFileItem): string | null {
metadataIdToken?.planType,
attributes?.plan_type,
attributes?.planType,
attributes?.id_token
attributes?.id_token,
];
for (const candidate of candidates) {
@@ -78,6 +91,61 @@ export function resolveCodexPlanType(file: AuthFileItem): string | null {
return null;
}
const normalizeDateLikeValue = (value: unknown): string | number | null => {
const numberValue = normalizeNumberValue(value);
if (numberValue === 0) return null;
if (numberValue !== null) return numberValue;
const stringValue = normalizeStringValue(value);
if (!stringValue || stringValue === '0') return null;
return stringValue;
};
export function resolveCodexSubscriptionActiveUntil(file: AuthFileItem): string | number | null {
const metadata = toRecord(file.metadata);
const attributes = toRecord(file.attributes);
const idToken = resolveCodexAuthInfo(file.id_token);
const metadataIdToken = resolveCodexAuthInfo(metadata?.id_token);
const attributesIdToken = resolveCodexAuthInfo(attributes?.id_token);
const subscription = toRecord(file.subscription);
const metadataSubscription = toRecord(metadata?.subscription);
const attributesSubscription = toRecord(attributes?.subscription);
const candidates = [
file.chatgpt_subscription_active_until,
file.chatgptSubscriptionActiveUntil,
file.subscription_active_until,
file.subscriptionActiveUntil,
subscription?.active_until,
subscription?.activeUntil,
idToken?.chatgpt_subscription_active_until,
idToken?.chatgptSubscriptionActiveUntil,
metadata?.chatgpt_subscription_active_until,
metadata?.chatgptSubscriptionActiveUntil,
metadata?.subscription_active_until,
metadata?.subscriptionActiveUntil,
metadataSubscription?.active_until,
metadataSubscription?.activeUntil,
metadataIdToken?.chatgpt_subscription_active_until,
metadataIdToken?.chatgptSubscriptionActiveUntil,
attributes?.chatgpt_subscription_active_until,
attributes?.chatgptSubscriptionActiveUntil,
attributes?.subscription_active_until,
attributes?.subscriptionActiveUntil,
attributesSubscription?.active_until,
attributesSubscription?.activeUntil,
attributesIdToken?.chatgpt_subscription_active_until,
attributesIdToken?.chatgptSubscriptionActiveUntil,
];
for (const candidate of candidates) {
const value = normalizeDateLikeValue(candidate);
if (value !== null) return value;
}
return null;
}
export function extractGeminiCliProjectId(value: unknown): string | null {
if (typeof value !== 'string') return null;
const matches = Array.from(value.matchAll(/\(([^()]+)\)/g));
@@ -96,12 +164,7 @@ export function resolveGeminiCliProjectId(file: AuthFileItem): string | null {
? (file.attributes as Record<string, unknown>)
: null;
const candidates = [
file.account,
file['account'],
metadata?.account,
attributes?.account
];
const candidates = [file.account, file['account'], metadata?.account, attributes?.account];
for (const candidate of candidates) {
const projectId = extractGeminiCliProjectId(candidate);