@@ -6,90 +6,118 @@ import User from '#models/user'
66import { DateTime } from 'luxon'
77
88export default class DashboardController {
9- async index ( { response } : HttpContext ) {
10- const stats = {
11- totalDataSources : await DataSource . query ( )
9+ async index ( { request, response, auth } : HttpContext ) {
10+ const user = auth . user !
11+ await user . load ( 'roles' )
12+ const isGlobalView = user . isAdmin
13+
14+ const timeRange = request . input ( 'timeRange' , '7d' )
15+ const daysMap : Record < string , number > = { '7d' : 7 , '30d' : 30 , '90d' : 90 , 'all' : 90 }
16+ const days = daysMap [ timeRange ] || 7
17+
18+ const stats : any = {
19+ totalQueries : await QueryLog . query ( )
20+ . if ( ! isGlobalView , q => q . where ( 'user_id' , user . id ) )
1221 . count ( '* as total' )
1322 . first ( )
1423 . then ( r => Number ( r ?. $extras . total || 0 ) ) ,
15- totalTasks : await QueryTask . query ( )
24+ }
25+
26+ if ( isGlobalView ) {
27+ stats . totalDataSources = await DataSource . query ( )
1628 . count ( '* as total' )
1729 . first ( )
18- . then ( r => Number ( r ?. $extras . total || 0 ) ) ,
19- totalQueries : await QueryLog . query ( )
30+ . then ( r => Number ( r ?. $extras . total || 0 ) )
31+ stats . totalTasks = await QueryTask . query ( )
2032 . count ( '* as total' )
2133 . first ( )
22- . then ( r => Number ( r ?. $extras . total || 0 ) ) ,
23- totalUsers : await User . query ( )
34+ . then ( r => Number ( r ?. $extras . total || 0 ) )
35+ stats . totalUsers = await User . query ( )
2436 . count ( '* as total' )
2537 . first ( )
26- . then ( r => Number ( r ?. $extras . total || 0 ) ) ,
38+ . then ( r => Number ( r ?. $extras . total || 0 ) )
2739 }
2840
29- // Get recent 7 days query activity (Success vs Failure)
30- const trend = [ ]
31- for ( let i = 6 ; i >= 0 ; i -- ) {
32- const targetDate = DateTime . local ( ) . minus ( { days : i } )
33- const startOfDay = targetDate . startOf ( 'day' ) . toSQL ( )
34- const endOfDay = targetDate . endOf ( 'day' ) . toSQL ( )
35-
36- const counts = await QueryLog . query ( )
37- . whereBetween ( 'created_at' , [ startOfDay , endOfDay ] )
38- . select ( 'status' )
39- . count ( '* as total' )
40- . groupBy ( 'status' )
41+ const startDate = DateTime . local ( ) . minus ( { days : days - 1 } ) . startOf ( 'day' ) . toSQL ( ) !
4142
42- let success = 0
43- let failed = 0
43+ // Efficient Trend JS Aggregation
44+ const trendQuery = QueryLog . query ( ) . where ( 'created_at' , '>=' , startDate ) . select ( 'created_at' , 'status' )
45+ if ( ! isGlobalView ) {
46+ trendQuery . where ( 'user_id' , user . id )
47+ }
48+ const rawTrend = await trendQuery
4449
45- counts . forEach ( ( c ) => {
46- if ( c . status === 'success' )
47- success = Number ( c . $extras . total )
48- else failed = Number ( c . $extras . total )
49- } )
50+ const trendMap = new Map ( )
51+ for ( let i = days - 1 ; i >= 0 ; i -- ) {
52+ trendMap . set ( DateTime . local ( ) . minus ( { days : i } ) . toISODate ( ) , { success : 0 , failed : 0 , total : 0 } )
53+ }
5054
51- trend . push ( { date : targetDate . toISODate ( ) , success, failed, total : success + failed } )
55+ // rawTrend created_at is a luxon DateTime in Lucid
56+ for ( const log of rawTrend ) {
57+ const d = log . createdAt . toISODate ( )
58+ if ( d && trendMap . has ( d ) ) {
59+ const obj = trendMap . get ( d )
60+ if ( log . status === 'success' )
61+ obj . success ++
62+ else obj . failed ++
63+ obj . total ++
64+ }
5265 }
5366
67+ const trend = Array . from ( trendMap . entries ( ) ) . map ( ( [ date , data ] ) => ( { date, ...data as any } ) )
68+
5469 // Top 5 Data Sources
55- const topSources = await QueryLog . query ( )
70+ const topSourcesQuery = QueryLog . query ( )
71+ . where ( 'query_logs.created_at' , '>=' , startDate )
5672 . leftJoin ( 'data_sources' , 'query_logs.data_source_id' , 'data_sources.id' )
5773 . select ( 'data_sources.name' )
5874 . count ( '* as total' )
5975 . groupBy ( 'data_sources.name' )
6076 . orderBy ( 'total' , 'desc' )
6177 . limit ( 5 )
62- . then ( rows =>
63- rows . map ( r => ( {
64- name : r . $extras . name || 'Unknown' ,
65- total : Number ( r . $extras . total ) ,
66- } ) ) ,
67- )
78+ if ( ! isGlobalView )
79+ topSourcesQuery . where ( 'query_logs.user_id' , user . id )
80+
81+ const topSources = await topSourcesQuery . then ( rows =>
82+ rows . map ( r => ( {
83+ name : r . $extras . name || 'Unknown' ,
84+ total : Number ( r . $extras . total ) ,
85+ } ) ) ,
86+ )
6887
6988 // Top 5 Users
70- const topUsers = await QueryLog . query ( )
71- . join ( 'users' , 'query_logs.user_id' , 'users.id' )
72- . select ( 'users.full_name' )
73- . count ( '* as total' )
74- . groupBy ( 'users.full_name' )
75- . orderBy ( 'total' , 'desc' )
76- . limit ( 5 )
77- . then ( rows =>
78- rows . map ( r => ( { name : r . $extras . full_name , total : Number ( r . $extras . total ) } ) ) ,
79- )
89+ let topUsers : any [ ] = [ ]
90+ if ( isGlobalView ) {
91+ topUsers = await QueryLog . query ( )
92+ . where ( 'query_logs.created_at' , '>=' , startDate )
93+ . join ( 'users' , 'query_logs.user_id' , 'users.id' )
94+ . select ( 'users.full_name' )
95+ . count ( '* as total' )
96+ . groupBy ( 'users.full_name' )
97+ . orderBy ( 'total' , 'desc' )
98+ . limit ( 5 )
99+ . then ( rows =>
100+ rows . map ( r => ( { name : r . $extras . full_name , total : Number ( r . $extras . total ) } ) ) ,
101+ )
102+ }
80103
81- const recentLogs = await QueryLog . query ( )
104+ const recentLogsQuery = QueryLog . query ( )
82105 . preload ( 'user' )
83106 . preload ( 'task' )
84107 . orderBy ( 'createdAt' , 'desc' )
85108 . limit ( 5 )
109+ if ( ! isGlobalView ) {
110+ recentLogsQuery . where ( 'user_id' , user . id )
111+ }
112+ const recentLogs = await recentLogsQuery
86113
87114 return response . ok ( {
88115 stats,
89116 trend,
90117 topSources,
91118 topUsers,
92119 recentLogs,
120+ isGlobalView, // Return this so frontend knows what to render
93121 } )
94122 }
95123}
0 commit comments