@ceon-oy/monitor-sdk 1.3.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ceon Monitor SDK
2
2
 
3
- Lightweight client SDK for integrating with the Ceon Monitor service. Provides error reporting, technology tracking, vulnerability auditing, and security event monitoring.
3
+ Lightweight client SDK for integrating with the Ceon Monitor service. Provides error reporting, technology tracking, vulnerability auditing, system metrics collection (CPU, RAM, disk), and security event monitoring.
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -12,6 +12,7 @@ Lightweight client SDK for integrating with the Ceon Monitor service. Provides e
12
12
  - [Technology Tracking](#technology-tracking)
13
13
  - [Vulnerability Auditing](#vulnerability-auditing)
14
14
  - [SDK-Based Health Checks](#sdk-based-health-checks)
15
+ - [System Metrics](#system-metrics)
15
16
  - [Security Events](#security-events)
16
17
  - [Framework Examples](#framework-examples)
17
18
  - [Express.js](#expressjs)
@@ -490,6 +491,75 @@ const results = [
490
491
  await monitor.submitHealthResults(results);
491
492
  ```
492
493
 
494
+ ### System Metrics
495
+
496
+ The SDK automatically collects and reports server resource usage — CPU, RAM, and disk — to Ceon Monitor. No SDK configuration flag is needed; the feature activates based on the project settings configured in the Ceon Monitor dashboard.
497
+
498
+ #### How It Works
499
+
500
+ When the `MonitorClient` is created, it fetches your project settings from the server. If the project has **Enable Metric Collection** turned on, the SDK:
501
+
502
+ 1. Reads `metricsIntervalSeconds` from project settings (configured in the dashboard)
503
+ 2. Reads `metricsDiskPaths` from project settings (e.g. `["/", "/data"]`)
504
+ 3. Starts collecting metrics at that interval automatically
505
+ 4. POSTs metrics to the server — no manual calls needed
506
+
507
+ #### What Is Collected
508
+
509
+ | Metric | Method | Notes |
510
+ |--------|--------|-------|
511
+ | CPU usage % | Dual `os.cpus()` sample with 500 ms gap | Accurate active-cycle average |
512
+ | RAM total | `os.totalmem()` | Bytes |
513
+ | RAM used | `os.totalmem() - os.freemem()` | Bytes |
514
+ | RAM % | `usedMemory / totalMemory * 100` | |
515
+ | Disk total / used / free per path | `fs.statfs(path)` | Node.js 18.15+ built-in, zero external deps |
516
+
517
+ Hostname is automatically set via `os.hostname()` and included with every report.
518
+
519
+ #### Enable in Dashboard
520
+
521
+ 1. Open the **Ceon Monitor** dashboard
522
+ 2. Go to **Projects** and click **Edit** on your project
523
+ 3. Enable the **System Metrics** widget
524
+ 4. Click **Enable Metric Collection**
525
+ 5. Set the collection interval (e.g. every 60 seconds)
526
+ 6. Add disk paths to monitor (e.g. `/` for root, `/data` for a data volume)
527
+ 7. Configure CPU / RAM / disk alert thresholds
528
+ 8. Save
529
+
530
+ The SDK will pick up the new settings on its next startup.
531
+
532
+ #### Notifications
533
+
534
+ When a metric exceeds a configured threshold, Ceon Monitor sends a `SYSTEM_METRIC_CRITICAL` notification to any user who has subscribed to **System Metric Alerts** for that project. Subscribe in **Settings → Subscriptions**.
535
+
536
+ #### Node.js Version Requirement
537
+
538
+ `fs.statfs()` (used for disk monitoring) requires **Node.js 18.15 or later**. No external packages are needed.
539
+
540
+ #### Manual Metric Submission
541
+
542
+ You can also submit a metric reading manually:
543
+
544
+ ```typescript
545
+ await monitor.submitSystemMetric({
546
+ hostname: os.hostname(),
547
+ cpuPercent: 72.5,
548
+ memoryTotal: 8 * 1024 ** 3, // 8 GB in bytes
549
+ memoryUsed: 5 * 1024 ** 3, // 5 GB in bytes
550
+ memoryPercent: 62.5,
551
+ disks: [
552
+ {
553
+ path: '/',
554
+ total: 100 * 1024 ** 3, // 100 GB
555
+ used: 60 * 1024 ** 3, // 60 GB
556
+ free: 40 * 1024 ** 3, // 40 GB
557
+ usedPercent: 60,
558
+ },
559
+ ],
560
+ });
561
+ ```
562
+
493
563
  ### Security Events
494
564
 
495
565
  #### Report Security Event
@@ -1092,6 +1162,10 @@ Runs npm audit and sends results to the server.
1092
1162
 
1093
1163
  Runs npm audit on all directories configured in `auditPaths` and returns a combined summary.
1094
1164
 
1165
+ #### `submitSystemMetric(metric: SystemMetric): Promise<void>`
1166
+
1167
+ Manually submits a system metric reading. Under normal usage this is called automatically by the SDK's internal collection loop — use this only if you need custom collection logic.
1168
+
1095
1169
  #### `flush(): Promise<void>`
1096
1170
 
1097
1171
  Immediately sends all queued errors.
@@ -1160,6 +1234,23 @@ interface AuditPath {
1160
1234
  environment: string; // Environment label (e.g., 'server', 'client')
1161
1235
  }
1162
1236
 
1237
+ interface DiskMetric {
1238
+ path: string; // Mount path (e.g. '/', '/data')
1239
+ total: number; // Total bytes
1240
+ used: number; // Used bytes
1241
+ free: number; // Free bytes
1242
+ usedPercent: number; // 0–100
1243
+ }
1244
+
1245
+ interface SystemMetric {
1246
+ hostname: string;
1247
+ cpuPercent: number;
1248
+ memoryTotal: number;
1249
+ memoryUsed: number;
1250
+ memoryPercent: number;
1251
+ disks: DiskMetric[];
1252
+ }
1253
+
1163
1254
  interface MultiAuditSummary {
1164
1255
  results: Array<{
1165
1256
  environment: string;
package/dist/index.d.mts CHANGED
@@ -166,6 +166,7 @@ interface SdkHealthEndpoint {
166
166
  intervalMs: number;
167
167
  timeoutMs: number;
168
168
  expectedStatus: number;
169
+ allowInsecureTls?: boolean;
169
170
  }
170
171
  interface SdkHealthResult {
171
172
  endpointId: string;
@@ -231,6 +232,7 @@ declare class MonitorClient {
231
232
  private sdkErrorsInCurrentWindow;
232
233
  private sdkErrorWindowResetTimer;
233
234
  private metricsCollectionTimer;
235
+ private metricsSettingsCheckTimer;
234
236
  constructor(config: MonitorClientConfig);
235
237
  /**
236
238
  * Security: Validate and sanitize metadata to prevent oversized payloads
@@ -304,6 +306,12 @@ declare class MonitorClient {
304
306
  * Fetch with timeout to prevent hanging requests
305
307
  */
306
308
  private fetchWithTimeout;
309
+ /**
310
+ * Perform an HTTPS request with TLS certificate verification disabled.
311
+ * Used for health checks on servers with self-signed certificates.
312
+ * Uses Node.js built-in `https` module — works reliably in bundled environments.
313
+ */
314
+ private fetchInsecureHttps;
307
315
  private sendSingle;
308
316
  private sendBatch;
309
317
  private startFlushTimer;
@@ -491,6 +499,7 @@ declare class MonitorClient {
491
499
  * Collects CPU, RAM, and disk usage using built-in Node.js modules.
492
500
  */
493
501
  setupSystemMetricsCollection(): Promise<void>;
502
+ private startMetricsCollection;
494
503
  private stopMetricsCollection;
495
504
  /**
496
505
  * Collect system metrics (CPU, RAM, disk) using built-in Node.js modules
package/dist/index.d.ts CHANGED
@@ -166,6 +166,7 @@ interface SdkHealthEndpoint {
166
166
  intervalMs: number;
167
167
  timeoutMs: number;
168
168
  expectedStatus: number;
169
+ allowInsecureTls?: boolean;
169
170
  }
170
171
  interface SdkHealthResult {
171
172
  endpointId: string;
@@ -231,6 +232,7 @@ declare class MonitorClient {
231
232
  private sdkErrorsInCurrentWindow;
232
233
  private sdkErrorWindowResetTimer;
233
234
  private metricsCollectionTimer;
235
+ private metricsSettingsCheckTimer;
234
236
  constructor(config: MonitorClientConfig);
235
237
  /**
236
238
  * Security: Validate and sanitize metadata to prevent oversized payloads
@@ -304,6 +306,12 @@ declare class MonitorClient {
304
306
  * Fetch with timeout to prevent hanging requests
305
307
  */
306
308
  private fetchWithTimeout;
309
+ /**
310
+ * Perform an HTTPS request with TLS certificate verification disabled.
311
+ * Used for health checks on servers with self-signed certificates.
312
+ * Uses Node.js built-in `https` module — works reliably in bundled environments.
313
+ */
314
+ private fetchInsecureHttps;
307
315
  private sendSingle;
308
316
  private sendBatch;
309
317
  private startFlushTimer;
@@ -491,6 +499,7 @@ declare class MonitorClient {
491
499
  * Collects CPU, RAM, and disk usage using built-in Node.js modules.
492
500
  */
493
501
  setupSystemMetricsCollection(): Promise<void>;
502
+ private startMetricsCollection;
494
503
  private stopMetricsCollection;
495
504
  /**
496
505
  * Collect system metrics (CPU, RAM, disk) using built-in Node.js modules
package/dist/index.js CHANGED
@@ -101,6 +101,7 @@ var MonitorClient = class {
101
101
  this.sdkErrorWindowResetTimer = null;
102
102
  // System metrics collection (on-premise only, opt-in)
103
103
  this.metricsCollectionTimer = null;
104
+ this.metricsSettingsCheckTimer = null;
104
105
  if (!config.apiKey || config.apiKey.trim().length === 0) {
105
106
  throw new Error("[MonitorClient] API key is required");
106
107
  }
@@ -543,6 +544,45 @@ var MonitorClient = class {
543
544
  clearTimeout(timeoutId);
544
545
  }
545
546
  }
547
+ /**
548
+ * Perform an HTTPS request with TLS certificate verification disabled.
549
+ * Used for health checks on servers with self-signed certificates.
550
+ * Uses Node.js built-in `https` module — works reliably in bundled environments.
551
+ */
552
+ async fetchInsecureHttps(url, options, timeoutMs) {
553
+ const https = await import("https");
554
+ const parsed = new URL(url);
555
+ return new Promise((resolve, reject) => {
556
+ let req;
557
+ const timeoutId = setTimeout(() => {
558
+ req?.destroy();
559
+ reject(new Error(`Request timeout after ${timeoutMs}ms`));
560
+ }, timeoutMs);
561
+ req = https.request(
562
+ {
563
+ hostname: parsed.hostname,
564
+ port: parsed.port || 443,
565
+ path: parsed.pathname + parsed.search,
566
+ method: options.method || "GET",
567
+ headers: options.headers,
568
+ rejectUnauthorized: false
569
+ },
570
+ (res) => {
571
+ clearTimeout(timeoutId);
572
+ res.resume();
573
+ resolve({ status: res.statusCode || 0 });
574
+ }
575
+ );
576
+ req.on("error", (err) => {
577
+ clearTimeout(timeoutId);
578
+ reject(err);
579
+ });
580
+ if (options.body) {
581
+ req.write(options.body);
582
+ }
583
+ req.end();
584
+ });
585
+ }
546
586
  async sendSingle(error) {
547
587
  const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/errors`, {
548
588
  method: "POST",
@@ -1347,11 +1387,7 @@ var MonitorClient = class {
1347
1387
  "Content-Type": "application/json"
1348
1388
  };
1349
1389
  }
1350
- const response = await this.fetchWithTimeout(
1351
- endpoint.url,
1352
- requestOptions,
1353
- endpoint.timeoutMs
1354
- );
1390
+ const response = endpoint.allowInsecureTls && endpoint.url.startsWith("https") ? await this.fetchInsecureHttps(endpoint.url, requestOptions, endpoint.timeoutMs) : await this.fetchWithTimeout(endpoint.url, requestOptions, endpoint.timeoutMs);
1355
1391
  const responseTimeMs = Date.now() - startTime;
1356
1392
  const statusCode = response.status;
1357
1393
  let status;
@@ -1487,16 +1523,33 @@ var MonitorClient = class {
1487
1523
  async setupSystemMetricsCollection() {
1488
1524
  const settings = await this.fetchProjectSettings();
1489
1525
  if (!settings?.metricsEnabled) {
1526
+ this.metricsSettingsCheckTimer = setInterval(() => {
1527
+ this.fetchProjectSettings().then((latestSettings) => {
1528
+ if (latestSettings?.metricsEnabled) {
1529
+ clearInterval(this.metricsSettingsCheckTimer);
1530
+ this.metricsSettingsCheckTimer = null;
1531
+ this.startMetricsCollection(latestSettings);
1532
+ }
1533
+ }).catch(() => {
1534
+ });
1535
+ }, 3e5);
1490
1536
  return;
1491
1537
  }
1492
- const intervalMs = Math.max(3e4, (settings.metricsIntervalSeconds ?? 60) * 1e3);
1493
- const diskPaths = settings.metricsDiskPaths ?? ["/"];
1494
- console.log(`[MonitorClient] System metrics collection enabled (every ${intervalMs / 1e3}s, paths: ${diskPaths.join(", ")})`);
1495
- this.collectAndSubmitMetrics(diskPaths).catch((err) => {
1538
+ this.startMetricsCollection(settings);
1539
+ }
1540
+ startMetricsCollection(settings) {
1541
+ const intervalMs = Math.max(3e4, (settings.metricsIntervalSeconds ?? 3600) * 1e3);
1542
+ console.log(`[MonitorClient] System metrics collection enabled (every ${intervalMs / 1e3}s)`);
1543
+ const initialDiskPaths = settings.metricsDiskPaths ?? ["/"];
1544
+ this.collectAndSubmitMetrics(initialDiskPaths).catch((err) => {
1496
1545
  this.reportError("SYSTEM_METRICS", "Initial system metrics collection failed", err);
1497
1546
  });
1498
1547
  this.metricsCollectionTimer = setInterval(() => {
1499
- this.collectAndSubmitMetrics(diskPaths).catch((err) => {
1548
+ this.fetchProjectSettings().then((latestSettings) => {
1549
+ if (!latestSettings?.metricsEnabled) return;
1550
+ const diskPaths = latestSettings.metricsDiskPaths ?? ["/"];
1551
+ return this.collectAndSubmitMetrics(diskPaths);
1552
+ }).catch((err) => {
1500
1553
  this.reportError("SYSTEM_METRICS", "Scheduled system metrics collection failed", err);
1501
1554
  });
1502
1555
  }, intervalMs);
@@ -1506,6 +1559,10 @@ var MonitorClient = class {
1506
1559
  clearInterval(this.metricsCollectionTimer);
1507
1560
  this.metricsCollectionTimer = null;
1508
1561
  }
1562
+ if (this.metricsSettingsCheckTimer) {
1563
+ clearInterval(this.metricsSettingsCheckTimer);
1564
+ this.metricsSettingsCheckTimer = null;
1565
+ }
1509
1566
  }
1510
1567
  /**
1511
1568
  * Collect system metrics (CPU, RAM, disk) using built-in Node.js modules
package/dist/index.mjs CHANGED
@@ -65,6 +65,7 @@ var MonitorClient = class {
65
65
  this.sdkErrorWindowResetTimer = null;
66
66
  // System metrics collection (on-premise only, opt-in)
67
67
  this.metricsCollectionTimer = null;
68
+ this.metricsSettingsCheckTimer = null;
68
69
  if (!config.apiKey || config.apiKey.trim().length === 0) {
69
70
  throw new Error("[MonitorClient] API key is required");
70
71
  }
@@ -507,6 +508,45 @@ var MonitorClient = class {
507
508
  clearTimeout(timeoutId);
508
509
  }
509
510
  }
511
+ /**
512
+ * Perform an HTTPS request with TLS certificate verification disabled.
513
+ * Used for health checks on servers with self-signed certificates.
514
+ * Uses Node.js built-in `https` module — works reliably in bundled environments.
515
+ */
516
+ async fetchInsecureHttps(url, options, timeoutMs) {
517
+ const https = await import("https");
518
+ const parsed = new URL(url);
519
+ return new Promise((resolve, reject) => {
520
+ let req;
521
+ const timeoutId = setTimeout(() => {
522
+ req?.destroy();
523
+ reject(new Error(`Request timeout after ${timeoutMs}ms`));
524
+ }, timeoutMs);
525
+ req = https.request(
526
+ {
527
+ hostname: parsed.hostname,
528
+ port: parsed.port || 443,
529
+ path: parsed.pathname + parsed.search,
530
+ method: options.method || "GET",
531
+ headers: options.headers,
532
+ rejectUnauthorized: false
533
+ },
534
+ (res) => {
535
+ clearTimeout(timeoutId);
536
+ res.resume();
537
+ resolve({ status: res.statusCode || 0 });
538
+ }
539
+ );
540
+ req.on("error", (err) => {
541
+ clearTimeout(timeoutId);
542
+ reject(err);
543
+ });
544
+ if (options.body) {
545
+ req.write(options.body);
546
+ }
547
+ req.end();
548
+ });
549
+ }
510
550
  async sendSingle(error) {
511
551
  const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/errors`, {
512
552
  method: "POST",
@@ -1311,11 +1351,7 @@ var MonitorClient = class {
1311
1351
  "Content-Type": "application/json"
1312
1352
  };
1313
1353
  }
1314
- const response = await this.fetchWithTimeout(
1315
- endpoint.url,
1316
- requestOptions,
1317
- endpoint.timeoutMs
1318
- );
1354
+ const response = endpoint.allowInsecureTls && endpoint.url.startsWith("https") ? await this.fetchInsecureHttps(endpoint.url, requestOptions, endpoint.timeoutMs) : await this.fetchWithTimeout(endpoint.url, requestOptions, endpoint.timeoutMs);
1319
1355
  const responseTimeMs = Date.now() - startTime;
1320
1356
  const statusCode = response.status;
1321
1357
  let status;
@@ -1451,16 +1487,33 @@ var MonitorClient = class {
1451
1487
  async setupSystemMetricsCollection() {
1452
1488
  const settings = await this.fetchProjectSettings();
1453
1489
  if (!settings?.metricsEnabled) {
1490
+ this.metricsSettingsCheckTimer = setInterval(() => {
1491
+ this.fetchProjectSettings().then((latestSettings) => {
1492
+ if (latestSettings?.metricsEnabled) {
1493
+ clearInterval(this.metricsSettingsCheckTimer);
1494
+ this.metricsSettingsCheckTimer = null;
1495
+ this.startMetricsCollection(latestSettings);
1496
+ }
1497
+ }).catch(() => {
1498
+ });
1499
+ }, 3e5);
1454
1500
  return;
1455
1501
  }
1456
- const intervalMs = Math.max(3e4, (settings.metricsIntervalSeconds ?? 60) * 1e3);
1457
- const diskPaths = settings.metricsDiskPaths ?? ["/"];
1458
- console.log(`[MonitorClient] System metrics collection enabled (every ${intervalMs / 1e3}s, paths: ${diskPaths.join(", ")})`);
1459
- this.collectAndSubmitMetrics(diskPaths).catch((err) => {
1502
+ this.startMetricsCollection(settings);
1503
+ }
1504
+ startMetricsCollection(settings) {
1505
+ const intervalMs = Math.max(3e4, (settings.metricsIntervalSeconds ?? 3600) * 1e3);
1506
+ console.log(`[MonitorClient] System metrics collection enabled (every ${intervalMs / 1e3}s)`);
1507
+ const initialDiskPaths = settings.metricsDiskPaths ?? ["/"];
1508
+ this.collectAndSubmitMetrics(initialDiskPaths).catch((err) => {
1460
1509
  this.reportError("SYSTEM_METRICS", "Initial system metrics collection failed", err);
1461
1510
  });
1462
1511
  this.metricsCollectionTimer = setInterval(() => {
1463
- this.collectAndSubmitMetrics(diskPaths).catch((err) => {
1512
+ this.fetchProjectSettings().then((latestSettings) => {
1513
+ if (!latestSettings?.metricsEnabled) return;
1514
+ const diskPaths = latestSettings.metricsDiskPaths ?? ["/"];
1515
+ return this.collectAndSubmitMetrics(diskPaths);
1516
+ }).catch((err) => {
1464
1517
  this.reportError("SYSTEM_METRICS", "Scheduled system metrics collection failed", err);
1465
1518
  });
1466
1519
  }, intervalMs);
@@ -1470,6 +1523,10 @@ var MonitorClient = class {
1470
1523
  clearInterval(this.metricsCollectionTimer);
1471
1524
  this.metricsCollectionTimer = null;
1472
1525
  }
1526
+ if (this.metricsSettingsCheckTimer) {
1527
+ clearInterval(this.metricsSettingsCheckTimer);
1528
+ this.metricsSettingsCheckTimer = null;
1529
+ }
1473
1530
  }
1474
1531
  /**
1475
1532
  * Collect system metrics (CPU, RAM, disk) using built-in Node.js modules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceon-oy/monitor-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.2",
4
4
  "description": "Client SDK for Ceon Monitor - Error tracking, health monitoring, security events, and vulnerability scanning",
5
5
  "author": "Ceon",
6
6
  "license": "MIT",