diff --git a/package.json b/package.json index dc3af64..4bf2c2e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@git.zone/tsrun": "^1.3.3", "@git.zone/tstest": "^2.3.1", "@git.zone/tswatch": "^2.0.1", - "@types/node": "^24.0.0", + "@types/node": "^22.0.0", "node-forge": "^1.3.1" }, "dependencies": { @@ -29,7 +29,7 @@ "@api.global/typedserver": "^3.0.74", "@api.global/typedsocket": "^3.0.0", "@apiclient.xyz/cloudflare": "^6.4.1", - "@design.estate/dees-catalog": "^1.8.1", + "@design.estate/dees-catalog": "^1.8.4", "@design.estate/dees-element": "^2.0.42", "@push.rocks/projectinfo": "^5.0.1", "@push.rocks/qenv": "^6.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0b21c6..ec7f032 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^6.4.1 version: 6.4.1 '@design.estate/dees-catalog': - specifier: ^1.8.1 - version: 1.8.1 + specifier: ^1.8.4 + version: 1.8.4 '@design.estate/dees-element': specifier: ^2.0.42 version: 2.0.42 @@ -130,8 +130,8 @@ importers: specifier: ^2.0.1 version: 2.1.0 '@types/node': - specifier: ^24.0.0 - version: 24.0.0 + specifier: ^22.0.0 + version: 22.15.31 node-forge: specifier: ^1.3.1 version: 1.3.1 @@ -344,8 +344,8 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - '@design.estate/dees-catalog@1.8.1': - resolution: {integrity: sha512-luYrGMK5APzw7GgXSe4UpFE5oC3vwzKTTB0etH3o6Au1nxvllhDcWr6S5UkB+WL9lroslBcK8Icyhiq8kIxEHg==} + '@design.estate/dees-catalog@1.8.4': + resolution: {integrity: sha512-G5N9CUzhWhTt3NuHbix4yyB/Fz1fqO2gTO1OoBffu5DTA1keJlOCvyBaUbgwjmZWnBBMN1O6r8cUhYIHcf4SJg==} '@design.estate/dees-comms@1.0.27': resolution: {integrity: sha512-GvzTUwkV442LD60T08iqSoqvhA02Mou5lFvvqBPc4yBUiU7cZISqBx+76xvMgMIEI9Dx9JfTl4/2nW8MoVAanw==} @@ -1661,8 +1661,8 @@ packages: '@types/node@18.19.111': resolution: {integrity: sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==} - '@types/node@24.0.0': - resolution: {integrity: sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==} + '@types/node@22.15.31': + resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==} '@types/pidusage@2.0.5': resolution: {integrity: sha512-MIiyZI4/MK9UGUXWt0jJcCZhVw7YdhBuTOuqP/BjuLDLZ2PmmViMIQgZiWxtaMicQfAz/kMrZ5T7PKxFSkTeUA==} @@ -3090,8 +3090,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide@0.501.0: - resolution: {integrity: sha512-ufrFxsi7s5dcuDgR/z21CqP61jyMshh2BghKaEviz3nrFohrJWZtjvHvlamDaNzeJU1tGTxxPMbWqkmTPBijjA==} + lucide@0.514.0: + resolution: {integrity: sha512-GQ3Rzj1qFANBvTmhe8RM3vR491RGnSjHsivA5wSuEj5taLwH1Sv4N7n6ae3ENGsYRzdrkVXZo4ieUbD1WOXmoA==} mailauth@4.8.6: resolution: {integrity: sha512-Ler6XMLCrXyCf3kmNOMA/1aUJN6let/w9HBtjl+2KzXOUxKIl4WPJM1FwqC4IHdVJO8kmHUrvyFIKIiEGj6mvg==} @@ -4194,8 +4194,8 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici@7.10.0: resolution: {integrity: sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==} @@ -5075,7 +5075,7 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@design.estate/dees-catalog@1.8.1': + '@design.estate/dees-catalog@1.8.4': dependencies: '@design.estate/dees-domtools': 2.3.2 '@design.estate/dees-element': 2.0.42 @@ -5092,7 +5092,7 @@ snapshots: apexcharts: 4.7.0 highlight.js: 11.11.1 ibantools: 4.5.1 - lucide: 0.501.0 + lucide: 0.514.0 monaco-editor: 0.52.2 pdfjs-dist: 4.10.38 xterm: 5.3.0 @@ -7044,27 +7044,27 @@ snapshots: '@types/bn.js@5.2.0': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/buffer-json@2.0.3': {} '@types/clean-css@4.2.11': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 source-map: 0.6.1 '@types/connect@3.4.38': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/cors@2.8.18': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/debug@4.1.12': dependencies: @@ -7076,7 +7076,7 @@ snapshots: '@types/dns-packet@5.6.5': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/elliptic@6.4.18': dependencies: @@ -7084,7 +7084,7 @@ snapshots: '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -7101,30 +7101,30 @@ snapshots: '@types/from2@2.3.5': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/fs-extra@9.0.13': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/glob@8.1.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/gunzip-maybe@1.4.2': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/hast@3.0.4': dependencies: @@ -7146,16 +7146,16 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/jsonwebtoken@9.0.9': dependencies: '@types/ms': 2.1.0 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/mailparser@3.4.6': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 iconv-lite: 0.6.3 '@types/mdast@4.0.4': @@ -7174,20 +7174,20 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 form-data: 4.0.2 '@types/node-forge@1.3.11': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/node@18.19.111': dependencies: undici-types: 5.26.5 - '@types/node@24.0.0': + '@types/node@22.15.31': dependencies: - undici-types: 7.8.0 + undici-types: 6.21.0 '@types/pidusage@2.0.5': {} @@ -7203,30 +7203,30 @@ snapshots: '@types/s3rver@3.7.4': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/semver@7.7.0': {} '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/send': 0.17.4 '@types/symbol-tree@3.2.5': {} '@types/tar-stream@2.2.3': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/through2@2.0.41': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/triple-beam@1.3.5': {} @@ -7250,18 +7250,18 @@ snapshots: '@types/whatwg-url@8.2.2': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/webidl-conversions': 7.0.3 '@types/which@3.0.4': {} '@types/ws@8.18.1': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.0.0 + '@types/node': 22.15.31 optional: true '@ungap/structured-clone@1.3.0': {} @@ -7833,7 +7833,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.18 - '@types/node': 24.0.0 + '@types/node': 22.15.31 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -8776,7 +8776,7 @@ snapshots: lru-cache@7.18.3: {} - lucide@0.501.0: {} + lucide@0.514.0: {} mailauth@4.8.6: dependencies: @@ -10166,7 +10166,7 @@ snapshots: undici-types@5.26.5: {} - undici-types@7.8.0: {} + undici-types@6.21.0: {} undici@7.10.0: {} diff --git a/readme.hints.md b/readme.hints.md index 9486c64..1f2b80a 100644 --- a/readme.hints.md +++ b/readme.hints.md @@ -903,4 +903,45 @@ The DNS functionality has been refactored from UnifiedEmailServer to a dedicated - DNS functionality is now easily discoverable in DnsManager - Clear separation between DNS management and email server logic - UnifiedEmailServer is simpler and more focused -- All DNS-related tests pass successfully \ No newline at end of file +- All DNS-related tests pass successfully + +## SmartMetrics Integration (2025-06-12) - COMPLETED + +### Overview +Fixed the UI metrics display to show accurate CPU and memory data from SmartMetrics. + +### Key Findings +1. **CPU Metrics:** + - SmartMetrics provides `cpuUsageText` as a string percentage + - MetricsManager parses it as `cpuUsage.user` (system is always 0) + - UI was incorrectly dividing by 2, showing half the actual CPU usage + +2. **Memory Metrics:** + - SmartMetrics calculates `maxMemoryMB` as minimum of: + - V8 heap size limit + - System total memory + - Docker memory limit (if available) + - Provides `memoryUsageBytes` (total process memory including children) + - Provides `memoryPercentage` (pre-calculated percentage) + - UI was only showing heap usage, missing actual memory constraints + +### Changes Made +1. **MetricsManager Enhanced:** + - Added `maxMemoryMB` from SmartMetrics instance + - Added `actualUsageBytes` from SmartMetrics data + - Added `actualUsagePercentage` from SmartMetrics data + - Kept existing memory fields for compatibility + +2. **Interface Updated:** + - Added optional fields to `IServerStats.memoryUsage` + - Fields are optional to maintain backward compatibility + +3. **UI Fixed:** + - Removed incorrect CPU division by 2 + - Uses `actualUsagePercentage` when available (falls back to heap percentage) + - Shows actual memory usage vs max memory limit (not just heap) + +### Result +- CPU now shows accurate usage percentage +- Memory shows percentage of actual constraints (Docker/system/V8 limits) +- Better monitoring for containerized environments \ No newline at end of file diff --git a/ts/monitoring/classes.metricsmanager.ts b/ts/monitoring/classes.metricsmanager.ts index 3bdc156..f080cf8 100644 --- a/ts/monitoring/classes.metricsmanager.ts +++ b/ts/monitoring/classes.metricsmanager.ts @@ -115,6 +115,10 @@ export class MetricsManager { heapTotal: process.memoryUsage().heapTotal, external: process.memoryUsage().external, rss: process.memoryUsage().rss, + // Add SmartMetrics memory data + maxMemoryMB: this.smartMetrics.maxMemoryMB, + actualUsageBytes: smartMetricsData.memoryUsageBytes, + actualUsagePercentage: smartMetricsData.memoryPercentage, }, cpuUsage: { user: parseFloat(smartMetricsData.cpuUsageText || '0'), diff --git a/ts_interfaces/data/stats.ts b/ts_interfaces/data/stats.ts index f49c41f..57694f1 100644 --- a/ts_interfaces/data/stats.ts +++ b/ts_interfaces/data/stats.ts @@ -6,6 +6,10 @@ export interface IServerStats { heapTotal: number; external: number; rss: number; + // SmartMetrics memory data + maxMemoryMB?: number; + actualUsageBytes?: number; + actualUsagePercentage?: number; }; cpuUsage: { user: number; diff --git a/ts_web/elements/ops-view-emails.ts b/ts_web/elements/ops-view-emails.ts index b9dfd19..3d1b286 100644 --- a/ts_web/elements/ops-view-emails.ts +++ b/ts_web/elements/ops-view-emails.ts @@ -1,5 +1,6 @@ import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element'; import * as appstate from '../appstate.js'; +import * as shared from './shared/index.js'; declare global { interface HTMLElementTagNameMap { @@ -56,95 +57,39 @@ export class OpsViewEmails extends DeesElement { public static styles = [ cssManager.defaultStyles, + shared.viewHostCss, css` :host { display: block; height: 100%; - padding: 24px; } .emailContainer { display: grid; - grid-template-columns: 250px 1fr; - gap: 24px; - height: calc(100vh - 200px); + grid-template-columns: 280px 1fr; + gap: 16px; + height: 100%; + min-height: 600px; } .sidebar { - background: white; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 16px; display: flex; flex-direction: column; gap: 16px; } - .folderList { - display: flex; - flex-direction: column; - gap: 8px; - } - - .folderItem { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s; - position: relative; - } - - .folderItem:hover { - background: #f5f5f5; - } - - .folderItem.selected { - background: #e3f2fd; - color: #1976d2; - } - - .folderIcon { - font-size: 18px; - } - - .folderLabel { - flex: 1; - font-weight: 500; - } - - .folderCount { - background: #e0e0e0; - color: #666; - padding: 2px 8px; - border-radius: 12px; - font-size: 12px; - font-weight: 600; - } - - .folderItem.selected .folderCount { - background: #1976d2; - color: white; - } - .mainContent { - background: white; - border: 1px solid #e9ecef; - border-radius: 8px; - overflow: hidden; display: flex; flex-direction: column; + gap: 16px; + overflow: hidden; } .emailToolbar { display: flex; align-items: center; - gap: 16px; - padding: 16px; - border-bottom: 1px solid #e9ecef; - background: #fafafa; + gap: 12px; + flex-wrap: wrap; } .searchBox { diff --git a/ts_web/elements/ops-view-network.ts b/ts_web/elements/ops-view-network.ts index 627fa6b..e9a5e5b 100644 --- a/ts_web/elements/ops-view-network.ts +++ b/ts_web/elements/ops-view-network.ts @@ -100,18 +100,8 @@ export class OpsViewNetwork extends DeesElement { margin-bottom: 24px; } - .chartSection { - background: white; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 24px; - } - - .tableSection { - background: white; - border: 1px solid #e9ecef; - border-radius: 8px; - overflow: hidden; + dees-chart-area { + margin-bottom: 24px; } .protocolBadge { @@ -224,51 +214,47 @@ export class OpsViewNetwork extends DeesElement { ${this.renderNetworkStats()} -
- -
+ -
- ({ - Time: new Date(req.timestamp).toLocaleTimeString(), - Protocol: html`${req.protocol.toUpperCase()}`, - Method: req.method, - 'Host:Port': `${req.hostname}:${req.port}`, - Path: this.truncateUrl(req.url), - Status: this.renderStatus(req.statusCode), - Duration: `${req.duration}ms`, - 'In/Out': `${this.formatBytes(req.bytesIn)} / ${this.formatBytes(req.bytesOut)}`, - 'Remote IP': req.remoteIp, - })} - .dataActions=${[ - { - name: 'View Details', - iconName: 'magnifyingGlass', - type: ['inRow', 'doubleClick', 'contextmenu'], - actionFunc: async (actionData) => { - await this.showRequestDetails(actionData.item); - } + ({ + Time: new Date(req.timestamp).toLocaleTimeString(), + Protocol: html`${req.protocol.toUpperCase()}`, + Method: req.method, + 'Host:Port': `${req.hostname}:${req.port}`, + Path: this.truncateUrl(req.url), + Status: this.renderStatus(req.statusCode), + Duration: `${req.duration}ms`, + 'In/Out': `${this.formatBytes(req.bytesIn)} / ${this.formatBytes(req.bytesOut)}`, + 'Remote IP': req.remoteIp, + })} + .dataActions=${[ + { + name: 'View Details', + iconName: 'magnifyingGlass', + type: ['inRow', 'doubleClick', 'contextmenu'], + actionFunc: async (actionData) => { + await this.showRequestDetails(actionData.item); } - ]} - heading1="Recent Network Activity" - heading2="Last ${this.selectedTimeRange} of network requests" - searchable - .pagination=${true} - .paginationSize=${50} - dataName="request" - > -
+ } + ]} + heading1="Recent Network Activity" + heading2="Last ${this.selectedTimeRange} of network requests" + searchable + .pagination=${true} + .paginationSize=${50} + dataName="request" + > `; } diff --git a/ts_web/elements/ops-view-overview.ts b/ts_web/elements/ops-view-overview.ts index 43cf18c..efb5a6f 100644 --- a/ts_web/elements/ops-view-overview.ts +++ b/ts_web/elements/ops-view-overview.ts @@ -138,8 +138,10 @@ export class OpsViewOverview extends DeesElement { 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 cpuUsage = Math.round(this.statsState.serverStats.cpuUsage.user); + const memoryUsage = this.statsState.serverStats.memoryUsage.actualUsagePercentage !== undefined + ? Math.round(this.statsState.serverStats.memoryUsage.actualUsagePercentage) + : Math.round((this.statsState.serverStats.memoryUsage.heapUsed / this.statsState.serverStats.memoryUsage.heapTotal) * 100); const tiles: IStatsTile[] = [ { @@ -183,7 +185,9 @@ export class OpsViewOverview extends DeesElement { 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)}`, + description: this.statsState.serverStats.memoryUsage.actualUsageBytes !== undefined && this.statsState.serverStats.memoryUsage.maxMemoryMB !== undefined + ? `${this.formatBytes(this.statsState.serverStats.memoryUsage.actualUsageBytes)} / ${this.formatBytes(this.statsState.serverStats.memoryUsage.maxMemoryMB * 1024 * 1024)}` + : `${this.formatBytes(this.statsState.serverStats.memoryUsage.rss)} / ${this.formatBytes(this.statsState.serverStats.memoryUsage.heapTotal)}`, }, ];