fix(auth-files): sync quota priority display and credential ordering
Read OAuth priorities in quota cards and sort credentials by descending priority followed by filename order across OAuth and quota pages.
This commit is contained in:
1 parent
46a9d3d449
commit
a08f38df7f
5 files changed
+54
-4
No files matched your search
@@ -69,6 +69,7 @@ import {
|
||||
parseAuthFilePriority,
|
||||
resetAuthFileCooldown,
|
||||
setOAuthCredentialFileDisabled,
|
||||
sortAuthFilesByPriority,
|
||||
} from '../services/authFiles';
|
||||
import { authFileHealth, normalizeAuthFileCooldowns } from '../services/authFileHealth';
|
||||
import { codexMetadataFor } from '../services/quotaMetadata';
|
||||
@@ -175,7 +176,7 @@ export function AuthFileManagementPage() {
|
||||
try {
|
||||
const payload = await managementApi.get('/credentials');
|
||||
if (!mountedRef.current || requestId !== fileRequestRef.current) return;
|
||||
const nextFiles = dedupeAuthFiles(responseList(payload, 'files'));
|
||||
const nextFiles = sortAuthFilesByPriority(dedupeAuthFiles(responseList(payload, 'files')));
|
||||
setFileSnapshot({ files: nextFiles, receivedAtMs: Date.now(), observedAt: readString(payload, 'observed_at') });
|
||||
const validQuotaKeys = new Set(nextFiles.map(quotaKey));
|
||||
pruneQuotaCache(validQuotaKeys);
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
updateQuotaCache,
|
||||
useQuotaCache,
|
||||
} from '../services/quotaCache';
|
||||
import { dedupeAuthFiles } from '../services/authFiles';
|
||||
import { dedupeAuthFiles, parseAuthFilePriority, sortAuthFilesByPriority } from '../services/authFiles';
|
||||
import { useI18n } from '../i18n';
|
||||
import './QuotaPage.css';
|
||||
|
||||
@@ -65,7 +65,7 @@ export function QuotaPage() {
|
||||
setError('');
|
||||
try {
|
||||
const payload = await managementApi.get('/credentials');
|
||||
const allFiles = dedupeAuthFiles(responseList(payload, 'files'));
|
||||
const allFiles = sortAuthFilesByPriority(dedupeAuthFiles(responseList(payload, 'files')));
|
||||
const nextFiles = allFiles.filter((file) => !readBoolean(file, 'disabled') && providerForFile(file));
|
||||
setFiles(nextFiles);
|
||||
const validQuotaKeys = new Set(allFiles.map(quotaKey));
|
||||
@@ -188,6 +188,7 @@ export function QuotaCard({ file, quota, onRefresh, onReset }: { file: AuthFile;
|
||||
const provider = providerForFile(file);
|
||||
const name = fileName(file);
|
||||
const disabled = readBoolean(file, 'disabled');
|
||||
const priority = parseAuthFilePriority(file.priority) ?? 0;
|
||||
return (
|
||||
<article className="panel real-quota-card">
|
||||
<div className="real-quota-card-header">
|
||||
@@ -196,7 +197,7 @@ export function QuotaCard({ file, quota, onRefresh, onReset }: { file: AuthFile;
|
||||
<div><strong title={name}>{name}</strong><span>{provider ? providerMeta[provider].label : t('quota.unknownProvider')}</span></div>
|
||||
</div>
|
||||
<div className="quota-plan"><strong>{quota.plan || '—'}</strong><span>{quota.subscriptionActiveUntil ? t('quota.expiresAt', { time: formatQuotaTimestamp(quota.subscriptionActiveUntil, locale) }) : ''}</span></div>
|
||||
<div className="quota-status"><span className={quota.status === 'success' ? 'quota-status-badge ready' : quota.status === 'error' ? 'quota-status-badge error' : 'quota-status-badge'}>{quota.status === 'success' ? t('common.enabled') : quota.status === 'loading' ? t('quota.querying') : quota.status === 'error' ? t('common.unavailable') : t('quota.notFetched')}</span><small>{t('authFiles.settings.priority')} 0</small></div>
|
||||
<div className="quota-status"><span className={quota.status === 'success' ? 'quota-status-badge ready' : quota.status === 'error' ? 'quota-status-badge error' : 'quota-status-badge'}>{quota.status === 'success' ? t('common.enabled') : quota.status === 'loading' ? t('quota.querying') : quota.status === 'error' ? t('common.unavailable') : t('quota.notFetched')}</span><small>{t('authFiles.settings.priority')} {priority}</small></div>
|
||||
<div className="quota-card-actions">
|
||||
<button type="button" className="icon-button quiet" onClick={onRefresh} disabled={disabled || quota.status === 'loading'} title={disabled ? t('quota.fileDisabled') : t('quota.refresh')} aria-label={disabled ? t('quota.fileDisabled') : t('quota.refresh')}><RefreshCw size={16} className={quota.status === 'loading' ? 'spin' : ''} aria-hidden="true" /></button>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,19 @@ export const resetAuthFileCooldown = async (
|
||||
export const authFileName = (file: AuthFileRecord) =>
|
||||
readString(file, 'name') || translate(getCurrentLocale(), 'authFiles.unnamed');
|
||||
|
||||
/**
|
||||
* Present credentials in the same order used by routing: higher explicit
|
||||
* priority first, then a deterministic case-insensitive filename order.
|
||||
* Return a new array so callers do not mutate the API response or snapshots.
|
||||
*/
|
||||
export const sortAuthFilesByPriority = <File extends AuthFileRecord>(files: File[]): File[] =>
|
||||
[...files].sort((left, right) => {
|
||||
const priorityDelta = (parseAuthFilePriority(right.priority) ?? 0)
|
||||
- (parseAuthFilePriority(left.priority) ?? 0);
|
||||
if (priorityDelta !== 0) return priorityDelta;
|
||||
return authFileName(left).localeCompare(authFileName(right), undefined, { sensitivity: 'base' });
|
||||
});
|
||||
|
||||
export const isRuntimeOnlyAuthFile = (file: AuthFileRecord) =>
|
||||
readBoolean(file, 'runtime_only', 'runtimeOnly');
|
||||
|
||||
|
||||
Reference in new issue
Block a user