mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 11:20:50 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a59ab73a1 | ||
|
|
66d58288b4 | ||
|
|
be3f58f0a8 | ||
|
|
c299e403cc |
@@ -361,10 +361,13 @@
|
|||||||
"title": "Antigravity Quota",
|
"title": "Antigravity Quota",
|
||||||
"empty_title": "No Antigravity Auth Files",
|
"empty_title": "No Antigravity Auth Files",
|
||||||
"empty_desc": "Upload an Antigravity credential to view remaining quota.",
|
"empty_desc": "Upload an Antigravity credential to view remaining quota.",
|
||||||
|
"idle": "Not loaded. Click Refresh Button.",
|
||||||
"loading": "Loading quota...",
|
"loading": "Loading quota...",
|
||||||
"load_failed": "Failed to load quota: {{message}}",
|
"load_failed": "Failed to load quota: {{message}}",
|
||||||
"missing_auth_index": "Auth file missing auth_index",
|
"missing_auth_index": "Auth file missing auth_index",
|
||||||
"empty_models": "No quota data available"
|
"empty_models": "No quota data available",
|
||||||
|
"refresh_button": "Refresh Quota",
|
||||||
|
"fetch_all": "Fetch All"
|
||||||
},
|
},
|
||||||
"vertex_import": {
|
"vertex_import": {
|
||||||
"title": "Vertex JSON Login",
|
"title": "Vertex JSON Login",
|
||||||
|
|||||||
@@ -361,10 +361,13 @@
|
|||||||
"title": "Antigravity 额度",
|
"title": "Antigravity 额度",
|
||||||
"empty_title": "暂无 Antigravity 认证",
|
"empty_title": "暂无 Antigravity 认证",
|
||||||
"empty_desc": "上传 Antigravity 认证文件后即可查看额度。",
|
"empty_desc": "上传 Antigravity 认证文件后即可查看额度。",
|
||||||
|
"idle": "尚未加载额度,请点击刷新按钮。",
|
||||||
"loading": "正在加载额度...",
|
"loading": "正在加载额度...",
|
||||||
"load_failed": "额度获取失败:{{message}}",
|
"load_failed": "额度获取失败:{{message}}",
|
||||||
"missing_auth_index": "认证文件缺少 auth_index",
|
"missing_auth_index": "认证文件缺少 auth_index",
|
||||||
"empty_models": "暂无额度数据"
|
"empty_models": "暂无额度数据",
|
||||||
|
"refresh_button": "刷新额度",
|
||||||
|
"fetch_all": "获取全部"
|
||||||
},
|
},
|
||||||
"vertex_import": {
|
"vertex_import": {
|
||||||
"title": "Vertex JSON 登录",
|
"title": "Vertex JSON 登录",
|
||||||
|
|||||||
@@ -176,6 +176,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.antigravityControls {
|
||||||
|
display: flex;
|
||||||
|
gap: $spacing-md;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
margin-bottom: $spacing-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.antigravityControl {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.antigravityCard {
|
.antigravityCard {
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ interface AntigravityQuotaGroupDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ANTIGRAVITY_QUOTA_URLS = [
|
const ANTIGRAVITY_QUOTA_URLS = [
|
||||||
'https://cloudcode-pa-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
|
'https://daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels',
|
||||||
'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
|
'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
|
||||||
'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels'
|
'https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels'
|
||||||
];
|
];
|
||||||
@@ -171,6 +171,8 @@ const ANTIGRAVITY_QUOTA_GROUPS: AntigravityQuotaGroupDefinition[] = [
|
|||||||
labelFromModel: true
|
labelFromModel: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致)
|
// 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致)
|
||||||
function normalizeAuthIndexValue(value: unknown): string | null {
|
function normalizeAuthIndexValue(value: unknown): string | null {
|
||||||
@@ -393,6 +395,7 @@ export function AuthFilesPage() {
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(9);
|
const [pageSize, setPageSize] = useState(9);
|
||||||
const [antigravityPage, setAntigravityPage] = useState(1);
|
const [antigravityPage, setAntigravityPage] = useState(1);
|
||||||
|
const [antigravityPageSize, setAntigravityPageSize] = useState(6);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [deleting, setDeleting] = useState<string | null>(null);
|
const [deleting, setDeleting] = useState<string | null>(null);
|
||||||
const [deletingAll, setDeletingAll] = useState(false);
|
const [deletingAll, setDeletingAll] = useState(false);
|
||||||
@@ -402,6 +405,9 @@ export function AuthFilesPage() {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const [antigravityLoading, setAntigravityLoading] = useState(false);
|
const [antigravityLoading, setAntigravityLoading] = useState(false);
|
||||||
|
const [antigravityLoadingScope, setAntigravityLoadingScope] = useState<
|
||||||
|
'page' | 'all' | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
// 详情弹窗相关
|
// 详情弹窗相关
|
||||||
const [detailModalOpen, setDetailModalOpen] = useState(false);
|
const [detailModalOpen, setDetailModalOpen] = useState(false);
|
||||||
@@ -508,7 +514,6 @@ export function AuthFilesPage() {
|
|||||||
[files]
|
[files]
|
||||||
);
|
);
|
||||||
|
|
||||||
const antigravityPageSize = 6;
|
|
||||||
const antigravityTotalPages = Math.max(
|
const antigravityTotalPages = Math.max(
|
||||||
1,
|
1,
|
||||||
Math.ceil(antigravityFiles.length / antigravityPageSize)
|
Math.ceil(antigravityFiles.length / antigravityPageSize)
|
||||||
@@ -569,71 +574,77 @@ export function AuthFilesPage() {
|
|||||||
[t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadAntigravityQuota = useCallback(async () => {
|
const loadAntigravityQuota = useCallback(
|
||||||
if (antigravityLoadingRef.current) return;
|
async (targets: AuthFileItem[], scope: 'page' | 'all') => {
|
||||||
antigravityLoadingRef.current = true;
|
if (antigravityLoadingRef.current) return;
|
||||||
const requestId = ++antigravityRequestIdRef.current;
|
antigravityLoadingRef.current = true;
|
||||||
setAntigravityLoading(true);
|
const requestId = ++antigravityRequestIdRef.current;
|
||||||
|
setAntigravityLoading(true);
|
||||||
|
setAntigravityLoadingScope(scope);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (antigravityFiles.length === 0) {
|
if (targets.length === 0) return;
|
||||||
setAntigravityQuota({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadingState: Record<string, AntigravityQuotaState> = {};
|
setAntigravityQuota((prev) => {
|
||||||
antigravityFiles.forEach((file) => {
|
const nextState = { ...prev };
|
||||||
loadingState[file.name] = { status: 'loading', groups: [] };
|
targets.forEach((file) => {
|
||||||
});
|
nextState[file.name] = { status: 'loading', groups: [] };
|
||||||
setAntigravityQuota(loadingState);
|
});
|
||||||
|
return nextState;
|
||||||
|
});
|
||||||
|
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
antigravityFiles.map(async (file) => {
|
targets.map(async (file) => {
|
||||||
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
|
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
|
||||||
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
|
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
|
||||||
if (!authIndex) {
|
if (!authIndex) {
|
||||||
return {
|
return {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
status: 'error' as const,
|
status: 'error' as const,
|
||||||
error: t('antigravity_quota.missing_auth_index')
|
error: t('antigravity_quota.missing_auth_index')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const groups = await fetchAntigravityQuota(authIndex);
|
const groups = await fetchAntigravityQuota(authIndex);
|
||||||
return { name: file.name, status: 'success' as const, groups };
|
return { name: file.name, status: 'success' as const, groups };
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const message = err instanceof Error ? err.message : t('common.unknown_error');
|
const message = err instanceof Error ? err.message : t('common.unknown_error');
|
||||||
return { name: file.name, status: 'error' as const, error: message };
|
return { name: file.name, status: 'error' as const, error: message };
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (requestId !== antigravityRequestIdRef.current) return;
|
if (requestId !== antigravityRequestIdRef.current) return;
|
||||||
|
|
||||||
const nextState: Record<string, AntigravityQuotaState> = {};
|
setAntigravityQuota((prev) => {
|
||||||
results.forEach((result) => {
|
const nextState = { ...prev };
|
||||||
if (result.status === 'success') {
|
results.forEach((result) => {
|
||||||
nextState[result.name] = {
|
if (result.status === 'success') {
|
||||||
status: 'success',
|
nextState[result.name] = {
|
||||||
groups: result.groups
|
status: 'success',
|
||||||
};
|
groups: result.groups
|
||||||
} else {
|
};
|
||||||
nextState[result.name] = {
|
} else {
|
||||||
status: 'error',
|
nextState[result.name] = {
|
||||||
groups: [],
|
status: 'error',
|
||||||
error: result.error
|
groups: [],
|
||||||
};
|
error: result.error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return nextState;
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (requestId === antigravityRequestIdRef.current) {
|
||||||
|
setAntigravityLoading(false);
|
||||||
|
setAntigravityLoadingScope(null);
|
||||||
|
antigravityLoadingRef.current = false;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
setAntigravityQuota(nextState);
|
|
||||||
} finally {
|
|
||||||
if (requestId === antigravityRequestIdRef.current) {
|
|
||||||
setAntigravityLoading(false);
|
|
||||||
antigravityLoadingRef.current = false;
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, [antigravityFiles, fetchAntigravityQuota, t]);
|
[fetchAntigravityQuota, t]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFiles();
|
loadFiles();
|
||||||
@@ -646,15 +657,20 @@ export function AuthFilesPage() {
|
|||||||
setAntigravityQuota({});
|
setAntigravityQuota({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadAntigravityQuota();
|
setAntigravityQuota((prev) => {
|
||||||
}, [antigravityFiles, loadAntigravityQuota]);
|
const nextState: Record<string, AntigravityQuotaState> = {};
|
||||||
|
antigravityFiles.forEach((file) => {
|
||||||
|
const cached = prev[file.name];
|
||||||
|
if (cached) {
|
||||||
|
nextState[file.name] = cached;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return nextState;
|
||||||
|
});
|
||||||
|
}, [antigravityFiles]);
|
||||||
|
|
||||||
// 定时刷新状态数据(每240秒)
|
// 定时刷新状态数据(每240秒)
|
||||||
useInterval(loadKeyStats, 240_000);
|
useInterval(loadKeyStats, 240_000);
|
||||||
useInterval(() => {
|
|
||||||
if (antigravityFiles.length === 0) return;
|
|
||||||
loadAntigravityQuota();
|
|
||||||
}, 240_000);
|
|
||||||
|
|
||||||
// 提取所有存在的类型
|
// 提取所有存在的类型
|
||||||
const existingTypes = useMemo(() => {
|
const existingTypes = useMemo(() => {
|
||||||
@@ -1211,8 +1227,10 @@ export function AuthFilesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.quotaSection}>
|
<div className={styles.quotaSection}>
|
||||||
{quotaStatus === 'loading' || quotaStatus === 'idle' ? (
|
{quotaStatus === 'loading' ? (
|
||||||
<div className={styles.quotaMessage}>{t('antigravity_quota.loading')}</div>
|
<div className={styles.quotaMessage}>{t('antigravity_quota.loading')}</div>
|
||||||
|
) : quotaStatus === 'idle' ? (
|
||||||
|
<div className={styles.quotaMessage}>{t('antigravity_quota.idle')}</div>
|
||||||
) : quotaStatus === 'error' ? (
|
) : quotaStatus === 'error' ? (
|
||||||
<div className={styles.quotaError}>
|
<div className={styles.quotaError}>
|
||||||
{t('antigravity_quota.load_failed', {
|
{t('antigravity_quota.load_failed', {
|
||||||
@@ -1383,15 +1401,26 @@ export function AuthFilesPage() {
|
|||||||
<Card
|
<Card
|
||||||
title={t('antigravity_quota.title')}
|
title={t('antigravity_quota.title')}
|
||||||
extra={
|
extra={
|
||||||
<Button
|
<div className={styles.headerActions}>
|
||||||
variant="secondary"
|
<Button
|
||||||
size="sm"
|
variant="secondary"
|
||||||
onClick={loadAntigravityQuota}
|
size="sm"
|
||||||
disabled={disableControls || antigravityLoading || antigravityFiles.length === 0}
|
onClick={() => loadAntigravityQuota(antigravityPageItems, 'page')}
|
||||||
loading={antigravityLoading}
|
disabled={disableControls || antigravityLoading || antigravityPageItems.length === 0}
|
||||||
>
|
loading={antigravityLoading && antigravityLoadingScope === 'page'}
|
||||||
{t('common.refresh')}
|
>
|
||||||
</Button>
|
{t('antigravity_quota.refresh_button')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => loadAntigravityQuota(antigravityFiles, 'all')}
|
||||||
|
disabled={disableControls || antigravityLoading || antigravityFiles.length === 0}
|
||||||
|
loading={antigravityLoading && antigravityLoadingScope === 'all'}
|
||||||
|
>
|
||||||
|
{t('antigravity_quota.fetch_all')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{antigravityFiles.length === 0 ? (
|
{antigravityFiles.length === 0 ? (
|
||||||
@@ -1401,6 +1430,31 @@ export function AuthFilesPage() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<div className={styles.antigravityControls}>
|
||||||
|
<div className={styles.antigravityControl}>
|
||||||
|
<label>{t('auth_files.page_size_label')}</label>
|
||||||
|
<select
|
||||||
|
className={styles.pageSizeSelect}
|
||||||
|
value={antigravityPageSize}
|
||||||
|
onChange={(e) => {
|
||||||
|
setAntigravityPageSize(Number(e.target.value) || 6);
|
||||||
|
setAntigravityPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value={6}>6</option>
|
||||||
|
<option value={9}>9</option>
|
||||||
|
<option value={12}>12</option>
|
||||||
|
<option value={18}>18</option>
|
||||||
|
<option value={24}>24</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className={styles.antigravityControl}>
|
||||||
|
<label>{t('common.info')}</label>
|
||||||
|
<div className={styles.statsInfo}>
|
||||||
|
{antigravityFiles.length} {t('auth_files.files_count')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className={styles.antigravityGrid}>
|
<div className={styles.antigravityGrid}>
|
||||||
{antigravityPageItems.map(renderAntigravityCard)}
|
{antigravityPageItems.map(renderAntigravityCard)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user