import fs from 'fs'; import path from 'path'; import { analyticsEmitter } from '../lib/analytics-emitter.js'; // import { isBotRequest, isAIRequest } from '../products/serving/bots.js'; const ANALYTICS_FILE = path.resolve(process.cwd(), 'logs/analytics.jsonl'); // Extensions to ignore const IGNORED_EXTENSIONS = new Set([ '.js', '.css', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg', '.woff', '.woff2', '.ttf', '.eot', '.map' ]); export const REVERSE_DEFAULT = { continent: 'unknown', countryName: 'unknown', city: 'unknown' }; const GEO_CACHE_FILE = path.resolve(process.cwd(), 'cache/geoip.json'); // Simple in-memory cache to reduce disk I/O, initialized on first use let geoCache = null; const loadGeoCache = () => { if (geoCache) return geoCache; try { if (fs.existsSync(GEO_CACHE_FILE)) { const data = fs.readFileSync(GEO_CACHE_FILE, 'utf-8'); geoCache = JSON.parse(data); } else { geoCache = {}; } } catch (e) { console.error('Error loading geo cache', e); geoCache = {}; } return geoCache; }; const saveGeoCache = (ip, data) => { if (!geoCache) geoCache = {}; geoCache[ip] = data; // Ensure directory exists const dir = path.dirname(GEO_CACHE_FILE); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } // Write to file (async to not block) fs.promises.writeFile(GEO_CACHE_FILE, JSON.stringify(geoCache, null, 2)).catch(err => { console.error('Error saving geo cache', err); }); }; export const reverse = async (ip, opts) => { return REVERSE_DEFAULT; /* const cache = loadGeoCache(); if (cache && cache[ip]) { return cache[ip]; } const config = CONFIG_DEFAULT() as any try { const q = `https://api-bdc.net/data/ip-geolocation?ip=${ip}&localityLanguage=en&key=${config.bigdata.key}` const ret = await axios.get(q) || { data: REVERSE_DEFAULT } const data = ret.data || REVERSE_DEFAULT saveGeoCache(ip, data); return data; } catch (e: any) { logger.error('Error reverse geocoding', e.message) return REVERSE_DEFAULT } */ }; export async function analyticsMiddleware(c, next) { await next(); // Execute the request first (non-blocking for the response?) // Wait, "await next()" blocks the middleware until the downstream handlers finish. // If we want to capture the status code, we need to wait. // The user asked for "non blocking analytics middleware". // Usually this means the *write* operation shouldn't block the response. // So we can do the logging logic *after* `await next()`, but ensuring the file write is not awaited or is fire-and-forget. try { const url = new URL(c.req.url); const pathname = url.pathname; const extension = path.extname(pathname).toLowerCase(); // Filter static assets if (IGNORED_EXTENSIONS.has(extension)) { return; } // Additional check for common static paths if they don't have extensions if (pathname.startsWith('/assets/') || pathname.startsWith('/static/')) { return; } const ip = c.req.header('x-forwarded-for') || c.req.header('cf-connecting-ip') || '92.176.215.140'; const geo = REVERSE_DEFAULT; // || ip !== 'unknown' ? await reverse(ip, CONFIG_DEFAULT()) : REVERSE_DEFAULT const userAgent = c.req.header('user-agent'); const entry = { timestamp: new Date().toISOString(), method: c.req.method, path: pathname, status: c.res.status, ip, userAgent, // isBot: isBotRequest(userAgent), // isAI: isAIRequest(userAgent), referer: c.req.header('referer'), userId: c.get('userId'), geo }; const line = JSON.stringify(entry) + '\n'; // Emit event for real-time streaming analyticsEmitter.emit('log', entry); // Fire and forget write fs.promises.appendFile(ANALYTICS_FILE, line).catch(err => { console.error('Failed to write to analytics file:', err); }); } catch (err) { console.error('Error in analytics middleware:', err); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5hbHl0aWNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21pZGRsZXdhcmUvYW5hbHl0aWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFFeEIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFL0QsMkVBQTJFO0FBRTNFLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLHNCQUFzQixDQUFDLENBQUM7QUFFM0UsdUJBQXVCO0FBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDL0IsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTTtDQUM1RyxDQUFDLENBQUM7QUFDSCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFBO0FBOENoRyxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBRXZFLHNFQUFzRTtBQUN0RSxJQUFJLFFBQVEsR0FBK0IsSUFBSSxDQUFDO0FBRWhELE1BQU0sWUFBWSxHQUFHLEdBQUcsRUFBRTtJQUN0QixJQUFJLFFBQVE7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUM5QixJQUFJLENBQUM7UUFDRCxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztZQUNoQyxNQUFNLElBQUks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