Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/i18n/authFileList.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const authFileListZhCN = {
'quota.pluginCategory': '插件额度',
'quota.autoBalance': '按剩余额度自动均衡权重',
'quota.autoBalanceHint': '刷新额度时根据剩余比例自动更新凭证的 priority 与 weight',
"authFiles.policy.title": "请求策略",
"authFiles.policy.hint": "未设置时继承全局配置。错误规则按顺序优先生效,清空后恢复继承;正则使用 Go 语法。",
"authFiles.operations.title": "批量管理",
Expand Down Expand Up @@ -43,6 +45,8 @@ export const authFileListZhCN = {

export const authFileListEn: Record<keyof typeof authFileListZhCN, string> = {
'quota.pluginCategory': 'Plugin quotas',
'quota.autoBalance': 'Auto-balance weights',
'quota.autoBalanceHint': 'Automatically update credential priority and weight based on remaining quota when refreshed',
"authFiles.policy.title": "Request policies",
"authFiles.policy.hint": "Unset values inherit global settings. Error rules run in order; clearing restores inheritance. Regex uses Go syntax.",
"authFiles.operations.title": "Batch management",
Expand Down Expand Up @@ -86,6 +90,8 @@ export const authFileListEn: Record<keyof typeof authFileListZhCN, string> = {

export const authFileListJa: Record<keyof typeof authFileListZhCN, string> = {
'quota.pluginCategory': 'プラグイン割当量',
'quota.autoBalance': '残量に応じて重みを自動調整',
'quota.autoBalanceHint': 'クォータ更新時に残り割合に応じて認証情報の priority と weight を自動更新します',
"authFiles.policy.title": "リクエストポリシー",
"authFiles.policy.hint": "未設定は全体設定を継承します。ルールは順番に適用され、空にすると継承に戻ります。正規表現は Go 構文です。",
"authFiles.operations.title": "一括管理",
Expand Down
7 changes: 7 additions & 0 deletions src/pages/AuthFileManagementPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { refreshCredential } from '../services/authFileOperations';
import { EmptyState } from '../components/EmptyState';
import { Checkbox } from '../components/Checkbox';
import { commitFetchedQuota } from '../services/quotaEnrichment';
import { applyQuotaRoutingChange, subscribeQuotaRoutingChanges } from '../services/quotaAutoBalance';
import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useConfirmation } from '../components/ConfirmationDialog';
import { QuotaActionFeedback } from '../components/QuotaActionFeedback';
Expand Down Expand Up @@ -137,6 +138,12 @@ export function AuthFileManagementPage({ initialProvider }: { initialProvider?:
observedAt?: string;
}>({ files: [], receivedAtMs: 0 });
const { files, receivedAtMs, observedAt } = fileSnapshot;
useEffect(() => subscribeQuotaRoutingChanges((name, fields) => {
setFileSnapshot((current) => {
const nextFiles = applyQuotaRoutingChange(current.files, name, fields);
return nextFiles === current.files ? current : { ...current, files: nextFiles };
});
}), []);
const [filter, setFilter] = useState('');
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
Expand Down
35 changes: 35 additions & 0 deletions src/pages/QuotaPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { Notice } from '../components/Notice';
import { EmptyState } from '../components/EmptyState';
import { SegmentedControl } from '../components/SegmentedControl';
import { Switch } from '../components/Switch';
import { commitFetchedQuota } from '../services/quotaEnrichment';
import {
applyQuotaRoutingChange,
restoreQuotaRoutingOriginals,
subscribeQuotaRoutingChanges,
syncCredentialQuotaRouting,
useQuotaAutoBalance,
} from '../services/quotaAutoBalance';
import { ResetCreditExpiries } from '../components/ResetCreditExpiries';
import { MessageNotice } from '../appNotice';
import { useCallback, useEffect, useMemo, useState } from 'react';
Expand Down Expand Up @@ -64,8 +72,28 @@ export function QuotaPage() {
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState('');
const [category, setCategory] = useState<'all' | QuotaDisplayProvider>('all');
const [autoBalance, setAutoBalance] = useQuotaAutoBalance();
const querying = Object.values(quotas).some((quota) => quota.status === 'loading');

useEffect(() => subscribeQuotaRoutingChanges((name, fields) => {
setFiles((current) => applyQuotaRoutingChange(current, name, fields));
}), []);

const handleToggleAutoBalance = useCallback((enabled: boolean) => {
setAutoBalance(enabled);
if (!enabled) {
void restoreQuotaRoutingOriginals().catch((restoreError) => setError(String(restoreError)));
return;
}
const snapshot = getQuotaCacheSnapshot();
files.forEach((file) => {
const quota = snapshot[quotaKey(file)];
if (quota?.status === 'success') {
void syncCredentialQuotaRouting(file, quota).catch(() => {});
}
});
}, [files, setAutoBalance]);

const loadFiles = useCallback(async () => {
setLoading(true);
setError('');
Expand Down Expand Up @@ -156,6 +184,13 @@ export function QuotaPage() {
icon={<Gauge size={18} />}
summary={t(files.length === 1 ? 'quota.queryableCredentials.one' : 'quota.queryableCredentials.other', { count: files.length })}
>
<Switch
size="sm"
checked={autoBalance}
onChange={(event) => handleToggleAutoBalance(event.currentTarget.checked)}
label={t('quota.autoBalance')}
title={t('quota.autoBalanceHint')}
/>
<button type="button" className="secondary-button compact-button" onClick={() => void loadFiles()} disabled={loading || refreshing || querying}>
{loading ? <RefreshCw size={16} className="spin" /> : <ListRestart size={16} />}{t('quota.readList')}
</button>
Expand Down
Loading