import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; export class LogsHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { // Add this handler's router to the parent this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private registerHandlers(): void { // Get Recent Logs Handler this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getRecentLogs', async (dataArg, toolsArg) => { const logs = await this.getRecentLogs( dataArg.level, dataArg.category, dataArg.limit || 100, dataArg.offset || 0, dataArg.search, dataArg.timeRange ); return { logs, total: logs.length, // TODO: Implement proper total count hasMore: false, // TODO: Implement proper pagination }; } ) ); // Get Log Stream Handler this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getLogStream', async (dataArg, toolsArg) => { // Create a virtual stream for log streaming const virtualStream = new plugins.typedrequest.VirtualStream(); // Set up log streaming const streamLogs = this.setupLogStream( virtualStream, dataArg.filters?.level, dataArg.filters?.category, dataArg.follow ); // Start streaming streamLogs.start(); // VirtualStream handles cleanup automatically return { logStream: virtualStream as any, // Cast to IVirtualStream interface }; } ) ); } private async getRecentLogs( level?: 'error' | 'warn' | 'info' | 'debug', category?: 'smtp' | 'dns' | 'security' | 'system' | 'email', limit: number = 100, offset: number = 0, search?: string, timeRange?: '1h' | '6h' | '24h' | '7d' | '30d' ): Promise> { // TODO: Implement actual log retrieval from storage or logger // For now, return mock data const mockLogs: Array<{ timestamp: number; level: 'debug' | 'info' | 'warn' | 'error'; category: 'smtp' | 'dns' | 'security' | 'system' | 'email'; message: string; metadata?: any; }> = []; const categories: Array<'smtp' | 'dns' | 'security' | 'system' | 'email'> = ['smtp', 'dns', 'security', 'system', 'email']; const levels: Array<'debug' | 'info' | 'warn' | 'error'> = ['info', 'warn', 'error', 'debug']; const now = Date.now(); // Generate some mock log entries for (let i = 0; i < 50; i++) { const mockCategory = categories[Math.floor(Math.random() * categories.length)]; const mockLevel = levels[Math.floor(Math.random() * levels.length)]; // Filter by requested criteria if (level && mockLevel !== level) continue; if (category && mockCategory !== category) continue; mockLogs.push({ timestamp: now - (i * 60000), // 1 minute apart level: mockLevel, category: mockCategory, message: `Sample log message ${i} from ${mockCategory}`, metadata: { requestId: plugins.uuid.v4(), }, }); } // Apply pagination return mockLogs.slice(offset, offset + limit); } private setupLogStream( virtualStream: plugins.typedrequest.VirtualStream, levelFilter?: string[], categoryFilter?: string[], follow: boolean = true ): { start: () => void; stop: () => void; } { let intervalId: NodeJS.Timeout | null = null; let logIndex = 0; const start = () => { if (!follow) { // Send existing logs and close this.getRecentLogs( levelFilter?.[0] as any, categoryFilter?.[0] as any, 100, 0 ).then(logs => { logs.forEach(log => { const logData = JSON.stringify(log); const encoder = new TextEncoder(); virtualStream.sendData(encoder.encode(logData)); }); // VirtualStream doesn't have end() method - it closes automatically }); return; } // For follow mode, simulate real-time log streaming intervalId = setInterval(() => { const categories: Array<'smtp' | 'dns' | 'security' | 'system' | 'email'> = ['smtp', 'dns', 'security', 'system', 'email']; const levels: Array<'debug' | 'info' | 'warn' | 'error'> = ['info', 'warn', 'error', 'debug']; const mockCategory = categories[Math.floor(Math.random() * categories.length)]; const mockLevel = levels[Math.floor(Math.random() * levels.length)]; // Filter by requested criteria if (levelFilter && !levelFilter.includes(mockLevel)) return; if (categoryFilter && !categoryFilter.includes(mockCategory)) return; const logEntry = { timestamp: Date.now(), level: mockLevel, category: mockCategory, message: `Real-time log ${logIndex++} from ${mockCategory}`, metadata: { requestId: plugins.uuid.v4(), }, }; const logData = JSON.stringify(logEntry); const encoder = new TextEncoder(); virtualStream.sendData(encoder.encode(logData)); }, 2000); // Send a log every 2 seconds // TODO: Hook into actual logger events // logger.on('log', (logEntry) => { // if (matchesCriteria(logEntry, level, service)) { // virtualStream.sendData(formatLogEntry(logEntry)); // } // }); }; const stop = () => { if (intervalId) { clearInterval(intervalId); intervalId = null; } // TODO: Unhook from logger events }; return { start, stop }; } }