feat(ops/monitoring): add in-memory log buffer, metrics time-series and ops UI integration

This commit is contained in:
2026-02-19 17:23:43 +00:00
parent dc6ce341bd
commit eacddc7ce1
14 changed files with 482 additions and 128 deletions

View File

@@ -249,7 +249,14 @@ export class OpsViewSecurity extends DeesElement {
private renderOverview(metrics: any) {
const threatLevel = this.calculateThreatLevel(metrics);
const threatScore = this.getThreatScore(metrics);
// Derive active sessions from recent successful auth events (last hour)
const allEvents: any[] = metrics.recentEvents || [];
const oneHourAgo = Date.now() - 3600000;
const recentAuthSuccesses = allEvents.filter(
(evt: any) => evt.type === 'authentication' && evt.success === true && evt.timestamp >= oneHourAgo
).length;
const tiles: IStatsTile[] = [
{
id: 'threatLevel',
@@ -271,7 +278,7 @@ export class OpsViewSecurity extends DeesElement {
{
id: 'blockedThreats',
title: 'Blocked Threats',
value: metrics.blockedIPs.length + metrics.spamDetected,
value: (metrics.blockedIPs?.length || 0) + metrics.spamDetected,
type: 'number',
icon: 'lucide:ShieldCheck',
color: '#ef4444',
@@ -280,11 +287,11 @@ export class OpsViewSecurity extends DeesElement {
{
id: 'activeSessions',
title: 'Active Sessions',
value: 0,
value: recentAuthSuccesses,
type: 'number',
icon: 'lucide:Users',
color: '#22c55e',
description: 'Current authenticated sessions',
description: 'Authenticated in last hour',
},
{
id: 'authFailures',
@@ -349,6 +356,11 @@ export class OpsViewSecurity extends DeesElement {
}
private renderAuthentication(metrics: any) {
// Derive auth events from recentEvents
const allEvents: any[] = metrics.recentEvents || [];
const authEvents = allEvents.filter((evt: any) => evt.type === 'authentication');
const successfulLogins = authEvents.filter((evt: any) => evt.success === true).length;
const tiles: IStatsTile[] = [
{
id: 'authFailures',
@@ -362,7 +374,7 @@ export class OpsViewSecurity extends DeesElement {
{
id: 'successfulLogins',
title: 'Successful Logins',
value: 0,
value: successfulLogins,
type: 'number',
icon: 'lucide:Lock',
color: '#22c55e',
@@ -370,6 +382,15 @@ export class OpsViewSecurity extends DeesElement {
},
];
// Map auth events to login history table data
const loginHistory = authEvents.map((evt: any) => ({
timestamp: evt.timestamp,
username: evt.details?.username || 'unknown',
ipAddress: evt.ipAddress || 'unknown',
success: evt.success ?? false,
reason: evt.success ? '' : evt.message || 'Authentication failed',
}));
return html`
<dees-statsgrid
.tiles=${tiles}
@@ -380,7 +401,7 @@ export class OpsViewSecurity extends DeesElement {
<dees-table
.heading1=${'Login History'}
.heading2=${'Recent authentication attempts'}
.data=${[]}
.data=${loginHistory}
.displayFunction=${(item) => ({
'Time': new Date(item.timestamp).toLocaleString(),
'Username': item.username,
@@ -483,48 +504,38 @@ export class OpsViewSecurity extends DeesElement {
private getThreatScore(metrics: any): number {
// Simple scoring algorithm
let score = 100;
score -= metrics.blockedIPs.length * 2;
score -= metrics.authenticationFailures * 1;
score -= metrics.spamDetected * 0.5;
score -= metrics.malwareDetected * 3;
score -= metrics.phishingDetected * 3;
score -= metrics.suspiciousActivities * 2;
const blockedCount = Array.isArray(metrics.blockedIPs) ? metrics.blockedIPs.length : (metrics.blockedIPs || 0);
score -= blockedCount * 2;
score -= (metrics.authenticationFailures || 0) * 1;
score -= (metrics.spamDetected || 0) * 0.5;
score -= (metrics.malwareDetected || 0) * 3;
score -= (metrics.phishingDetected || 0) * 3;
score -= (metrics.suspiciousActivities || 0) * 2;
return Math.max(0, Math.min(100, Math.round(score)));
}
private getSecurityEvents(metrics: any): any[] {
// Mock data - in real implementation, this would come from the server
return [
{
timestamp: Date.now() - 1000 * 60 * 5,
event: 'Multiple failed login attempts',
severity: 'warning',
details: 'IP: 192.168.1.100',
},
{
timestamp: Date.now() - 1000 * 60 * 15,
event: 'SPF check failed',
severity: 'medium',
details: 'Domain: example.com',
},
{
timestamp: Date.now() - 1000 * 60 * 30,
event: 'IP blocked due to spam',
severity: 'high',
details: 'IP: 10.0.0.1',
},
];
const events: any[] = metrics.recentEvents || [];
return events.map((evt: any) => ({
timestamp: evt.timestamp,
event: evt.message,
severity: evt.level === 'critical' ? 'critical' : evt.level === 'error' ? 'high' : evt.level === 'warn' ? 'warning' : 'info',
details: evt.ipAddress ? `IP: ${evt.ipAddress}` : evt.domain ? `Domain: ${evt.domain}` : evt.type,
}));
}
private async clearBlockedIPs() {
console.log('Clear blocked IPs');
// SmartProxy manages IP blocking — not yet exposed via API
alert('Clearing blocked IPs is not yet supported from the UI.');
}
private async unblockIP(ip: string) {
console.log('Unblock IP:', ip);
// SmartProxy manages IP blocking — not yet exposed via API
alert(`Unblocking IP ${ip} is not yet supported from the UI.`);
}
private async saveEmailSecuritySettings() {
console.log('Save email security settings');
// Config is read-only from the UI for now
alert('Email security settings are read-only. Update the dcrouter configuration file to change these settings.');
}
}