@ceon-oy/monitor-sdk 1.1.5 → 1.2.1
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 +77 -0
- package/dist/index.d.mts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +220 -1
- package/dist/index.mjs +220 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Lightweight client SDK for integrating with the Ceon Monitor service. Provides e
|
|
|
11
11
|
- [Error Capture](#error-capture)
|
|
12
12
|
- [Technology Tracking](#technology-tracking)
|
|
13
13
|
- [Vulnerability Auditing](#vulnerability-auditing)
|
|
14
|
+
- [SDK-Based Health Checks](#sdk-based-health-checks)
|
|
14
15
|
- [Security Events](#security-events)
|
|
15
16
|
- [Framework Examples](#framework-examples)
|
|
16
17
|
- [Express.js](#expressjs)
|
|
@@ -104,6 +105,8 @@ interface MonitorClientConfig {
|
|
|
104
105
|
}[];
|
|
105
106
|
auditTimeoutMs?: number; // Timeout for npm audit command (default: 60000, max: 300000)
|
|
106
107
|
registryTimeoutMs?: number; // Timeout for npm registry requests (default: 5000, max: 30000)
|
|
108
|
+
healthCheckEnabled?: boolean; // Enable SDK-based health check polling (default: false)
|
|
109
|
+
healthCheckFetchIntervalMs?: number; // Interval to fetch health endpoints in ms (default: 60000)
|
|
107
110
|
}
|
|
108
111
|
```
|
|
109
112
|
|
|
@@ -413,6 +416,80 @@ setInterval(async () => {
|
|
|
413
416
|
}, 24 * 60 * 60 * 1000); // Daily
|
|
414
417
|
```
|
|
415
418
|
|
|
419
|
+
### SDK-Based Health Checks
|
|
420
|
+
|
|
421
|
+
The SDK can poll health endpoints that are inside your private network (VPN, internal services) and report the results to Ceon Monitor. This is useful for monitoring services that are not publicly accessible.
|
|
422
|
+
|
|
423
|
+
#### Enable SDK Polling
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
const monitor = new MonitorClient({
|
|
427
|
+
apiKey: process.env.CEON_MONITOR_API_KEY!,
|
|
428
|
+
endpoint: 'https://monitor.example.com',
|
|
429
|
+
environment: 'production',
|
|
430
|
+
|
|
431
|
+
// Enable SDK-based health check polling
|
|
432
|
+
healthCheckEnabled: true,
|
|
433
|
+
|
|
434
|
+
// Optional: customize fetch interval (default: 60000ms)
|
|
435
|
+
healthCheckFetchIntervalMs: 60000,
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
With `healthCheckEnabled: true`, the SDK will:
|
|
440
|
+
1. Fetch assigned health endpoints from the server every 60 seconds (configurable)
|
|
441
|
+
2. Poll each endpoint locally at its configured interval
|
|
442
|
+
3. Submit results back to the server in batches
|
|
443
|
+
4. Automatically handle endpoint additions/removals
|
|
444
|
+
|
|
445
|
+
#### How It Works
|
|
446
|
+
|
|
447
|
+
1. **Configure in Dashboard**: Create health endpoints in the Ceon Monitor dashboard and set their polling mode to "SDK"
|
|
448
|
+
2. **SDK Fetches Endpoints**: The SDK periodically fetches the list of endpoints assigned for SDK polling
|
|
449
|
+
3. **Local Polling**: The SDK polls each endpoint from your server (inside your VPN/private network)
|
|
450
|
+
4. **Report Results**: Health check results are batched and submitted to Ceon Monitor
|
|
451
|
+
|
|
452
|
+
#### Use Cases
|
|
453
|
+
|
|
454
|
+
- **Internal APIs**: Monitor APIs that are only accessible within your VPN
|
|
455
|
+
- **Database Health**: Check database connection health from inside your network
|
|
456
|
+
- **Microservices**: Monitor internal service-to-service communication
|
|
457
|
+
- **Private Infrastructure**: Any endpoint that's not publicly accessible
|
|
458
|
+
|
|
459
|
+
#### Configuration Options
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
interface MonitorClientConfig {
|
|
463
|
+
// ... other options
|
|
464
|
+
|
|
465
|
+
/** Enable SDK-based health check polling (default: false) */
|
|
466
|
+
healthCheckEnabled?: boolean;
|
|
467
|
+
|
|
468
|
+
/** Interval to fetch health endpoints from server in ms (default: 60000) */
|
|
469
|
+
healthCheckFetchIntervalMs?: number;
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
#### Manual Health Check Methods
|
|
474
|
+
|
|
475
|
+
You can also manually perform health checks:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
// Fetch endpoints assigned for SDK polling
|
|
479
|
+
const endpoints = await monitor.fetchHealthEndpoints();
|
|
480
|
+
|
|
481
|
+
// Submit health check results
|
|
482
|
+
const results = [
|
|
483
|
+
{
|
|
484
|
+
endpointId: 'clxyz123',
|
|
485
|
+
status: 'HEALTHY',
|
|
486
|
+
statusCode: 200,
|
|
487
|
+
responseTimeMs: 150,
|
|
488
|
+
},
|
|
489
|
+
];
|
|
490
|
+
await monitor.submitHealthResults(results);
|
|
491
|
+
```
|
|
492
|
+
|
|
416
493
|
### Security Events
|
|
417
494
|
|
|
418
495
|
#### Report Security Event
|
package/dist/index.d.mts
CHANGED
|
@@ -53,6 +53,10 @@ interface MonitorClientConfig {
|
|
|
53
53
|
allowInsecureHttp?: boolean;
|
|
54
54
|
/** Custom npm registry URL for fetching latest versions (default: https://registry.npmjs.org) */
|
|
55
55
|
npmRegistryUrl?: string;
|
|
56
|
+
/** Enable SDK-based health check polling (default: false) */
|
|
57
|
+
healthCheckEnabled?: boolean;
|
|
58
|
+
/** Interval to fetch health endpoints from server in ms (default: 60000) */
|
|
59
|
+
healthCheckFetchIntervalMs?: number;
|
|
56
60
|
}
|
|
57
61
|
interface TechnologyItem {
|
|
58
62
|
name: string;
|
|
@@ -150,6 +154,40 @@ interface MultiAuditSummary {
|
|
|
150
154
|
}>;
|
|
151
155
|
totalSummary: VulnerabilitySummary;
|
|
152
156
|
}
|
|
157
|
+
type HealthStatus = 'HEALTHY' | 'DEGRADED' | 'UNHEALTHY';
|
|
158
|
+
interface SdkHealthEndpoint {
|
|
159
|
+
id: string;
|
|
160
|
+
name: string;
|
|
161
|
+
url: string;
|
|
162
|
+
method: 'GET' | 'POST';
|
|
163
|
+
headers?: Record<string, string>;
|
|
164
|
+
body?: Record<string, unknown>;
|
|
165
|
+
environment: string;
|
|
166
|
+
intervalMs: number;
|
|
167
|
+
timeoutMs: number;
|
|
168
|
+
expectedStatus: number;
|
|
169
|
+
}
|
|
170
|
+
interface SdkHealthResult {
|
|
171
|
+
endpointId: string;
|
|
172
|
+
status: HealthStatus;
|
|
173
|
+
statusCode?: number;
|
|
174
|
+
responseTimeMs?: number;
|
|
175
|
+
errorMessage?: string;
|
|
176
|
+
}
|
|
177
|
+
interface SdkHealthEndpointsResponse {
|
|
178
|
+
success: boolean;
|
|
179
|
+
data: {
|
|
180
|
+
endpoints: SdkHealthEndpoint[];
|
|
181
|
+
pollIntervalMs: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
interface SdkHealthResultsResponse {
|
|
185
|
+
success: boolean;
|
|
186
|
+
data: {
|
|
187
|
+
processed: number;
|
|
188
|
+
total: number;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
153
191
|
|
|
154
192
|
declare class MonitorClient {
|
|
155
193
|
private apiKey;
|
|
@@ -182,6 +220,12 @@ declare class MonitorClient {
|
|
|
182
220
|
private auditTimeoutMs;
|
|
183
221
|
private registryTimeoutMs;
|
|
184
222
|
private npmRegistryUrl;
|
|
223
|
+
private healthCheckEnabled;
|
|
224
|
+
private healthCheckFetchIntervalMs;
|
|
225
|
+
private healthCheckFetchTimer;
|
|
226
|
+
private healthCheckTimers;
|
|
227
|
+
private healthCheckResultsQueue;
|
|
228
|
+
private healthCheckFlushTimer;
|
|
185
229
|
constructor(config: MonitorClientConfig);
|
|
186
230
|
/**
|
|
187
231
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -382,6 +426,45 @@ declare class MonitorClient {
|
|
|
382
426
|
* Yarn audit outputs one JSON object per line (newline-delimited JSON)
|
|
383
427
|
*/
|
|
384
428
|
private parseYarnAuditOutput;
|
|
429
|
+
/**
|
|
430
|
+
* Fetch health endpoints assigned to this project for SDK polling
|
|
431
|
+
*/
|
|
432
|
+
fetchHealthEndpoints(): Promise<SdkHealthEndpoint[]>;
|
|
433
|
+
/**
|
|
434
|
+
* Submit health check results to the server
|
|
435
|
+
*/
|
|
436
|
+
submitHealthResults(results: SdkHealthResult[]): Promise<{
|
|
437
|
+
processed: number;
|
|
438
|
+
total: number;
|
|
439
|
+
}>;
|
|
440
|
+
/**
|
|
441
|
+
* Perform a single health check on an endpoint
|
|
442
|
+
*/
|
|
443
|
+
private performHealthCheck;
|
|
444
|
+
/**
|
|
445
|
+
* Setup automatic health check polling
|
|
446
|
+
*/
|
|
447
|
+
private setupHealthCheckPolling;
|
|
448
|
+
/**
|
|
449
|
+
* Fetch health endpoints and schedule individual checks
|
|
450
|
+
*/
|
|
451
|
+
private fetchAndScheduleHealthChecks;
|
|
452
|
+
/**
|
|
453
|
+
* Schedule individual health check for an endpoint
|
|
454
|
+
*/
|
|
455
|
+
private scheduleHealthCheck;
|
|
456
|
+
/**
|
|
457
|
+
* Run a health check and queue the result
|
|
458
|
+
*/
|
|
459
|
+
private runHealthCheck;
|
|
460
|
+
/**
|
|
461
|
+
* Flush queued health results to the server
|
|
462
|
+
*/
|
|
463
|
+
private flushHealthResults;
|
|
464
|
+
/**
|
|
465
|
+
* Stop all health check timers
|
|
466
|
+
*/
|
|
467
|
+
private stopHealthCheckTimers;
|
|
385
468
|
}
|
|
386
469
|
|
|
387
|
-
export { type AuditPath, type AuditResult, type AuditSummary, type BruteForceDetectionResult, type DependencySource, type ErrorContext, type ErrorPayload, MonitorClient, type MonitorClientConfig, type MultiAuditSummary, type SecurityCategory, type SecurityEventInput, type SecurityEventPayload, type SecuritySeverity, type Severity, type TechnologyItem, type TechnologyType, type VulnerabilityItem, type VulnerabilitySeverity };
|
|
470
|
+
export { type AuditPath, type AuditResult, type AuditSummary, type BruteForceDetectionResult, type DependencySource, type ErrorContext, type ErrorPayload, type HealthStatus, MonitorClient, type MonitorClientConfig, type MultiAuditSummary, type SdkHealthEndpoint, type SdkHealthEndpointsResponse, type SdkHealthResult, type SdkHealthResultsResponse, type SecurityCategory, type SecurityEventInput, type SecurityEventPayload, type SecuritySeverity, type Severity, type TechnologyItem, type TechnologyType, type VulnerabilityItem, type VulnerabilitySeverity };
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,10 @@ interface MonitorClientConfig {
|
|
|
53
53
|
allowInsecureHttp?: boolean;
|
|
54
54
|
/** Custom npm registry URL for fetching latest versions (default: https://registry.npmjs.org) */
|
|
55
55
|
npmRegistryUrl?: string;
|
|
56
|
+
/** Enable SDK-based health check polling (default: false) */
|
|
57
|
+
healthCheckEnabled?: boolean;
|
|
58
|
+
/** Interval to fetch health endpoints from server in ms (default: 60000) */
|
|
59
|
+
healthCheckFetchIntervalMs?: number;
|
|
56
60
|
}
|
|
57
61
|
interface TechnologyItem {
|
|
58
62
|
name: string;
|
|
@@ -150,6 +154,40 @@ interface MultiAuditSummary {
|
|
|
150
154
|
}>;
|
|
151
155
|
totalSummary: VulnerabilitySummary;
|
|
152
156
|
}
|
|
157
|
+
type HealthStatus = 'HEALTHY' | 'DEGRADED' | 'UNHEALTHY';
|
|
158
|
+
interface SdkHealthEndpoint {
|
|
159
|
+
id: string;
|
|
160
|
+
name: string;
|
|
161
|
+
url: string;
|
|
162
|
+
method: 'GET' | 'POST';
|
|
163
|
+
headers?: Record<string, string>;
|
|
164
|
+
body?: Record<string, unknown>;
|
|
165
|
+
environment: string;
|
|
166
|
+
intervalMs: number;
|
|
167
|
+
timeoutMs: number;
|
|
168
|
+
expectedStatus: number;
|
|
169
|
+
}
|
|
170
|
+
interface SdkHealthResult {
|
|
171
|
+
endpointId: string;
|
|
172
|
+
status: HealthStatus;
|
|
173
|
+
statusCode?: number;
|
|
174
|
+
responseTimeMs?: number;
|
|
175
|
+
errorMessage?: string;
|
|
176
|
+
}
|
|
177
|
+
interface SdkHealthEndpointsResponse {
|
|
178
|
+
success: boolean;
|
|
179
|
+
data: {
|
|
180
|
+
endpoints: SdkHealthEndpoint[];
|
|
181
|
+
pollIntervalMs: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
interface SdkHealthResultsResponse {
|
|
185
|
+
success: boolean;
|
|
186
|
+
data: {
|
|
187
|
+
processed: number;
|
|
188
|
+
total: number;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
153
191
|
|
|
154
192
|
declare class MonitorClient {
|
|
155
193
|
private apiKey;
|
|
@@ -182,6 +220,12 @@ declare class MonitorClient {
|
|
|
182
220
|
private auditTimeoutMs;
|
|
183
221
|
private registryTimeoutMs;
|
|
184
222
|
private npmRegistryUrl;
|
|
223
|
+
private healthCheckEnabled;
|
|
224
|
+
private healthCheckFetchIntervalMs;
|
|
225
|
+
private healthCheckFetchTimer;
|
|
226
|
+
private healthCheckTimers;
|
|
227
|
+
private healthCheckResultsQueue;
|
|
228
|
+
private healthCheckFlushTimer;
|
|
185
229
|
constructor(config: MonitorClientConfig);
|
|
186
230
|
/**
|
|
187
231
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -382,6 +426,45 @@ declare class MonitorClient {
|
|
|
382
426
|
* Yarn audit outputs one JSON object per line (newline-delimited JSON)
|
|
383
427
|
*/
|
|
384
428
|
private parseYarnAuditOutput;
|
|
429
|
+
/**
|
|
430
|
+
* Fetch health endpoints assigned to this project for SDK polling
|
|
431
|
+
*/
|
|
432
|
+
fetchHealthEndpoints(): Promise<SdkHealthEndpoint[]>;
|
|
433
|
+
/**
|
|
434
|
+
* Submit health check results to the server
|
|
435
|
+
*/
|
|
436
|
+
submitHealthResults(results: SdkHealthResult[]): Promise<{
|
|
437
|
+
processed: number;
|
|
438
|
+
total: number;
|
|
439
|
+
}>;
|
|
440
|
+
/**
|
|
441
|
+
* Perform a single health check on an endpoint
|
|
442
|
+
*/
|
|
443
|
+
private performHealthCheck;
|
|
444
|
+
/**
|
|
445
|
+
* Setup automatic health check polling
|
|
446
|
+
*/
|
|
447
|
+
private setupHealthCheckPolling;
|
|
448
|
+
/**
|
|
449
|
+
* Fetch health endpoints and schedule individual checks
|
|
450
|
+
*/
|
|
451
|
+
private fetchAndScheduleHealthChecks;
|
|
452
|
+
/**
|
|
453
|
+
* Schedule individual health check for an endpoint
|
|
454
|
+
*/
|
|
455
|
+
private scheduleHealthCheck;
|
|
456
|
+
/**
|
|
457
|
+
* Run a health check and queue the result
|
|
458
|
+
*/
|
|
459
|
+
private runHealthCheck;
|
|
460
|
+
/**
|
|
461
|
+
* Flush queued health results to the server
|
|
462
|
+
*/
|
|
463
|
+
private flushHealthResults;
|
|
464
|
+
/**
|
|
465
|
+
* Stop all health check timers
|
|
466
|
+
*/
|
|
467
|
+
private stopHealthCheckTimers;
|
|
385
468
|
}
|
|
386
469
|
|
|
387
|
-
export { type AuditPath, type AuditResult, type AuditSummary, type BruteForceDetectionResult, type DependencySource, type ErrorContext, type ErrorPayload, MonitorClient, type MonitorClientConfig, type MultiAuditSummary, type SecurityCategory, type SecurityEventInput, type SecurityEventPayload, type SecuritySeverity, type Severity, type TechnologyItem, type TechnologyType, type VulnerabilityItem, type VulnerabilitySeverity };
|
|
470
|
+
export { type AuditPath, type AuditResult, type AuditSummary, type BruteForceDetectionResult, type DependencySource, type ErrorContext, type ErrorPayload, type HealthStatus, MonitorClient, type MonitorClientConfig, type MultiAuditSummary, type SdkHealthEndpoint, type SdkHealthEndpointsResponse, type SdkHealthResult, type SdkHealthResultsResponse, type SecurityCategory, type SecurityEventInput, type SecurityEventPayload, type SecuritySeverity, type Severity, type TechnologyItem, type TechnologyType, type VulnerabilityItem, type VulnerabilitySeverity };
|
package/dist/index.js
CHANGED
|
@@ -67,8 +67,16 @@ var CONFIG_LIMITS = {
|
|
|
67
67
|
// 60 seconds max for all dependency syncs
|
|
68
68
|
AUDIT_MULTI_PATH_TIMEOUT_MS: 18e4,
|
|
69
69
|
// 3 minutes max for all audit paths
|
|
70
|
-
REGISTRY_CONCURRENCY_LIMIT: 5
|
|
70
|
+
REGISTRY_CONCURRENCY_LIMIT: 5,
|
|
71
71
|
// Limit parallel requests to avoid rate limiting
|
|
72
|
+
HEALTH_CHECK_FETCH_INTERVAL_MS: 6e4,
|
|
73
|
+
// 60 seconds (default)
|
|
74
|
+
HEALTH_CHECK_MIN_INTERVAL_MS: 1e4,
|
|
75
|
+
// 10 seconds (min endpoint interval)
|
|
76
|
+
HEALTH_CHECK_MAX_BATCH_SIZE: 50,
|
|
77
|
+
// Max results per batch
|
|
78
|
+
HEALTH_CHECK_FLUSH_INTERVAL_MS: 3e4
|
|
79
|
+
// Flush health results every 30 seconds
|
|
72
80
|
};
|
|
73
81
|
var MonitorClient = class {
|
|
74
82
|
constructor(config) {
|
|
@@ -82,6 +90,10 @@ var MonitorClient = class {
|
|
|
82
90
|
this.lastScanTime = null;
|
|
83
91
|
this.lastKnownScanRequestedAt = null;
|
|
84
92
|
this.lastKnownTechScanRequestedAt = null;
|
|
93
|
+
this.healthCheckFetchTimer = null;
|
|
94
|
+
this.healthCheckTimers = /* @__PURE__ */ new Map();
|
|
95
|
+
this.healthCheckResultsQueue = [];
|
|
96
|
+
this.healthCheckFlushTimer = null;
|
|
85
97
|
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
86
98
|
throw new Error("[MonitorClient] API key is required");
|
|
87
99
|
}
|
|
@@ -141,6 +153,8 @@ var MonitorClient = class {
|
|
|
141
153
|
this.auditTimeoutMs = Math.min(CONFIG_LIMITS.MAX_AUDIT_TIMEOUT_MS, Math.max(1e3, config.auditTimeoutMs || CONFIG_LIMITS.AUDIT_TIMEOUT_MS));
|
|
142
154
|
this.registryTimeoutMs = Math.min(CONFIG_LIMITS.MAX_REGISTRY_TIMEOUT_MS, Math.max(1e3, config.registryTimeoutMs || CONFIG_LIMITS.REGISTRY_TIMEOUT_MS));
|
|
143
155
|
this.npmRegistryUrl = (config.npmRegistryUrl || "https://registry.npmjs.org").replace(/\/$/, "");
|
|
156
|
+
this.healthCheckEnabled = config.healthCheckEnabled || false;
|
|
157
|
+
this.healthCheckFetchIntervalMs = config.healthCheckFetchIntervalMs || CONFIG_LIMITS.HEALTH_CHECK_FETCH_INTERVAL_MS;
|
|
144
158
|
this.startFlushTimer();
|
|
145
159
|
if (this.trackDependencies) {
|
|
146
160
|
this.syncDependencies().catch((err) => {
|
|
@@ -152,6 +166,11 @@ var MonitorClient = class {
|
|
|
152
166
|
console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
|
|
153
167
|
});
|
|
154
168
|
}
|
|
169
|
+
if (this.healthCheckEnabled) {
|
|
170
|
+
this.setupHealthCheckPolling().catch((err) => {
|
|
171
|
+
console.error("[MonitorClient] Failed to setup health check polling:", err instanceof Error ? err.message : String(err));
|
|
172
|
+
});
|
|
173
|
+
}
|
|
155
174
|
}
|
|
156
175
|
/**
|
|
157
176
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -300,7 +319,9 @@ var MonitorClient = class {
|
|
|
300
319
|
this.isClosed = true;
|
|
301
320
|
this.stopFlushTimer();
|
|
302
321
|
this.stopAuditIntervalTimer();
|
|
322
|
+
this.stopHealthCheckTimers();
|
|
303
323
|
await this.flush();
|
|
324
|
+
await this.flushHealthResults();
|
|
304
325
|
}
|
|
305
326
|
stopAuditIntervalTimer() {
|
|
306
327
|
if (this.auditIntervalTimer) {
|
|
@@ -1190,6 +1211,204 @@ var MonitorClient = class {
|
|
|
1190
1211
|
}
|
|
1191
1212
|
return vulnerabilities;
|
|
1192
1213
|
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Fetch health endpoints assigned to this project for SDK polling
|
|
1216
|
+
*/
|
|
1217
|
+
async fetchHealthEndpoints() {
|
|
1218
|
+
try {
|
|
1219
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/health/sdk/endpoints`, {
|
|
1220
|
+
method: "GET",
|
|
1221
|
+
headers: {
|
|
1222
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
1223
|
+
"Content-Type": "application/json"
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
if (!response.ok) {
|
|
1227
|
+
console.warn("[MonitorClient] Failed to fetch health endpoints:", response.status);
|
|
1228
|
+
return [];
|
|
1229
|
+
}
|
|
1230
|
+
const result = await response.json();
|
|
1231
|
+
return result.data?.endpoints || [];
|
|
1232
|
+
} catch (err) {
|
|
1233
|
+
console.warn("[MonitorClient] Failed to fetch health endpoints:", err instanceof Error ? err.message : String(err));
|
|
1234
|
+
return [];
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Submit health check results to the server
|
|
1239
|
+
*/
|
|
1240
|
+
async submitHealthResults(results) {
|
|
1241
|
+
if (results.length === 0) {
|
|
1242
|
+
return { processed: 0, total: 0 };
|
|
1243
|
+
}
|
|
1244
|
+
try {
|
|
1245
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/health/sdk/results`, {
|
|
1246
|
+
method: "POST",
|
|
1247
|
+
headers: {
|
|
1248
|
+
"Content-Type": "application/json",
|
|
1249
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1250
|
+
},
|
|
1251
|
+
body: JSON.stringify({ results })
|
|
1252
|
+
});
|
|
1253
|
+
if (!response.ok) {
|
|
1254
|
+
const errorText = await response.text().catch(() => "");
|
|
1255
|
+
throw new Error(`HTTP ${response.status}${errorText ? `: ${this.sanitizeErrorResponse(errorText)}` : ""}`);
|
|
1256
|
+
}
|
|
1257
|
+
const result = await response.json();
|
|
1258
|
+
return result.data;
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
console.error("[MonitorClient] Failed to submit health results:", err instanceof Error ? err.message : String(err));
|
|
1261
|
+
throw err;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Perform a single health check on an endpoint
|
|
1266
|
+
*/
|
|
1267
|
+
async performHealthCheck(endpoint) {
|
|
1268
|
+
const startTime = Date.now();
|
|
1269
|
+
try {
|
|
1270
|
+
const requestOptions = {
|
|
1271
|
+
method: endpoint.method,
|
|
1272
|
+
headers: endpoint.headers || {}
|
|
1273
|
+
};
|
|
1274
|
+
if (endpoint.method === "POST" && endpoint.body) {
|
|
1275
|
+
requestOptions.body = JSON.stringify(endpoint.body);
|
|
1276
|
+
requestOptions.headers = {
|
|
1277
|
+
...requestOptions.headers,
|
|
1278
|
+
"Content-Type": "application/json"
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const response = await this.fetchWithTimeout(
|
|
1282
|
+
endpoint.url,
|
|
1283
|
+
requestOptions,
|
|
1284
|
+
endpoint.timeoutMs
|
|
1285
|
+
);
|
|
1286
|
+
const responseTimeMs = Date.now() - startTime;
|
|
1287
|
+
const statusCode = response.status;
|
|
1288
|
+
let status;
|
|
1289
|
+
if (statusCode === endpoint.expectedStatus) {
|
|
1290
|
+
const slowThreshold = endpoint.timeoutMs * 0.8;
|
|
1291
|
+
if (responseTimeMs > slowThreshold) {
|
|
1292
|
+
status = "DEGRADED";
|
|
1293
|
+
} else {
|
|
1294
|
+
status = "HEALTHY";
|
|
1295
|
+
}
|
|
1296
|
+
} else {
|
|
1297
|
+
status = "UNHEALTHY";
|
|
1298
|
+
}
|
|
1299
|
+
return {
|
|
1300
|
+
endpointId: endpoint.id,
|
|
1301
|
+
status,
|
|
1302
|
+
statusCode,
|
|
1303
|
+
responseTimeMs
|
|
1304
|
+
};
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
const responseTimeMs = Date.now() - startTime;
|
|
1307
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1308
|
+
return {
|
|
1309
|
+
endpointId: endpoint.id,
|
|
1310
|
+
status: "UNHEALTHY",
|
|
1311
|
+
responseTimeMs,
|
|
1312
|
+
errorMessage: this.sanitizeErrorResponse(errorMessage)
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Setup automatic health check polling
|
|
1318
|
+
*/
|
|
1319
|
+
async setupHealthCheckPolling() {
|
|
1320
|
+
console.log("[MonitorClient] Setting up health check polling...");
|
|
1321
|
+
await this.fetchAndScheduleHealthChecks();
|
|
1322
|
+
this.healthCheckFetchTimer = setInterval(() => {
|
|
1323
|
+
this.fetchAndScheduleHealthChecks().catch((err) => {
|
|
1324
|
+
console.error("[MonitorClient] Failed to fetch and schedule health checks:", err);
|
|
1325
|
+
});
|
|
1326
|
+
}, this.healthCheckFetchIntervalMs);
|
|
1327
|
+
this.healthCheckFlushTimer = setInterval(() => {
|
|
1328
|
+
this.flushHealthResults().catch((err) => {
|
|
1329
|
+
console.error("[MonitorClient] Failed to flush health results:", err);
|
|
1330
|
+
});
|
|
1331
|
+
}, CONFIG_LIMITS.HEALTH_CHECK_FLUSH_INTERVAL_MS);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Fetch health endpoints and schedule individual checks
|
|
1335
|
+
*/
|
|
1336
|
+
async fetchAndScheduleHealthChecks() {
|
|
1337
|
+
try {
|
|
1338
|
+
const endpoints = await this.fetchHealthEndpoints();
|
|
1339
|
+
if (endpoints.length === 0) {
|
|
1340
|
+
console.log("[MonitorClient] No health endpoints assigned for SDK polling");
|
|
1341
|
+
this.stopHealthCheckTimers();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
console.log(`[MonitorClient] Fetched ${endpoints.length} health endpoints for polling`);
|
|
1345
|
+
this.stopHealthCheckTimers();
|
|
1346
|
+
for (const endpoint of endpoints) {
|
|
1347
|
+
this.scheduleHealthCheck(endpoint);
|
|
1348
|
+
}
|
|
1349
|
+
} catch (err) {
|
|
1350
|
+
console.error("[MonitorClient] Failed to fetch health endpoints:", err);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Schedule individual health check for an endpoint
|
|
1355
|
+
*/
|
|
1356
|
+
scheduleHealthCheck(endpoint) {
|
|
1357
|
+
const intervalMs = Math.max(CONFIG_LIMITS.HEALTH_CHECK_MIN_INTERVAL_MS, endpoint.intervalMs);
|
|
1358
|
+
this.runHealthCheck(endpoint).catch((err) => {
|
|
1359
|
+
console.error(`[MonitorClient] Health check failed for ${endpoint.name}:`, err);
|
|
1360
|
+
});
|
|
1361
|
+
const timer = setInterval(() => {
|
|
1362
|
+
this.runHealthCheck(endpoint).catch((err) => {
|
|
1363
|
+
console.error(`[MonitorClient] Health check failed for ${endpoint.name}:`, err);
|
|
1364
|
+
});
|
|
1365
|
+
}, intervalMs);
|
|
1366
|
+
this.healthCheckTimers.set(endpoint.id, timer);
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Run a health check and queue the result
|
|
1370
|
+
*/
|
|
1371
|
+
async runHealthCheck(endpoint) {
|
|
1372
|
+
const result = await this.performHealthCheck(endpoint);
|
|
1373
|
+
this.healthCheckResultsQueue.push(result);
|
|
1374
|
+
if (this.healthCheckResultsQueue.length >= CONFIG_LIMITS.HEALTH_CHECK_MAX_BATCH_SIZE) {
|
|
1375
|
+
await this.flushHealthResults();
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Flush queued health results to the server
|
|
1380
|
+
*/
|
|
1381
|
+
async flushHealthResults() {
|
|
1382
|
+
if (this.healthCheckResultsQueue.length === 0) return;
|
|
1383
|
+
const results = [...this.healthCheckResultsQueue];
|
|
1384
|
+
this.healthCheckResultsQueue = [];
|
|
1385
|
+
try {
|
|
1386
|
+
const response = await this.submitHealthResults(results);
|
|
1387
|
+
console.log(`[MonitorClient] Submitted ${response.processed}/${response.total} health check results`);
|
|
1388
|
+
} catch (err) {
|
|
1389
|
+
console.error("[MonitorClient] Failed to flush health results:", err);
|
|
1390
|
+
if (this.healthCheckResultsQueue.length + results.length <= CONFIG_LIMITS.HEALTH_CHECK_MAX_BATCH_SIZE) {
|
|
1391
|
+
this.healthCheckResultsQueue.unshift(...results);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Stop all health check timers
|
|
1397
|
+
*/
|
|
1398
|
+
stopHealthCheckTimers() {
|
|
1399
|
+
if (this.healthCheckFetchTimer) {
|
|
1400
|
+
clearInterval(this.healthCheckFetchTimer);
|
|
1401
|
+
this.healthCheckFetchTimer = null;
|
|
1402
|
+
}
|
|
1403
|
+
if (this.healthCheckFlushTimer) {
|
|
1404
|
+
clearInterval(this.healthCheckFlushTimer);
|
|
1405
|
+
this.healthCheckFlushTimer = null;
|
|
1406
|
+
}
|
|
1407
|
+
for (const [endpointId, timer] of this.healthCheckTimers.entries()) {
|
|
1408
|
+
clearInterval(timer);
|
|
1409
|
+
this.healthCheckTimers.delete(endpointId);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1193
1412
|
};
|
|
1194
1413
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1195
1414
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -31,8 +31,16 @@ var CONFIG_LIMITS = {
|
|
|
31
31
|
// 60 seconds max for all dependency syncs
|
|
32
32
|
AUDIT_MULTI_PATH_TIMEOUT_MS: 18e4,
|
|
33
33
|
// 3 minutes max for all audit paths
|
|
34
|
-
REGISTRY_CONCURRENCY_LIMIT: 5
|
|
34
|
+
REGISTRY_CONCURRENCY_LIMIT: 5,
|
|
35
35
|
// Limit parallel requests to avoid rate limiting
|
|
36
|
+
HEALTH_CHECK_FETCH_INTERVAL_MS: 6e4,
|
|
37
|
+
// 60 seconds (default)
|
|
38
|
+
HEALTH_CHECK_MIN_INTERVAL_MS: 1e4,
|
|
39
|
+
// 10 seconds (min endpoint interval)
|
|
40
|
+
HEALTH_CHECK_MAX_BATCH_SIZE: 50,
|
|
41
|
+
// Max results per batch
|
|
42
|
+
HEALTH_CHECK_FLUSH_INTERVAL_MS: 3e4
|
|
43
|
+
// Flush health results every 30 seconds
|
|
36
44
|
};
|
|
37
45
|
var MonitorClient = class {
|
|
38
46
|
constructor(config) {
|
|
@@ -46,6 +54,10 @@ var MonitorClient = class {
|
|
|
46
54
|
this.lastScanTime = null;
|
|
47
55
|
this.lastKnownScanRequestedAt = null;
|
|
48
56
|
this.lastKnownTechScanRequestedAt = null;
|
|
57
|
+
this.healthCheckFetchTimer = null;
|
|
58
|
+
this.healthCheckTimers = /* @__PURE__ */ new Map();
|
|
59
|
+
this.healthCheckResultsQueue = [];
|
|
60
|
+
this.healthCheckFlushTimer = null;
|
|
49
61
|
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
50
62
|
throw new Error("[MonitorClient] API key is required");
|
|
51
63
|
}
|
|
@@ -105,6 +117,8 @@ var MonitorClient = class {
|
|
|
105
117
|
this.auditTimeoutMs = Math.min(CONFIG_LIMITS.MAX_AUDIT_TIMEOUT_MS, Math.max(1e3, config.auditTimeoutMs || CONFIG_LIMITS.AUDIT_TIMEOUT_MS));
|
|
106
118
|
this.registryTimeoutMs = Math.min(CONFIG_LIMITS.MAX_REGISTRY_TIMEOUT_MS, Math.max(1e3, config.registryTimeoutMs || CONFIG_LIMITS.REGISTRY_TIMEOUT_MS));
|
|
107
119
|
this.npmRegistryUrl = (config.npmRegistryUrl || "https://registry.npmjs.org").replace(/\/$/, "");
|
|
120
|
+
this.healthCheckEnabled = config.healthCheckEnabled || false;
|
|
121
|
+
this.healthCheckFetchIntervalMs = config.healthCheckFetchIntervalMs || CONFIG_LIMITS.HEALTH_CHECK_FETCH_INTERVAL_MS;
|
|
108
122
|
this.startFlushTimer();
|
|
109
123
|
if (this.trackDependencies) {
|
|
110
124
|
this.syncDependencies().catch((err) => {
|
|
@@ -116,6 +130,11 @@ var MonitorClient = class {
|
|
|
116
130
|
console.error("[MonitorClient] Failed to setup auto audit:", err instanceof Error ? err.message : String(err));
|
|
117
131
|
});
|
|
118
132
|
}
|
|
133
|
+
if (this.healthCheckEnabled) {
|
|
134
|
+
this.setupHealthCheckPolling().catch((err) => {
|
|
135
|
+
console.error("[MonitorClient] Failed to setup health check polling:", err instanceof Error ? err.message : String(err));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
119
138
|
}
|
|
120
139
|
/**
|
|
121
140
|
* Security: Validate and sanitize metadata to prevent oversized payloads
|
|
@@ -264,7 +283,9 @@ var MonitorClient = class {
|
|
|
264
283
|
this.isClosed = true;
|
|
265
284
|
this.stopFlushTimer();
|
|
266
285
|
this.stopAuditIntervalTimer();
|
|
286
|
+
this.stopHealthCheckTimers();
|
|
267
287
|
await this.flush();
|
|
288
|
+
await this.flushHealthResults();
|
|
268
289
|
}
|
|
269
290
|
stopAuditIntervalTimer() {
|
|
270
291
|
if (this.auditIntervalTimer) {
|
|
@@ -1154,6 +1175,204 @@ var MonitorClient = class {
|
|
|
1154
1175
|
}
|
|
1155
1176
|
return vulnerabilities;
|
|
1156
1177
|
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Fetch health endpoints assigned to this project for SDK polling
|
|
1180
|
+
*/
|
|
1181
|
+
async fetchHealthEndpoints() {
|
|
1182
|
+
try {
|
|
1183
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/health/sdk/endpoints`, {
|
|
1184
|
+
method: "GET",
|
|
1185
|
+
headers: {
|
|
1186
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
1187
|
+
"Content-Type": "application/json"
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
if (!response.ok) {
|
|
1191
|
+
console.warn("[MonitorClient] Failed to fetch health endpoints:", response.status);
|
|
1192
|
+
return [];
|
|
1193
|
+
}
|
|
1194
|
+
const result = await response.json();
|
|
1195
|
+
return result.data?.endpoints || [];
|
|
1196
|
+
} catch (err) {
|
|
1197
|
+
console.warn("[MonitorClient] Failed to fetch health endpoints:", err instanceof Error ? err.message : String(err));
|
|
1198
|
+
return [];
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Submit health check results to the server
|
|
1203
|
+
*/
|
|
1204
|
+
async submitHealthResults(results) {
|
|
1205
|
+
if (results.length === 0) {
|
|
1206
|
+
return { processed: 0, total: 0 };
|
|
1207
|
+
}
|
|
1208
|
+
try {
|
|
1209
|
+
const response = await this.fetchWithTimeout(`${this.endpoint}/api/v1/health/sdk/results`, {
|
|
1210
|
+
method: "POST",
|
|
1211
|
+
headers: {
|
|
1212
|
+
"Content-Type": "application/json",
|
|
1213
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1214
|
+
},
|
|
1215
|
+
body: JSON.stringify({ results })
|
|
1216
|
+
});
|
|
1217
|
+
if (!response.ok) {
|
|
1218
|
+
const errorText = await response.text().catch(() => "");
|
|
1219
|
+
throw new Error(`HTTP ${response.status}${errorText ? `: ${this.sanitizeErrorResponse(errorText)}` : ""}`);
|
|
1220
|
+
}
|
|
1221
|
+
const result = await response.json();
|
|
1222
|
+
return result.data;
|
|
1223
|
+
} catch (err) {
|
|
1224
|
+
console.error("[MonitorClient] Failed to submit health results:", err instanceof Error ? err.message : String(err));
|
|
1225
|
+
throw err;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Perform a single health check on an endpoint
|
|
1230
|
+
*/
|
|
1231
|
+
async performHealthCheck(endpoint) {
|
|
1232
|
+
const startTime = Date.now();
|
|
1233
|
+
try {
|
|
1234
|
+
const requestOptions = {
|
|
1235
|
+
method: endpoint.method,
|
|
1236
|
+
headers: endpoint.headers || {}
|
|
1237
|
+
};
|
|
1238
|
+
if (endpoint.method === "POST" && endpoint.body) {
|
|
1239
|
+
requestOptions.body = JSON.stringify(endpoint.body);
|
|
1240
|
+
requestOptions.headers = {
|
|
1241
|
+
...requestOptions.headers,
|
|
1242
|
+
"Content-Type": "application/json"
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
const response = await this.fetchWithTimeout(
|
|
1246
|
+
endpoint.url,
|
|
1247
|
+
requestOptions,
|
|
1248
|
+
endpoint.timeoutMs
|
|
1249
|
+
);
|
|
1250
|
+
const responseTimeMs = Date.now() - startTime;
|
|
1251
|
+
const statusCode = response.status;
|
|
1252
|
+
let status;
|
|
1253
|
+
if (statusCode === endpoint.expectedStatus) {
|
|
1254
|
+
const slowThreshold = endpoint.timeoutMs * 0.8;
|
|
1255
|
+
if (responseTimeMs > slowThreshold) {
|
|
1256
|
+
status = "DEGRADED";
|
|
1257
|
+
} else {
|
|
1258
|
+
status = "HEALTHY";
|
|
1259
|
+
}
|
|
1260
|
+
} else {
|
|
1261
|
+
status = "UNHEALTHY";
|
|
1262
|
+
}
|
|
1263
|
+
return {
|
|
1264
|
+
endpointId: endpoint.id,
|
|
1265
|
+
status,
|
|
1266
|
+
statusCode,
|
|
1267
|
+
responseTimeMs
|
|
1268
|
+
};
|
|
1269
|
+
} catch (err) {
|
|
1270
|
+
const responseTimeMs = Date.now() - startTime;
|
|
1271
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1272
|
+
return {
|
|
1273
|
+
endpointId: endpoint.id,
|
|
1274
|
+
status: "UNHEALTHY",
|
|
1275
|
+
responseTimeMs,
|
|
1276
|
+
errorMessage: this.sanitizeErrorResponse(errorMessage)
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Setup automatic health check polling
|
|
1282
|
+
*/
|
|
1283
|
+
async setupHealthCheckPolling() {
|
|
1284
|
+
console.log("[MonitorClient] Setting up health check polling...");
|
|
1285
|
+
await this.fetchAndScheduleHealthChecks();
|
|
1286
|
+
this.healthCheckFetchTimer = setInterval(() => {
|
|
1287
|
+
this.fetchAndScheduleHealthChecks().catch((err) => {
|
|
1288
|
+
console.error("[MonitorClient] Failed to fetch and schedule health checks:", err);
|
|
1289
|
+
});
|
|
1290
|
+
}, this.healthCheckFetchIntervalMs);
|
|
1291
|
+
this.healthCheckFlushTimer = setInterval(() => {
|
|
1292
|
+
this.flushHealthResults().catch((err) => {
|
|
1293
|
+
console.error("[MonitorClient] Failed to flush health results:", err);
|
|
1294
|
+
});
|
|
1295
|
+
}, CONFIG_LIMITS.HEALTH_CHECK_FLUSH_INTERVAL_MS);
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Fetch health endpoints and schedule individual checks
|
|
1299
|
+
*/
|
|
1300
|
+
async fetchAndScheduleHealthChecks() {
|
|
1301
|
+
try {
|
|
1302
|
+
const endpoints = await this.fetchHealthEndpoints();
|
|
1303
|
+
if (endpoints.length === 0) {
|
|
1304
|
+
console.log("[MonitorClient] No health endpoints assigned for SDK polling");
|
|
1305
|
+
this.stopHealthCheckTimers();
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
console.log(`[MonitorClient] Fetched ${endpoints.length} health endpoints for polling`);
|
|
1309
|
+
this.stopHealthCheckTimers();
|
|
1310
|
+
for (const endpoint of endpoints) {
|
|
1311
|
+
this.scheduleHealthCheck(endpoint);
|
|
1312
|
+
}
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
console.error("[MonitorClient] Failed to fetch health endpoints:", err);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Schedule individual health check for an endpoint
|
|
1319
|
+
*/
|
|
1320
|
+
scheduleHealthCheck(endpoint) {
|
|
1321
|
+
const intervalMs = Math.max(CONFIG_LIMITS.HEALTH_CHECK_MIN_INTERVAL_MS, endpoint.intervalMs);
|
|
1322
|
+
this.runHealthCheck(endpoint).catch((err) => {
|
|
1323
|
+
console.error(`[MonitorClient] Health check failed for ${endpoint.name}:`, err);
|
|
1324
|
+
});
|
|
1325
|
+
const timer = setInterval(() => {
|
|
1326
|
+
this.runHealthCheck(endpoint).catch((err) => {
|
|
1327
|
+
console.error(`[MonitorClient] Health check failed for ${endpoint.name}:`, err);
|
|
1328
|
+
});
|
|
1329
|
+
}, intervalMs);
|
|
1330
|
+
this.healthCheckTimers.set(endpoint.id, timer);
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Run a health check and queue the result
|
|
1334
|
+
*/
|
|
1335
|
+
async runHealthCheck(endpoint) {
|
|
1336
|
+
const result = await this.performHealthCheck(endpoint);
|
|
1337
|
+
this.healthCheckResultsQueue.push(result);
|
|
1338
|
+
if (this.healthCheckResultsQueue.length >= CONFIG_LIMITS.HEALTH_CHECK_MAX_BATCH_SIZE) {
|
|
1339
|
+
await this.flushHealthResults();
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Flush queued health results to the server
|
|
1344
|
+
*/
|
|
1345
|
+
async flushHealthResults() {
|
|
1346
|
+
if (this.healthCheckResultsQueue.length === 0) return;
|
|
1347
|
+
const results = [...this.healthCheckResultsQueue];
|
|
1348
|
+
this.healthCheckResultsQueue = [];
|
|
1349
|
+
try {
|
|
1350
|
+
const response = await this.submitHealthResults(results);
|
|
1351
|
+
console.log(`[MonitorClient] Submitted ${response.processed}/${response.total} health check results`);
|
|
1352
|
+
} catch (err) {
|
|
1353
|
+
console.error("[MonitorClient] Failed to flush health results:", err);
|
|
1354
|
+
if (this.healthCheckResultsQueue.length + results.length <= CONFIG_LIMITS.HEALTH_CHECK_MAX_BATCH_SIZE) {
|
|
1355
|
+
this.healthCheckResultsQueue.unshift(...results);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Stop all health check timers
|
|
1361
|
+
*/
|
|
1362
|
+
stopHealthCheckTimers() {
|
|
1363
|
+
if (this.healthCheckFetchTimer) {
|
|
1364
|
+
clearInterval(this.healthCheckFetchTimer);
|
|
1365
|
+
this.healthCheckFetchTimer = null;
|
|
1366
|
+
}
|
|
1367
|
+
if (this.healthCheckFlushTimer) {
|
|
1368
|
+
clearInterval(this.healthCheckFlushTimer);
|
|
1369
|
+
this.healthCheckFlushTimer = null;
|
|
1370
|
+
}
|
|
1371
|
+
for (const [endpointId, timer] of this.healthCheckTimers.entries()) {
|
|
1372
|
+
clearInterval(timer);
|
|
1373
|
+
this.healthCheckTimers.delete(endpointId);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1157
1376
|
};
|
|
1158
1377
|
export {
|
|
1159
1378
|
MonitorClient
|
package/package.json
CHANGED