fix: require live admin key on web auth gating

This commit is contained in:
xuhongbin
2026-03-09 12:31:08 +08:00
parent 434bbf258a
commit a054a44808
6 changed files with 167 additions and 44 deletions

View File

@@ -8,8 +8,8 @@ import { BarChartCard } from '@/src/components/bar-chart-card';
import { formatTokenValue } from '@/src/lib/formatters';
import { DonutChartCard } from '@/src/components/donut-chart-card';
import { LineTrendChart } from '@/src/components/line-trend-chart';
import { getAdminSettings, getDashboardModels, getDashboardStats, getDashboardTrend, listAccounts } from '@/src/services/admin';
import { adminConfigState } from '@/src/store/admin-config';
import { getAdminSettings, getDashboardModels, getDashboardStats, getDashboardTrend, listAllAccounts } from '@/src/services/admin';
import { adminConfigState, hasAuthenticatedAdminSession } from '@/src/store/admin-config';
const { useSnapshot } = require('valtio/react');
@@ -41,6 +41,38 @@ const RANGE_TITLE_MAP: Record<RangeKey, string> = {
'30d': '30D',
};
function hasAccountError(account: { status?: string; error_message?: string | null }) {
return Boolean(account.status === 'error' || account.error_message);
}
function hasAccountRateLimited(account: {
rate_limit_reset_at?: string | null;
extra?: Record<string, unknown>;
}) {
if (account.rate_limit_reset_at) {
const resetTime = new Date(account.rate_limit_reset_at).getTime();
if (!Number.isNaN(resetTime) && resetTime > Date.now()) {
return true;
}
}
const modelLimits = account.extra?.model_rate_limits;
if (!modelLimits || typeof modelLimits !== 'object' || Array.isArray(modelLimits)) {
return false;
}
const now = Date.now();
return Object.values(modelLimits as Record<string, unknown>).some((info) => {
if (!info || typeof info !== 'object' || Array.isArray(info)) return false;
const resetAt = (info as { rate_limit_reset_at?: unknown }).rate_limit_reset_at;
if (typeof resetAt !== 'string' || !resetAt.trim()) return false;
const resetTime = new Date(resetAt).getTime();
return !Number.isNaN(resetTime) && resetTime > now;
});
}
function getDateRange(rangeKey: RangeKey) {
const end = new Date();
const start = new Date();
@@ -136,13 +168,18 @@ function StatCard({ title, value, detail }: { title: string; value: string; deta
export default function MonitorScreen() {
const config = useSnapshot(adminConfigState);
const hasAccount = Boolean(config.baseUrl.trim());
const hasAccount = hasAuthenticatedAdminSession(config);
const [rangeKey, setRangeKey] = useState<RangeKey>('7d');
const range = useMemo(() => getDateRange(rangeKey), [rangeKey]);
const statsQuery = useQuery({ queryKey: ['monitor-stats'], queryFn: getDashboardStats, enabled: hasAccount });
const settingsQuery = useQuery({ queryKey: ['admin-settings'], queryFn: getAdminSettings, enabled: hasAccount });
const accountsQuery = useQuery({ queryKey: ['monitor-accounts'], queryFn: () => listAccounts(''), enabled: hasAccount });
const accountPageSize = Math.max(statsQuery.data?.total_accounts ?? 20, 20);
const accountsQuery = useQuery({
queryKey: ['monitor-accounts', accountPageSize],
queryFn: () => listAllAccounts(''),
enabled: hasAccount,
});
const trendQuery = useQuery({
queryKey: ['monitor-trend', rangeKey, range.start_date, range.end_date, range.granularity],
queryFn: () => getDashboardTrend(range),
@@ -168,9 +205,12 @@ export default function MonitorScreen() {
const trend = trendQuery.data?.trend ?? [];
const topModels = (modelsQuery.data?.models ?? []).slice(0, 5);
const errorMessage = getErrorMessage(statsQuery.error ?? settingsQuery.error ?? accountsQuery.error ?? trendQuery.error ?? modelsQuery.error);
const currentPageErrorAccounts = accounts.filter((item) => item.status === 'error' || item.error_message).length;
const currentPagePausedAccounts = accounts.filter((item) => item.schedulable === false && item.status !== 'error' && !item.error_message).length;
const currentPageBusyAccounts = accounts.filter((item) => (item.current_concurrency ?? 0) > 0 && item.status !== 'error' && !item.error_message).length;
const currentPageErrorAccounts = accounts.filter(hasAccountError).length;
const currentPageLimitedAccounts = accounts.filter((item) => hasAccountRateLimited(item)).length;
const currentPageBusyAccounts = accounts.filter((item) => {
if (hasAccountError(item) || hasAccountRateLimited(item)) return false;
return (item.current_concurrency ?? 0) > 0;
}).length;
const totalAccounts = stats?.total_accounts ?? accountsQuery.data?.total ?? accounts.length;
const aggregatedErrorAccounts = stats?.error_accounts ?? 0;
const errorAccounts = Math.max(aggregatedErrorAccounts, currentPageErrorAccounts);
@@ -271,7 +311,7 @@ export default function MonitorScreen() {
detail={`TPM ${formatNumber(stats?.tpm)}`}
/>
</View>
<Section title="账号概览" subtitle="总数、健康、异常和暂停状态一览">
<Section title="账号概览" subtitle="总数、健康、异常和限流状态一览">
<View style={{ flexDirection: 'row', gap: 8 }}>
<View style={{ flex: 1, backgroundColor: colors.mutedCard, borderRadius: 14, padding: 12 }}>
<Text style={{ fontSize: 11, color: '#8a8072' }}></Text>
@@ -286,11 +326,11 @@ export default function MonitorScreen() {
<Text style={{ marginTop: 6, fontSize: 18, fontWeight: '700', color: colors.danger }}>{formatNumber(errorAccounts)}</Text>
</View>
<View style={{ flex: 1, backgroundColor: colors.mutedCard, borderRadius: 14, padding: 12 }}>
<Text style={{ fontSize: 11, color: '#8a8072' }}></Text>
<Text style={{ marginTop: 6, fontSize: 18, fontWeight: '700', color: colors.text }}>{formatNumber(currentPagePausedAccounts)}</Text>
<Text style={{ fontSize: 11, color: '#8a8072' }}></Text>
<Text style={{ marginTop: 6, fontSize: 18, fontWeight: '700', color: colors.text }}>{formatNumber(currentPageLimitedAccounts)}</Text>
</View>
</View>
<Text style={{ marginTop: 10, fontSize: 12, color: colors.subtext }}> / / 使</Text>
<Text style={{ marginTop: 10, fontSize: 12, color: colors.subtext }}> / / 使</Text>
</Section>
{throughputPoints.length > 1 ? (
@@ -318,13 +358,13 @@ export default function MonitorScreen() {
<DonutChartCard
title="账号健康"
subtitle="健康、繁忙、暂停、异常分布"
subtitle="健康、繁忙、限流、异常分布"
centerLabel="总账号"
centerValue={formatNumber(totalAccounts)}
segments={[
{ label: '健康', value: healthyAccounts, color: '#1d5f55' },
{ label: '繁忙', value: currentPageBusyAccounts, color: '#d38b36' },
{ label: '暂停', value: currentPagePausedAccounts, color: '#7d7468' },
{ label: '限流', value: currentPageLimitedAccounts, color: '#7d7468' },
{ label: '异常', value: errorAccounts, color: '#a34d2d' },
]}
/>