From 9bfbbd65f57647a3b2bdc58c259e33ab0e4b7078 Mon Sep 17 00:00:00 2001 From: ivan_yakimkin Date: Thu, 16 Apr 2026 11:31:53 +0300 Subject: [PATCH 1/2] fix: API Manager usage stats showing 0 for all registered keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fetchUsageStats function fetched call-logs and filtered them by key.id === log.apiKeyId. However, the API key table uses UUIDs while the call-log pipeline stores a different identifier in the apiKeyId field — so the comparison never matched, yielding 0 requests for every key. Fix by sourcing request counts from the /api/usage/analytics endpoint (same data that already powers the 'API Key Breakdown' table on the Analytics tab), matched by apiKeyName. The lastUsed timestamp is still derived from call-logs, also matched by name. Both requests are issued in parallel via Promise.all to avoid increasing page load time. --- .../api-manager/ApiManagerPageClient.tsx | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx index 2b6b37fb24..1635025b97 100644 --- a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx +++ b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx @@ -166,24 +166,42 @@ export default function ApiManagerPageClient() { const fetchUsageStats = async (apiKeys: ApiKey[]) => { if (apiKeys.length === 0) return; try { - const res = await fetch("/api/usage/call-logs?limit=1000"); - if (!res.ok) return; - const logs = await res.json(); + // Fetch analytics (accurate aggregated counts) and recent call-logs + // (for lastUsed timestamps) in parallel. + // The previous approach matched call-logs by key.id === log.apiKeyId, + // but these use different ID schemes and never matched, yielding 0. + const [analyticsRes, logsRes] = await Promise.all([ + fetch("/api/usage/analytics?range=all"), + fetch("/api/usage/call-logs?limit=1000"), + ]); + + const analytics = analyticsRes.ok ? await analyticsRes.json() : null; + const byApiKey: any[] = analytics?.byApiKey || []; + const logs = logsRes.ok ? await logsRes.json() : []; + const stats: Record = {}; for (const key of apiKeys) { - const keyLogs = (logs || []).filter( - (log: any) => log.apiKeyId === key.id || log.apiKeyName === key.name + // Match analytics entry by key name (reliable across both systems) + const analyticsMatch = byApiKey.find( + (entry: any) => entry.apiKeyName === key.name ); + + // Find the most recent call-log for this key to determine lastUsed + const keyLogs = (logs || []).filter( + (log: any) => log.apiKeyName === key.name + ); + const lastUsed = + keyLogs.length > 0 + ? keyLogs.sort( + (a: any, b: any) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + )[0]?.timestamp + : null; + stats[key.id] = { - totalRequests: keyLogs.length, - lastUsed: - keyLogs.length > 0 - ? keyLogs.sort( - (a: any, b: any) => - new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() - )[0]?.timestamp - : null, + totalRequests: analyticsMatch?.requests ?? 0, + lastUsed, }; } setUsageStats(stats); From 33e7167090a7b9f97f824ca57718a43066defedc Mon Sep 17 00:00:00 2001 From: ivan_yakimkin Date: Thu, 16 Apr 2026 11:41:17 +0300 Subject: [PATCH 2/2] fix: use .find() for lastUsed instead of filter+sort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call-logs endpoint already returns entries sorted by timestamp DESC, so the first match for a given apiKeyName is guaranteed to be the most recent one. Replace O(K·M·log M) filter+sort with O(M) find. --- .../dashboard/api-manager/ApiManagerPageClient.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx index 1635025b97..a71b38a258 100644 --- a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx +++ b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx @@ -187,17 +187,11 @@ export default function ApiManagerPageClient() { (entry: any) => entry.apiKeyName === key.name ); - // Find the most recent call-log for this key to determine lastUsed - const keyLogs = (logs || []).filter( + // The call-logs endpoint returns entries sorted by timestamp DESC, + // so the first match is the most recent one. + const lastUsed = (logs || []).find( (log: any) => log.apiKeyName === key.name - ); - const lastUsed = - keyLogs.length > 0 - ? keyLogs.sort( - (a: any, b: any) => - new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() - )[0]?.timestamp - : null; + )?.timestamp || null; stats[key.id] = { totalRequests: analyticsMatch?.requests ?? 0,