Files
2026-02-26 19:41:09 +01:00

122 lines
10 KiB
JavaScript

import { streamSSE } from 'hono/streaming';
import { AbstractProduct } from '../AbstractProduct.js';
import { getAnalyticsRoute, getAnalyticsStreamRoute, deleteAnalyticsRoute } from './routes.js';
import { analyticsEmitter } from '../../lib/analytics-emitter.js';
import fs from 'fs';
import path from 'path';
import readline from 'readline';
const ANALYTICS_FILE = path.resolve(process.cwd(), 'logs/analytics.jsonl');
export class AnalyticsProduct extends AbstractProduct {
id = 'analytics';
jobOptions = {};
actions = {}; // Optional: Add actions if needed for jobs
workers = [];
routes = [];
hash = () => 'analytics-hash';
meta = () => ({});
constructor() {
super();
this.initializeRoutes();
}
initializeRoutes() {
this.routes.push({
definition: getAnalyticsRoute,
handler: this.handleGetAnalytics.bind(this)
});
this.routes.push({
definition: getAnalyticsStreamRoute,
handler: this.handleGetAnalyticsStream.bind(this)
});
this.routes.push({
definition: deleteAnalyticsRoute,
handler: this.handleDeleteAnalytics.bind(this)
});
}
// ... existing handlers
async handleDeleteAnalytics(c) {
try {
if (fs.existsSync(ANALYTICS_FILE)) {
// Truncate file
await fs.promises.truncate(ANALYTICS_FILE, 0);
}
return c.json({ success: true });
}
catch (err) {
console.error('Error clearing analytics:', err);
return c.json({ error: 'Internal Server Error' }, 500);
}
}
async handleGetAnalyticsStream(c) {
return streamSSE(c, async (stream) => {
const listener = async (entry) => {
await stream.writeSSE({
data: JSON.stringify(entry),
event: 'log',
});
};
analyticsEmitter.on('log', listener);
// Keep connection alive or handle disconnect
// Hono's streamSSE handles closing the stream when the connection drops,
// but we need to remove the listener to avoid leaks.
stream.onAbort(() => {
analyticsEmitter.off('log', listener);
});
// Wait forever (or until client disconnects)
while (true) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
});
}
async handleGetAnalytics(c) {
try {
const limit = parseInt(c.req.query('limit') || '100', 10);
const startDateStr = c.req.query('startDate');
const endDateStr = c.req.query('endDate');
const startDate = startDateStr ? new Date(startDateStr).getTime() : 0;
const endDate = endDateStr ? new Date(endDateStr).getTime() : Date.now();
if (!fs.existsSync(ANALYTICS_FILE)) {
return c.json([]);
}
// Efficiently read last N lines would be better, but for "filtered" queries we generally need to scan.
// If file is huge, this is slow.
// However, typical usage for "analytics middleware... for now" implies simple logging.
// We will stream the file from the beginning (or end if we could) and collect matching entries.
// To respect 'limit' effectively with date filters, we ideally want the *latest* entries.
// So reading from end or collecting all and sorting/slicing is needed.
// Collecting all in memory is dangerous for large files.
// But implementing reverse line reading is complex without a library.
// Compromise: Read all, parse, filter, take last N.
// Optimization: If no date filter, reasonable to assume we want latest.
const logs = [];
const fileStream = fs.createReadStream(ANALYTICS_FILE);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
if (!line.trim())
continue;
try {
const entry = JSON.parse(line);
const timestamp = new Date(entry.timestamp).getTime();
if (timestamp >= startDate && timestamp <= endDate) {
logs.push(entry);
}
}
catch (e) {
// Ignore bad lines
}
}
// Sort by timestamp desc
logs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
// Limit
const result = logs.slice(0, limit);
return c.json(result);
}
catch (err) {
console.error('Error reading analytics:', err);
return c.json({ error: 'Internal Server Error' }, 500);
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvZHVjdHMvYW5hbHl0aWNzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLHVCQUF1QixFQUFFLG9CQUFvQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQy9GLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRWxFLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFDeEIsT0FBTyxRQUFRLE1BQU0sVUFBVSxDQUFDO0FBRWhDLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLHNCQUFzQixDQUFDLENBQUM7QUFFM0UsTUFBTSxPQUFPLGdCQUFpQixTQUFRLGVBQW9CO0lBQ3RELEVBQUUsR0FBRyxXQUFXLENBQUM7SUFDakIsVUFBVSxHQUFHLEVBQUUsQ0FBQztJQUNoQixPQUFPLEdBQUcsRUFBRSxDQUFDLENBQUMsMkNBQTJDO0lBQ3pELE9BQU8sR0FBVSxFQUFFLENBQUM7SUFDcEIsTUFBTSxHQUFVLEVBQUUsQ0FBQztJQUNuQixJQUFJLEdBQUcsR0FBRyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7SUFDOUIsSUFBSSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7SUFFbEI7UUFDSSxLQUFLLEVBQUUsQ0FBQztRQUNSLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzVCLENBQUM7SUFFRCxnQkFBZ0I7UUFDWixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztZQUNiLFVBQVUsRUFBRSxp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