Files
Cli-Proxy-API-Management-Ce…/src/components/providers/hooks/useProviderRecentRequests.ts
T
Supra4E8C b25f722fef Refactor provider usage tracking to utilize recent requests data
- Introduced `useProviderRecentRequests` hook to manage recent request data for providers.
- Replaced usage of `useProviderStats` with `useProviderRecentRequests` in relevant components.
- Updated `VertexSection` and other provider components to consume recent request data instead of key stats.
- Removed obsolete `useProviderStats` hook and related usage details.
- Added new utility functions for handling recent request data, including normalization and aggregation.
- Updated API service to fetch recent request usage data.
- Refactored `AuthFileCard` and related components to utilize recent request statistics.
- Cleaned up unused code and types related to previous usage stats implementation.
2026-05-02 02:53:15 +08:00

127 lines
3.8 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
import { useInterval } from '@/hooks/useInterval';
import { apiKeyUsageApi } from '@/services/api';
import {
normalizeRecentRequestBuckets,
type ApiKeyUsageResponse,
type RecentRequestBucket,
} from '@/utils/recentRequests';
const PROVIDER_RECENT_REQUESTS_STALE_TIME_MS = 240_000;
export type ProviderRecentRequests = Map<string, Map<string, RecentRequestBucket[]>>;
export type UseProviderRecentRequestsOptions = {
enabled?: boolean;
};
const EMPTY_USAGE_BY_PROVIDER: ProviderRecentRequests = new Map();
let cachedUsageByProvider: ProviderRecentRequests = EMPTY_USAGE_BY_PROVIDER;
let cachedAt = 0;
let inFlightRequest: Promise<ProviderRecentRequests> | null = null;
const normalizeProviderKey = (value: unknown): string => String(value ?? '').trim().toLowerCase();
const normalizeApiKeyUsageResponse = (payload: ApiKeyUsageResponse): ProviderRecentRequests => {
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
return EMPTY_USAGE_BY_PROVIDER;
}
const usageByProvider: ProviderRecentRequests = new Map();
Object.entries(payload).forEach(([provider, entries]) => {
const providerKey = normalizeProviderKey(provider);
if (!providerKey || !entries || typeof entries !== 'object' || Array.isArray(entries)) {
return;
}
const usageByCompositeKey = new Map<string, RecentRequestBucket[]>();
Object.entries(entries).forEach(([compositeKey, buckets]) => {
usageByCompositeKey.set(compositeKey, normalizeRecentRequestBuckets(buckets));
});
usageByProvider.set(providerKey, usageByCompositeKey);
});
return usageByProvider;
};
const fetchProviderRecentRequests = async (): Promise<ProviderRecentRequests> => {
if (!inFlightRequest) {
inFlightRequest = apiKeyUsageApi
.getUsage()
.then((payload) => {
const normalized = normalizeApiKeyUsageResponse(payload);
cachedUsageByProvider = normalized;
cachedAt = Date.now();
return normalized;
})
.finally(() => {
inFlightRequest = null;
});
}
return inFlightRequest;
};
export function useProviderRecentRequests(options: UseProviderRecentRequestsOptions = {}) {
const enabled = options.enabled ?? true;
const [usageByProvider, setUsageByProvider] = useState<ProviderRecentRequests>(
cachedUsageByProvider
);
const [isLoading, setIsLoading] = useState(false);
const loadRecentRequests = useCallback(
async (loadOptions: { force?: boolean } = {}) => {
if (!enabled) {
return EMPTY_USAGE_BY_PROVIDER;
}
const hasFreshCache =
cachedAt > 0 &&
Date.now() - cachedAt < PROVIDER_RECENT_REQUESTS_STALE_TIME_MS;
if (!loadOptions.force && hasFreshCache) {
setUsageByProvider(cachedUsageByProvider);
return cachedUsageByProvider;
}
setIsLoading(true);
try {
const nextUsage = await fetchProviderRecentRequests();
setUsageByProvider(nextUsage);
return nextUsage;
} catch {
if (cachedAt > 0) {
setUsageByProvider(cachedUsageByProvider);
}
return cachedUsageByProvider;
} finally {
setIsLoading(false);
}
},
[enabled]
);
const refreshRecentRequests = useCallback(
async () => loadRecentRequests({ force: true }),
[loadRecentRequests]
);
useEffect(() => {
setUsageByProvider(enabled ? cachedUsageByProvider : EMPTY_USAGE_BY_PROVIDER);
}, [enabled]);
useInterval(() => {
void refreshRecentRequests().catch(() => {});
}, enabled ? PROVIDER_RECENT_REQUESTS_STALE_TIME_MS : null);
return {
usageByProvider: enabled ? usageByProvider : EMPTY_USAGE_BY_PROVIDER,
isLoading: enabled ? isLoading : false,
loadRecentRequests,
refreshRecentRequests,
};
}