agent-smith/dist-in/middleware/analytics.js
2026-02-26 19:41:09 +01:00

114 lines
8.8 KiB
JavaScript

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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