import * as plugins from '../plugins.js'; import * as shared from './shared/index.js'; import * as appstate from '../appstate.js'; import { DeesElement, customElement, html, state, css, cssManager, } from '@design.estate/dees-element'; import { type IStatsTile } from '@design.estate/dees-catalog'; @customElement('ops-view-security') export class OpsViewSecurity extends DeesElement { @state() accessor statsState: appstate.IStatsState = { serverStats: null, emailStats: null, dnsStats: null, securityMetrics: null, radiusStats: null, vpnStats: null, lastUpdated: 0, isLoading: false, error: null, }; @state() accessor selectedTab: 'overview' | 'blocked' | 'authentication' | 'email-security' = 'overview'; private tabLabelMap: Record = { 'overview': 'Overview', 'blocked': 'Blocked IPs', 'authentication': 'Authentication', 'email-security': 'Email Security', }; private labelToTab: Record = { 'Overview': 'overview', 'Blocked IPs': 'blocked', 'Authentication': 'authentication', 'Email Security': 'email-security', }; constructor() { super(); const subscription = appstate.statsStatePart .select((stateArg) => stateArg) .subscribe((statsState) => { this.statsState = statsState; }); this.rxSubscriptions.push(subscription); } async firstUpdated() { const toggle = this.shadowRoot!.querySelector('dees-input-multitoggle') as any; if (toggle) { const sub = toggle.changeSubject.subscribe(() => { const tab = this.labelToTab[toggle.selectedOption]; if (tab) this.selectedTab = tab; }); this.rxSubscriptions.push(sub); } } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css` dees-input-multitoggle { margin-bottom: 24px; } h2 { margin: 32px 0 16px 0; font-size: 24px; font-weight: 600; color: ${cssManager.bdTheme('#333', '#ccc')}; } dees-statsgrid { margin-bottom: 32px; } .securityCard { background: ${cssManager.bdTheme('#fff', '#222')}; border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')}; border-radius: 8px; padding: 24px; position: relative; overflow: hidden; } .actionButton { margin-top: 16px; } `, ]; public render() { return html` Security ${this.renderTabContent()} `; } private renderTabContent() { const metrics = this.statsState.securityMetrics; if (!metrics) { return html`

Loading security metrics...

`; } switch(this.selectedTab) { case 'overview': return this.renderOverview(metrics); case 'blocked': return this.renderBlockedIPs(metrics); case 'authentication': return this.renderAuthentication(metrics); case 'email-security': return this.renderEmailSecurity(metrics); } } 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', title: 'Threat Level', value: threatScore, type: 'gauge', icon: 'lucide:Shield', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: '#ef4444' }, { value: 30, color: '#f59e0b' }, { value: 70, color: '#22c55e' }, ], }, description: `Status: ${threatLevel.toUpperCase()}`, }, { id: 'blockedThreats', title: 'Blocked Threats', value: (metrics.blockedIPs?.length || 0) + metrics.spamDetected, type: 'number', icon: 'lucide:ShieldCheck', color: '#ef4444', description: 'Total threats blocked today', }, { id: 'activeSessions', title: 'Active Sessions', value: recentAuthSuccesses, type: 'number', icon: 'lucide:Users', color: '#22c55e', description: 'Authenticated in last hour', }, { id: 'authFailures', title: 'Auth Failures', value: metrics.authenticationFailures, type: 'number', icon: 'lucide:LockOpen', color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b', description: 'Failed login attempts today', }, ]; return html`

Recent Security Events

({ 'Time': new Date(item.timestamp).toLocaleTimeString(), 'Event': item.event, 'Severity': item.severity, 'Details': item.details, })} > `; } private renderBlockedIPs(metrics: any) { const blockedIPs: string[] = metrics.blockedIPs || []; const tiles: IStatsTile[] = [ { id: 'totalBlocked', title: 'Blocked IPs', value: blockedIPs.length, type: 'number', icon: 'lucide:ShieldBan', color: blockedIPs.length > 0 ? '#ef4444' : '#22c55e', description: 'Currently blocked addresses', }, ]; return html` ({ ip }))} .showColumnFilters=${true} .displayFunction=${(item) => ({ 'IP Address': item.ip, 'Reason': 'Suspicious activity', })} .dataActions=${[ { name: 'Unblock', iconName: 'lucide:shield-off', type: ['contextmenu' as const], actionFunc: async (item) => { await this.unblockIP(item.ip); }, }, { name: 'Clear All', iconName: 'lucide:trash-2', type: ['header' as const], actionFunc: async () => { await this.clearBlockedIPs(); }, }, ]} > `; } 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', title: 'Authentication Failures', value: metrics.authenticationFailures, type: 'number', icon: 'lucide:LockOpen', color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b', description: 'Failed authentication attempts today', }, { id: 'successfulLogins', title: 'Successful Logins', value: successfulLogins, type: 'number', icon: 'lucide:Lock', color: '#22c55e', description: 'Successful logins today', }, ]; // 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`

Recent Login Attempts

({ 'Time': new Date(item.timestamp).toLocaleString(), 'Username': item.username, 'IP Address': item.ipAddress, 'Status': item.success ? 'Success' : 'Failed', 'Reason': item.reason || '-', })} > `; } private renderEmailSecurity(metrics: any) { const tiles: IStatsTile[] = [ { id: 'malware', title: 'Malware Detection', value: metrics.malwareDetected, type: 'number', icon: 'lucide:BugOff', color: metrics.malwareDetected > 0 ? '#ef4444' : '#22c55e', description: 'Malware detected', }, { id: 'phishing', title: 'Phishing Detection', value: metrics.phishingDetected, type: 'number', icon: 'lucide:Fish', color: metrics.phishingDetected > 0 ? '#ef4444' : '#22c55e', description: 'Phishing attempts detected', }, { id: 'suspicious', title: 'Suspicious Activities', value: metrics.suspiciousActivities, type: 'number', icon: 'lucide:TriangleAlert', color: metrics.suspiciousActivities > 5 ? '#ef4444' : '#f59e0b', description: 'Suspicious activities detected', }, { id: 'spam', title: 'Spam Detection', value: metrics.spamDetected, type: 'number', icon: 'lucide:Ban', color: '#f59e0b', description: 'Spam emails blocked', }, ]; return html`

Email Security Configuration

this.saveEmailSecuritySettings()} > Save Settings
`; } private calculateThreatLevel(metrics: any): string { const score = this.getThreatScore(metrics); if (score < 30) return 'alert'; if (score < 70) return 'warning'; return 'success'; } private getThreatScore(metrics: any): number { // Simple scoring algorithm let score = 100; 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[] { 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() { // 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) { // SmartProxy manages IP blocking — not yet exposed via API alert(`Unblocking IP ${ip} is not yet supported from the UI.`); } private async saveEmailSecuritySettings() { // 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.'); } }