mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
- 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.
127 lines
3.8 KiB
TypeScript
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,
|
|
};
|
|
}
|