a cache for slack profile pictures and emojis
at main 65 lines 1.7 kB view raw
1/** 2 * Analytics wrapper utility to eliminate boilerplate in route handlers 3 */ 4 5import type { SlackCache } from "../cache"; 6 7// Cache will be injected by the route system 8 9export type AnalyticsRecorder = (statusCode: number) => Promise<void>; 10export type RouteHandlerWithAnalytics = ( 11 request: Request, 12 recordAnalytics: AnalyticsRecorder, 13) => Promise<Response> | Response; 14 15/** 16 * Creates analytics wrapper with injected cache 17 */ 18export function createAnalyticsWrapper(cache: SlackCache) { 19 return function withAnalytics( 20 path: string, 21 method: string, 22 handler: RouteHandlerWithAnalytics, 23 ) { 24 return async (request: Request): Promise<Response> => { 25 const startTime = Date.now(); 26 27 const recordAnalytics: AnalyticsRecorder = async (statusCode: number) => { 28 const userAgent = request.headers.get("user-agent") || ""; 29 const ipAddress = 30 request.headers.get("x-forwarded-for") || 31 request.headers.get("x-real-ip") || 32 "unknown"; 33 const referer = request.headers.get("referer") || undefined; 34 35 // Use the pathname for dynamic paths to ensure proper endpoint grouping 36 const requestUrl = new URL(request.url); 37 const analyticsPath = path.includes(":") ? requestUrl.pathname : path; 38 39 await cache.recordRequest( 40 analyticsPath, 41 method, 42 statusCode, 43 userAgent, 44 ipAddress, 45 Date.now() - startTime, 46 referer, 47 ); 48 }; 49 50 return handler(request, recordAnalytics); 51 }; 52 }; 53} 54 55/** 56 * Type-safe analytics wrapper that automatically infers path and method 57 */ 58export function createAnalyticsHandler( 59 cache: SlackCache, 60 path: string, 61 method: string, 62) { 63 return (handler: RouteHandlerWithAnalytics) => 64 createAnalyticsWrapper(cache)(path, method, handler); 65}