@@ -3,6 +3,8 @@ import { GitHubClient } from '../utils/github-client.js';
33import { CardRenderer } from '../components/card-renderer.js' ;
44import { createLogger } from '../common/logger.js' ;
55import sharp from 'sharp' ;
6+ import { db } from '../db/index.js' ;
7+ import { statsRequests } from '../db/schema.js' ;
68
79const logger = createLogger ( { controller : 'StatsController' } ) ;
810
@@ -70,6 +72,33 @@ export class StatsController {
7072 return res . status ( 400 ) . send ( 'Username is required' ) ;
7173 }
7274
75+ const normalizedParams = Object . entries ( req . query )
76+ . flatMap ( ( [ key , value ] ) => {
77+ if ( value === undefined || value === null ) return [ ] as Array < [ string , string ] > ;
78+ if ( Array . isArray ( value ) ) {
79+ return value . map ( ( item ) => [ key , String ( item ) ] as [ string , string ] ) ;
80+ }
81+ return [ [ key , String ( value ) ] as [ string , string ] ] ;
82+ } )
83+ . sort ( ( [ aKey , aVal ] , [ bKey , bVal ] ) => {
84+ const keyCompare = aKey . localeCompare ( bKey ) ;
85+ return keyCompare !== 0 ? keyCompare : aVal . localeCompare ( bVal ) ;
86+ } ) ;
87+
88+ const queryString = new URLSearchParams ( normalizedParams ) . toString ( ) ;
89+ const normalizedEndpoint = queryString ? `${ req . path } ?${ queryString } ` : req . path ;
90+
91+ db . insert ( statsRequests )
92+ . values ( { username, url : normalizedEndpoint , created_at : Date . now ( ) } )
93+ . onConflictDoUpdate ( {
94+ target : statsRequests . url ,
95+ set : {
96+ username,
97+ created_at : Date . now ( ) ,
98+ } ,
99+ } )
100+ . catch ( ( err : Error ) => logger . error ( 'Failed to log stats request' , err , { username, normalizedEndpoint } ) ) ;
101+
73102 // Backward compatibility: convert show_avatar to avatar_mode
74103 let finalAvatarMode = avatar_mode as string ;
75104 if ( show_avatar === 'true' ) {
0 commit comments