195 lines
6.3 KiB
TypeScript
195 lines
6.3 KiB
TypeScript
![]() |
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<interfaces.requests.IReq_GetRecentLogs>(
|
||
|
'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<interfaces.requests.IReq_GetLogStream>(
|
||
|
'getLogStream',
|
||
|
async (dataArg, toolsArg) => {
|
||
|
// Create a virtual stream for log streaming
|
||
|
const virtualStream = new plugins.typedrequest.VirtualStream<Uint8Array>();
|
||
|
|
||
|
// 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<Array<{
|
||
|
timestamp: number;
|
||
|
level: 'debug' | 'info' | 'warn' | 'error';
|
||
|
category: 'smtp' | 'dns' | 'security' | 'system' | 'email';
|
||
|
message: string;
|
||
|
metadata?: any;
|
||
|
}>> {
|
||
|
// 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<Uint8Array>,
|
||
|
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 };
|
||
|
}
|
||
|
}
|