@@ -9,12 +9,17 @@ import {
99 SecurityPanel ,
1010 LoadingSkeleton ,
1111 DashboardHeader ,
12- StatusBadge
12+ StatusBadge ,
13+ OAuthOverview ,
14+ OAuthSecurityPanel ,
15+ OAuthClientActivity ,
16+ TokenExpiration ,
17+ GrantTypeDistribution ,
18+ ToolUsagePanel
1319} from "@/components/analytics"
1420import {
1521 BarChart3 ,
1622 Globe ,
17- Users ,
1823 Activity ,
1924 Clock ,
2025 AlertTriangle ,
@@ -32,6 +37,65 @@ export default function AnalyticsPage() {
3237 p95ResponseTime : number
3338 errorRate : number
3439 }
40+ oauth ?: {
41+ totalClients : number
42+ activeTokens : number
43+ tokenRefreshRate : number
44+ pkceAdoption : number
45+ clientGrowth ?: string
46+ tokenGrowth ?: string
47+ refreshGrowth ?: string
48+ pkceGrowth ?: string
49+ clients ?: {
50+ name : string
51+ clientId : string
52+ tokenCount : number
53+ lastActivity : string
54+ grantType : string
55+ pkceEnabled : boolean
56+ } [ ]
57+ expiringTokens ?: {
58+ clientName : string
59+ tokenCount : number
60+ hoursUntilExpiry : number
61+ } [ ]
62+ grantTypes ?: {
63+ type : string
64+ count : number
65+ percentage : number
66+ } [ ]
67+ }
68+ oauthSecurity ?: {
69+ totalEvents : number
70+ invalidClientAttempts : number
71+ invalidGrantAttempts : number
72+ unauthorizedScopes : number
73+ events ?: {
74+ clientName : string
75+ clientId : string
76+ eventType : string
77+ severity : string
78+ count : number
79+ lastOccurred : string
80+ } [ ]
81+ }
82+ toolUsage ?: {
83+ tools : {
84+ toolName : string
85+ mcpMethod : string
86+ usageCount : number
87+ uniqueUsers : number
88+ avgResponseTime ?: number
89+ } [ ]
90+ geographic : {
91+ country : string
92+ city ?: string
93+ count : number
94+ percentage : number
95+ } [ ]
96+ totalCalls : number
97+ activeUsers : number
98+ }
3599 topEndpoints ?: { endpoint : string ; count : number } [ ]
36100 geography ?: { country : string ; count : number } [ ]
37101 security ?: {
@@ -104,10 +168,30 @@ export default function AnalyticsPage() {
104168
105169
106170 const getHealthStatus = ( ) => {
107- if ( ! data ?. performance ) return { status : "unknown" , color : "bg-muted" }
108- const { errorRate, avgResponseTime } = data . performance
109- if ( errorRate > 5 || avgResponseTime > 1000 ) return { status : "critical" , color : "bg-destructive" }
110- if ( errorRate > 1 || avgResponseTime > 500 ) return { status : "warning" , color : "bg-yellow-500" }
171+ if ( ! data ?. performance && ! data ?. oauth ) return { status : "unknown" , color : "bg-muted" }
172+
173+ let criticalIssues = 0
174+ let warningIssues = 0
175+
176+ // Check performance health
177+ if ( data ?. performance ) {
178+ const { errorRate, avgResponseTime } = data . performance
179+ if ( errorRate > 5 || avgResponseTime > 1000 ) criticalIssues ++
180+ else if ( errorRate > 1 || avgResponseTime > 500 ) warningIssues ++
181+ }
182+
183+ // Check OAuth security health
184+ if ( data ?. oauthSecurity ) {
185+ const { invalidClientAttempts, invalidGrantAttempts, unauthorizedScopes } = data . oauthSecurity
186+ if ( invalidClientAttempts > 10 || invalidGrantAttempts > 20 ) criticalIssues ++
187+ else if ( invalidClientAttempts > 5 || invalidGrantAttempts > 10 || unauthorizedScopes > 5 ) warningIssues ++
188+ }
189+
190+ // Check PKCE adoption
191+ if ( data ?. oauth && data . oauth . pkceAdoption < 50 ) warningIssues ++
192+
193+ if ( criticalIssues > 0 ) return { status : "critical" , color : "bg-destructive" }
194+ if ( warningIssues > 0 ) return { status : "warning" , color : "bg-yellow-500" }
111195 return { status : "healthy" , color : "bg-green-500" }
112196 }
113197
@@ -193,108 +277,72 @@ export default function AnalyticsPage() {
193277
194278 < div className = "container mx-auto px-6 py-8 max-w-7xl space-y-8" >
195279
196- { /* Performance Overview */ }
197- { data . performance && (
198- < section aria-labelledby = "performance-title" className = "space-y-6" >
199- < h2 id = "performance-title" className = "sr-only" >
200- Performance Overview
201- </ h2 >
202- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6" >
203- < MetricCard
204- title = "Total Requests"
205- value = { data . performance . totalRequests . toLocaleString ( ) }
206- icon = { BarChart3 }
207- variant = "primary"
208- change = "+12.5%"
209- changeType = "positive"
210- subtitle = "vs last period"
211- />
212- < MetricCard
213- title = "Avg Response"
214- value = { `${ data . performance . avgResponseTime } ms` }
215- icon = { Clock }
216- variant = "primary"
217- change = { data . performance . avgResponseTime < 200 ? "-8%" : "+15%" }
218- changeType = { data . performance . avgResponseTime < 200 ? "positive" : "negative" }
219- subtitle = "response time"
220- />
221- < MetricCard
222- title = "P95 Response"
223- value = { `${ data . performance . p95ResponseTime } ms` }
224- icon = { TrendingUp }
225- variant = "secondary"
226- change = "+3.2%"
227- changeType = "neutral"
228- subtitle = "95th percentile"
229- />
230- < MetricCard
231- title = "Error Rate"
232- value = { `${ data . performance . errorRate } %` }
233- icon = { data . performance . errorRate > 5 ? AlertTriangle : CheckCircle }
234- variant = "secondary"
235- change = { data . performance . errorRate > 5 ? "+2.1%" : "-0.5%" }
236- changeType = { data . performance . errorRate > 5 ? "negative" : "positive" }
237- subtitle = "error percentage"
238- />
239- </ div >
240- </ section >
280+ { /* OAuth Overview */ }
281+ { data . oauth && (
282+ < OAuthOverview
283+ totalClients = { data . oauth . totalClients }
284+ activeTokens = { data . oauth . activeTokens }
285+ tokenRefreshRate = { data . oauth . tokenRefreshRate }
286+ pkceAdoption = { data . oauth . pkceAdoption }
287+ clientGrowth = { data . oauth . clientGrowth }
288+ tokenGrowth = { data . oauth . tokenGrowth }
289+ refreshGrowth = { data . oauth . refreshGrowth }
290+ pkceGrowth = { data . oauth . pkceGrowth }
291+ />
241292 ) }
242293
243294 { /* Main Content Grid */ }
244295 < div className = "grid grid-cols-1 lg:grid-cols-3 gap-8" >
245- { /* Left Column - Endpoints & Geography */ }
296+ { /* Left Column - OAuth Client Management */ }
246297 < div className = "space-y-8" >
247- { /* Top Endpoints */ }
248- { data . topEndpoints && data . topEndpoints . length > 0 && (
249- < DataTable
250- title = "Popular Endpoints"
251- icon = { Server }
252- data = { data . topEndpoints . map ( endpoint => ( {
253- primary : endpoint . endpoint ,
254- value : endpoint . count
255- } ) ) }
256- emptyMessage = "No endpoint data available"
298+ { /* OAuth Client Activity */ }
299+ { data . oauth ?. clients && data . oauth . clients . length > 0 && (
300+ < OAuthClientActivity
301+ clients = { data . oauth . clients }
302+ title = "Active OAuth Clients"
303+ maxItems = { 6 }
257304 />
258305 ) }
259306
260- { /* Geographic Distribution */ }
261- { data . geography && data . geography . length > 0 && (
262- < DataTable
263- title = "Geographic Distribution"
264- icon = { Globe }
265- data = { data . geography . map ( country => ( {
266- primary : country . country ,
267- value : country . count
268- } ) ) }
269- emptyMessage = "No geographic data available"
270- />
307+ { /* Token Expiration Tracking */ }
308+ { data . oauth ?. expiringTokens && data . oauth . expiringTokens . length > 0 && (
309+ < TokenExpiration expiringTokens = { data . oauth . expiringTokens } />
310+ ) }
311+
312+ { /* Grant Type Distribution */ }
313+ { data . oauth ?. grantTypes && data . oauth . grantTypes . length > 0 && (
314+ < GrantTypeDistribution grantTypes = { data . oauth . grantTypes } />
271315 ) }
272316 </ div >
273317
274- { /* Middle Column - Enterprise Analytics */ }
318+ { /* Middle Column - Tool Usage & Geography */ }
275319 < div className = "space-y-8" >
276-
277-
278- { /* Tool Usage */ }
279- { data . enterprise ?. toolUsage && data . enterprise . toolUsage . length > 0 && (
280- < DataTable
281- title = "Popular Tools"
282- icon = { Activity }
283- data = { data . enterprise . toolUsage . map ( tool => ( {
284- primary : tool . toolName ,
285- secondary : tool . mcpMethod ,
286- value : `${ tool . usageCount } calls` ,
287- badge : `${ tool . uniqueUsers } users`
288- } ) ) }
289- emptyMessage = "No tool usage data available"
290- maxItems = { 4 }
320+ { /* Enhanced Tool Usage Panel */ }
321+ { data . toolUsage && (
322+ < ToolUsagePanel
323+ toolUsage = { data . toolUsage . tools }
324+ geographicUsage = { data . toolUsage . geographic }
325+ totalCalls = { data . toolUsage . totalCalls }
326+ activeUsers = { data . toolUsage . activeUsers }
291327 />
292328 ) }
293329 </ div >
294330
295- { /* Right Column - Security */ }
331+ { /* Right Column - Security Focus */ }
296332 < div className = "space-y-8" >
297- { data . security && (
333+ { /* OAuth Security Panel */ }
334+ { data . oauthSecurity && (
335+ < OAuthSecurityPanel
336+ totalEvents = { data . oauthSecurity . totalEvents }
337+ oauthEvents = { data . oauthSecurity . events }
338+ invalidClientAttempts = { data . oauthSecurity . invalidClientAttempts }
339+ invalidGrantAttempts = { data . oauthSecurity . invalidGrantAttempts }
340+ unauthorizedScopes = { data . oauthSecurity . unauthorizedScopes }
341+ />
342+ ) }
343+
344+ { /* General Security Events (Fallback) */ }
345+ { data . security && ! data . oauthSecurity && (
298346 < SecurityPanel
299347 eventCount = { data . security . eventCount || 0 }
300348 byOrganization = { data . security . byOrganization }
0 commit comments