Files
EasyCLIProxyAPI/src/services/authFileRequests.ts
T
lzt404 fe83731b94 feat: enhance credential management with cards, settings, and quota controls
- Display credentials in responsive cards with quota progress bars
- Add request statistics and detailed health and cooldown information
- Consolidate nine credential settings into a dialog with partial updates
- Reuse Codex quota reset logic and confirmation across pages
- Move notes above card actions and hide the healthy status indicator
- Add translations and regression coverage
2026-09-20 18:09:32 +08:00

71 lines
2.5 KiB
TypeScript

export const AUTH_REQUEST_BUCKET_COUNT = 20;
export type AuthRequestBucket = {
time: string;
success: number;
failure: number;
rate: number | null;
};
export type AuthFileRequestStats = {
success: number | null;
failure: number | null;
recentAvailable: boolean;
buckets: AuthRequestBucket[];
recentSuccess: number;
recentFailure: number;
recentRate: number | null;
};
function requestCount(value: unknown): number | null {
if (typeof value !== 'number' && typeof value !== 'string') return null;
if (typeof value === 'string' && !value.trim()) return null;
const count = Number(value);
return Number.isSafeInteger(count) && count >= 0 ? count : null;
}
export function authFileRequestStats(file: Record<string, unknown>): AuthFileRequestStats {
const recent = file.recent_requests ?? file.recentRequests;
const recentAvailable = Array.isArray(recent);
const buckets: AuthRequestBucket[] = (recentAvailable ? recent : [])
.slice(-AUTH_REQUEST_BUCKET_COUNT)
.map((value: unknown) => {
const bucket = value && typeof value === 'object' && !Array.isArray(value)
? value as Record<string, unknown> : {};
const success = requestCount(bucket.success) ?? 0;
const failure = requestCount(bucket.failed) ?? 0;
return {
time: typeof bucket.time === 'string' ? bucket.time.trim() : '',
success,
failure,
rate: success + failure > 0 ? success / (success + failure) : null,
};
});
while (buckets.length < AUTH_REQUEST_BUCKET_COUNT) {
buckets.unshift({ time: '', success: 0, failure: 0, rate: null });
}
const recentSuccess = buckets.reduce((total, bucket) => total + bucket.success, 0);
const recentFailure = buckets.reduce((total, bucket) => total + bucket.failure, 0);
return {
success: requestCount(file.success ?? file.successCount),
failure: requestCount(file.failed ?? file.failureCount),
recentAvailable,
buckets,
recentSuccess,
recentFailure,
recentRate: recentSuccess + recentFailure > 0
? recentSuccess / (recentSuccess + recentFailure) : null,
};
}
export function requestRateColor(rate: number): string {
const stops = [[239, 68, 68], [250, 204, 21], [34, 197, 94]];
const normalized = Math.max(0, Math.min(1, rate));
const segment = normalized < 0.5 ? 0 : 1;
const progress = segment === 0 ? normalized * 2 : (normalized - 0.5) * 2;
const color = stops[segment].map((value, index) =>
Math.round(value + (stops[segment + 1][index] - value) * progress),
);
return `rgb(${color.join(', ')})`;
}