a cache for slack profile pictures and emojis
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
34 // Use the actual request URL for dynamic paths, fallback to provided path
35 const analyticsPath = path.includes(":") ? request.url : path;
36
37 await cache.recordRequest(
38 analyticsPath,
39 method,
40 statusCode,
41 userAgent,
42 ipAddress,
43 Date.now() - startTime,
44 );
45 };
46
47 return handler(request, recordAnalytics);
48 };
49 };
50}
51
52/**
53 * Type-safe analytics wrapper that automatically infers path and method
54 */
55export function createAnalyticsHandler(
56 cache: SlackCache,
57 path: string,
58 method: string,
59) {
60 return (handler: RouteHandlerWithAnalytics) =>
61 createAnalyticsWrapper(cache)(path, method, handler);
62}