@@ -39,18 +39,32 @@ interface VisitEntry {
3939 city ?: string ;
4040 ua ?: string ;
4141 referer ?: string ;
42+ visitorId ?: string ;
4243}
4344
4445function buildVisitKey ( ts : number ) : string {
4546 const newestFirst = String ( 9_999_999_999_999 - ts ) . padStart ( 13 , '0' ) ;
4647 return `visit2:${ newestFirst } :${ ts } :${ crypto . randomUUID ( ) . slice ( 0 , 8 ) } ` ;
4748}
4849
50+ async function buildVisitorId ( request : Request , secret ?: string ) : Promise < string | undefined > {
51+ const ip = request . headers . get ( 'cf-connecting-ip' ) ?. trim ( ) ;
52+ if ( ! ip || ! secret ) {
53+ return undefined ;
54+ }
55+
56+ const payload = new TextEncoder ( ) . encode ( `${ secret } :${ ip } ` ) ;
57+ const digest = await crypto . subtle . digest ( 'SHA-256' , payload ) ;
58+ const bytes = Array . from ( new Uint8Array ( digest ) ) ;
59+ return bytes . map ( ( byte ) => byte . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) . slice ( 0 , 16 ) ;
60+ }
61+
4962async function trackVisit ( context : AppContext ) : Promise < void > {
5063 try {
5164 const request = context . request ;
5265 const url = new URL ( request . url ) ;
5366 const cfData = ( request as unknown as { cf ?: Record < string , string > } ) . cf ;
67+ const visitorId = await buildVisitorId ( request , context . env . VISITOR_NOTIFY_SECRET ) ;
5468
5569 const entry : VisitEntry = {
5670 ts : Date . now ( ) ,
@@ -59,6 +73,7 @@ async function trackVisit(context: AppContext): Promise<void> {
5973 city : cfData ?. city ,
6074 ua : ( request . headers . get ( 'user-agent' ) ?? '' ) . slice ( 0 , 200 ) ,
6175 referer : request . headers . get ( 'referer' ) ?? undefined ,
76+ visitorId,
6277 } ;
6378
6479 // Store newest-first keys so polling clients can read the latest visits efficiently.
0 commit comments