@@ -222,4 +134,171 @@ export class OpsViewOverview extends DeesElement {
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
+
+ private renderServerStats(): TemplateResult {
+ if (!this.statsState.serverStats) return html``;
+
+ const cpuUsage = Math.round((this.statsState.serverStats.cpuUsage.user + this.statsState.serverStats.cpuUsage.system) / 2);
+ const memoryUsage = Math.round((this.statsState.serverStats.memoryUsage.heapUsed / this.statsState.serverStats.memoryUsage.heapTotal) * 100);
+
+ const tiles: IStatsTile[] = [
+ {
+ id: 'status',
+ title: 'Server Status',
+ value: this.statsState.serverStats.uptime ? 'Online' : 'Offline',
+ type: 'text',
+ icon: 'server',
+ color: this.statsState.serverStats.uptime ? '#22c55e' : '#ef4444',
+ description: `Uptime: ${this.formatUptime(this.statsState.serverStats.uptime)}`,
+ },
+ {
+ id: 'connections',
+ title: 'Active Connections',
+ value: this.statsState.serverStats.activeConnections,
+ type: 'number',
+ icon: 'networkWired',
+ color: '#3b82f6',
+ description: `Total: ${this.statsState.serverStats.totalConnections}`,
+ },
+ {
+ id: 'cpu',
+ title: 'CPU Usage',
+ value: cpuUsage,
+ type: 'gauge',
+ icon: 'microchip',
+ gaugeOptions: {
+ min: 0,
+ max: 100,
+ thresholds: [
+ { value: 0, color: '#22c55e' },
+ { value: 60, color: '#f59e0b' },
+ { value: 80, color: '#ef4444' },
+ ],
+ },
+ },
+ {
+ id: 'memory',
+ title: 'Memory Usage',
+ value: memoryUsage,
+ type: 'percentage',
+ icon: 'memory',
+ color: memoryUsage > 80 ? '#ef4444' : memoryUsage > 60 ? '#f59e0b' : '#22c55e',
+ description: `${this.formatBytes(this.statsState.serverStats.memoryUsage.heapUsed)} / ${this.formatBytes(this.statsState.serverStats.memoryUsage.heapTotal)}`,
+ },
+ ];
+
+ return html`
+
{
+ await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
+ },
+ },
+ ]}
+ >
+ `;
+ }
+
+ private renderEmailStats(): TemplateResult {
+ if (!this.statsState.emailStats) return html``;
+
+ const deliveryRate = this.statsState.emailStats.deliveryRate || 0;
+ const bounceRate = this.statsState.emailStats.bounceRate || 0;
+
+ const tiles: IStatsTile[] = [
+ {
+ id: 'sent',
+ title: 'Emails Sent',
+ value: this.statsState.emailStats.sent,
+ type: 'number',
+ icon: 'paperPlane',
+ color: '#22c55e',
+ description: `Delivery rate: ${(deliveryRate * 100).toFixed(1)}%`,
+ },
+ {
+ id: 'received',
+ title: 'Emails Received',
+ value: this.statsState.emailStats.received,
+ type: 'number',
+ icon: 'envelope',
+ color: '#3b82f6',
+ },
+ {
+ id: 'queued',
+ title: 'Queued',
+ value: this.statsState.emailStats.queued,
+ type: 'number',
+ icon: 'clock',
+ color: '#f59e0b',
+ description: 'Pending delivery',
+ },
+ {
+ id: 'failed',
+ title: 'Failed',
+ value: this.statsState.emailStats.failed,
+ type: 'number',
+ icon: 'triangleExclamation',
+ color: '#ef4444',
+ description: `Bounce rate: ${(bounceRate * 100).toFixed(1)}%`,
+ },
+ ];
+
+ return html`
+
Email Statistics
+
+ `;
+ }
+
+ private renderDnsStats(): TemplateResult {
+ if (!this.statsState.dnsStats) return html``;
+
+ const cacheHitRate = Math.round(this.statsState.dnsStats.cacheHitRate * 100);
+
+ const tiles: IStatsTile[] = [
+ {
+ id: 'queries',
+ title: 'DNS Queries',
+ value: this.statsState.dnsStats.totalQueries,
+ type: 'number',
+ icon: 'globe',
+ color: '#3b82f6',
+ description: 'Total queries handled',
+ },
+ {
+ id: 'cacheRate',
+ title: 'Cache Hit Rate',
+ value: cacheHitRate,
+ type: 'percentage',
+ icon: 'database',
+ color: cacheHitRate > 80 ? '#22c55e' : cacheHitRate > 60 ? '#f59e0b' : '#ef4444',
+ description: `${this.statsState.dnsStats.cacheHits} hits / ${this.statsState.dnsStats.cacheMisses} misses`,
+ },
+ {
+ id: 'domains',
+ title: 'Active Domains',
+ value: this.statsState.dnsStats.activeDomains,
+ type: 'number',
+ icon: 'sitemap',
+ color: '#8b5cf6',
+ },
+ {
+ id: 'responseTime',
+ title: 'Avg Response Time',
+ value: this.statsState.dnsStats.averageResponseTime.toFixed(1),
+ unit: 'ms',
+ type: 'number',
+ icon: 'clockRotateLeft',
+ color: this.statsState.dnsStats.averageResponseTime < 50 ? '#22c55e' : '#f59e0b',
+ },
+ ];
+
+ return html`
+
DNS Statistics
+
+ `;
+ }
}
\ No newline at end of file
diff --git a/ts_web/elements/ops-view-stats.ts b/ts_web/elements/ops-view-stats.ts
deleted file mode 100644
index b343164..0000000
--- a/ts_web/elements/ops-view-stats.ts
+++ /dev/null
@@ -1,302 +0,0 @@
-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';
-
-@customElement('ops-view-stats')
-export class OpsViewStats extends DeesElement {
- @state()
- private statsState: appstate.IStatsState = {
- serverStats: null,
- emailStats: null,
- dnsStats: null,
- securityMetrics: null,
- lastUpdated: 0,
- isLoading: false,
- error: null,
- };
-
- @state()
- private uiState: appstate.IUiState = {
- activeView: 'dashboard',
- sidebarCollapsed: false,
- autoRefresh: true,
- refreshInterval: 30000,
- theme: 'light',
- };
-
- constructor() {
- super();
- const statsSubscription = appstate.statsStatePart
- .select((stateArg) => stateArg)
- .subscribe((statsState) => {
- this.statsState = statsState;
- });
- this.rxSubscriptions.push(statsSubscription);
-
- const uiSubscription = appstate.uiStatePart
- .select((stateArg) => stateArg)
- .subscribe((uiState) => {
- this.uiState = uiState;
- });
- this.rxSubscriptions.push(uiSubscription);
- }
-
- public static styles = [
- cssManager.defaultStyles,
- shared.viewHostCss,
- css`
- .controls {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 24px;
- padding: 16px;
- background: #f8f9fa;
- border-radius: 8px;
- }
-
- .refreshButton {
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .lastUpdated {
- font-size: 14px;
- color: #666;
- }
-
- .statsSection {
- margin-bottom: 48px;
- }
-
- .sectionTitle {
- font-size: 24px;
- font-weight: 600;
- margin-bottom: 24px;
- color: #333;
- }
-
- .metricsGrid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- gap: 16px;
- }
-
- .metricCard {
- background: white;
- border: 1px solid #e9ecef;
- border-radius: 8px;
- padding: 20px;
- transition: all 0.2s ease;
- }
-
- .metricCard:hover {
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- transform: translateY(-2px);
- }
-
- .metricLabel {
- font-size: 14px;
- color: #666;
- margin-bottom: 8px;
- }
-
- .metricValue {
- font-size: 28px;
- font-weight: 700;
- color: #2196F3;
- }
-
- .metricUnit {
- font-size: 16px;
- color: #999;
- margin-left: 4px;
- }
-
- .chartContainer {
- background: white;
- border: 1px solid #e9ecef;
- border-radius: 8px;
- padding: 24px;
- margin-top: 24px;
- }
- `,
- ];
-
- public render() {
- return html`
-
Statistics
-
-
-
- appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null)}
- .disabled=${this.statsState.isLoading}
- >
- ${this.statsState.isLoading ? html`` : 'Refresh'}
-
- appstate.uiStatePart.dispatchAction(appstate.toggleAutoRefreshAction, null)}
- .type=${this.uiState.autoRefresh ? 'highlighted' : 'normal'}
- >
- Auto-refresh: ${this.uiState.autoRefresh ? 'ON' : 'OFF'}
-
-
-
- ${this.statsState.lastUpdated ? html`
- Last updated: ${new Date(this.statsState.lastUpdated).toLocaleTimeString()}
- ` : ''}
-
-
-
- ${this.statsState.serverStats ? html`
-
-
Server Metrics
-
-
-
Uptime
-
${this.formatUptime(this.statsState.serverStats.uptime)}
-
-
-
CPU Usage
-
${Math.round((this.statsState.serverStats.cpuUsage.user + this.statsState.serverStats.cpuUsage.system) / 2)}%
-
-
-
Memory Used
-
${this.formatBytes(this.statsState.serverStats.memoryUsage.heapUsed)}
-
-
-
Active Connections
-
${this.statsState.serverStats.activeConnections}
-
-
-
-
-
-
-
- ` : ''}
-
- ${this.statsState.emailStats ? html`
-
-
Email Statistics
- ({
- Metric: item.metric,
- Value: `${item.value} ${item.unit}`,
- })}
- >
-
- ` : ''}
-
- ${this.statsState.dnsStats ? html`
-
-
DNS Statistics
-
-
-
Total Queries
-
${this.formatNumber(this.statsState.dnsStats.totalQueries)}
-
-
-
Cache Hit Rate
-
${Math.round(this.statsState.dnsStats.cacheHitRate * 100)}%
-
-
-
Average Response Time
-
${this.statsState.dnsStats.averageResponseTime}ms
-
-
-
Domains Configured
-
${this.statsState.dnsStats.activeDomains}
-
-
-
- ` : ''}
-
- ${this.statsState.securityMetrics ? html`
-
-
Security Metrics
- ({
- 'Security Metric': item.metric,
- 'Count': item.value,
- 'Severity': item.severity,
- })}
- >
-
- ` : ''}
- `;
- }
-
- private formatUptime(seconds: number): string {
- const days = Math.floor(seconds / 86400);
- const hours = Math.floor((seconds % 86400) / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = Math.floor(seconds % 60);
-
- if (days > 0) {
- return `${days}d ${hours}h ${minutes}m ${secs}s`;
- } else if (hours > 0) {
- return `${hours}h ${minutes}m ${secs}s`;
- } else if (minutes > 0) {
- return `${minutes}m ${secs}s`;
- } else {
- return `${secs}s`;
- }
- }
-
- private formatBytes(bytes: number): string {
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
- let size = bytes;
- let unitIndex = 0;
-
- while (size >= 1024 && unitIndex < units.length - 1) {
- size /= 1024;
- unitIndex++;
- }
-
- return `${size.toFixed(1)} ${units[unitIndex]}`;
- }
-
- private formatNumber(num: number): string {
- if (num >= 1000000) {
- return `${(num / 1000000).toFixed(1)}M`;
- } else if (num >= 1000) {
- return `${(num / 1000).toFixed(1)}K`;
- }
- return num.toString();
- }
-}
\ No newline at end of file