Download server/src/lib/client-context.ts from automindy/fokemal: direct link, hf CLI and curl.
- Browser
- Download file 2.15 kB
-
https://huggingface.co/spaces/automindy/fokemal/resolve/main/server/src/lib/client-context.ts
- Command line
-
hf download hf://spaces/automindy/fokemal/server/src/lib/client-context.ts
-
curl -L -o client-context.ts https://huggingface.co/spaces/automindy/fokemal/resolve/main/server/src/lib/client-context.ts
2.15 kB
| import { AsyncLocalStorage } from 'async_hooks'; | |
| import type { NextFunction, Request, Response } from 'express'; | |
| import { classifyClientAgent, type ClientAgent } from './client-classifier.js'; | |
| export interface ClientContext { | |
| ip: string | null; | |
| userAgent: string | null; | |
| agent: ClientAgent | null; | |
| } | |
| // Request-scoped caller identity, readable from anywhere below the middleware | |
| // without threading parameters through every logRequest() call site (the chat | |
| // proxy, responses, anthropic, fusion, embeddings and media paths all log). | |
| const storage = new AsyncLocalStorage<ClientContext>(); | |
| // Resolve the client IP. With Express's `trust proxy` disabled (the default), | |
| // this is the socket peer address and a spoofed X-Forwarded-For from a LAN | |
| // client is ignored. When TRUST_PROXY (#1024) opts into trusting a reverse | |
| // proxy, `req.ip` walks the configured trusted-proxy chain and returns the | |
| // first untrusted address — instead of trusting the leftmost caller-supplied | |
| // X-Forwarded-For value, which would let any direct caller spoof it. | |
| function resolveClientIp(req: Request): string | null { | |
| const raw = req.ip ?? req.socket.remoteAddress ?? null; | |
| // Normalize IPv4-mapped IPv6 ("::ffff:192.168.0.5" -> "192.168.0.5"). | |
| return raw?.replace(/^::ffff:/i, '') ?? null; | |
| } | |
| // Privacy opt-out: REQUEST_ANALYTICS_LOG_CLIENT=false stores nulls instead of | |
| // the caller's IP/UA. Read per request (not at module load) so tests and | |
| // embedders can toggle it without re-importing. | |
| function clientLoggingEnabled(): boolean { | |
| return process.env.REQUEST_ANALYTICS_LOG_CLIENT !== 'false'; | |
| } | |
| export function clientContextMiddleware(req: Request, _res: Response, next: NextFunction): void { | |
| if (!clientLoggingEnabled()) { | |
| storage.run({ ip: null, userAgent: null, agent: null }, next); | |
| return; | |
| } | |
| const ua = req.headers['user-agent']; | |
| storage.run({ | |
| ip: resolveClientIp(req), | |
| userAgent: typeof ua === 'string' ? ua.slice(0, 256) : null, | |
| agent: classifyClientAgent(req), | |
| }, next); | |
| } | |
| export function getClientContext(): ClientContext { | |
| return storage.getStore() ?? { ip: null, userAgent: null, agent: null }; | |
| } | |