@uploadista/server 0.0.18-beta.16 → 0.0.18-beta.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 +0 -83
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +9 -794
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -794
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/core/http-handlers/flow-http-handlers.ts +6 -61
- package/src/core/http-handlers/http-handlers.ts +0 -50
- package/src/core/http-handlers/upload-http-handlers.ts +3 -56
- package/src/core/routes.ts +0 -171
- package/src/core/server.ts +11 -71
- package/src/core/types.ts +0 -150
- package/src/index.ts +0 -2
- package/src/plugins-typing.ts +1 -1
- package/src/service.ts +3 -101
- package/docs/HEALTH_CHECKS.md +0 -256
- package/src/core/health-check-service.ts +0 -367
- package/src/core/http-handlers/dlq-http-handlers.ts +0 -219
- package/src/core/http-handlers/health-http-handlers.ts +0 -150
- package/src/permissions/errors.ts +0 -105
- package/src/permissions/index.ts +0 -9
- package/src/permissions/matcher.ts +0 -139
- package/src/permissions/types.ts +0 -151
- package/src/usage-hooks/index.ts +0 -8
- package/src/usage-hooks/service.ts +0 -162
- package/src/usage-hooks/types.ts +0 -221
- package/tests/core/health-check-service.test.ts +0 -570
- package/tests/core/http-handlers/health-handlers.test.ts +0 -351
package/docs/HEALTH_CHECKS.md
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
# Health Check Endpoints
|
|
2
|
-
|
|
3
|
-
Uploadista SDK provides production-ready health check endpoints for Kubernetes deployments, load balancer health checks, and operational monitoring.
|
|
4
|
-
|
|
5
|
-
## Endpoints
|
|
6
|
-
|
|
7
|
-
All health endpoints are available at `/{baseUrl}/` (not under `/api/`):
|
|
8
|
-
|
|
9
|
-
| Endpoint | Aliases | Purpose | HTTP Status |
|
|
10
|
-
|----------|---------|---------|-------------|
|
|
11
|
-
| `/health` | `/healthz` | Liveness probe - is the server alive? | Always 200 |
|
|
12
|
-
| `/ready` | `/readyz` | Readiness probe - can the server accept traffic? | 200 or 503 |
|
|
13
|
-
| `/health/components` | - | Detailed component status for debugging | Always 200 |
|
|
14
|
-
|
|
15
|
-
## Liveness Probe (`/health`)
|
|
16
|
-
|
|
17
|
-
The liveness endpoint returns immediately without checking dependencies. Use this for Kubernetes liveness probes to detect if the server process is stuck.
|
|
18
|
-
|
|
19
|
-
**Request:**
|
|
20
|
-
```http
|
|
21
|
-
GET /uploadista/health
|
|
22
|
-
Accept: application/json
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Response (JSON):**
|
|
26
|
-
```json
|
|
27
|
-
{
|
|
28
|
-
"status": "healthy",
|
|
29
|
-
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
30
|
-
"uptime": 3600000,
|
|
31
|
-
"version": "1.2.3"
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
**Response (Plain Text):**
|
|
36
|
-
```http
|
|
37
|
-
GET /uploadista/health
|
|
38
|
-
Accept: text/plain
|
|
39
|
-
|
|
40
|
-
OK
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Readiness Probe (`/ready`)
|
|
44
|
-
|
|
45
|
-
The readiness endpoint checks all critical dependencies before accepting traffic. Use this for Kubernetes readiness probes to prevent traffic from being routed to unhealthy instances.
|
|
46
|
-
|
|
47
|
-
**Request:**
|
|
48
|
-
```http
|
|
49
|
-
GET /uploadista/ready
|
|
50
|
-
Accept: application/json
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Response (Healthy):**
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"status": "healthy",
|
|
57
|
-
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
58
|
-
"uptime": 3600000,
|
|
59
|
-
"components": {
|
|
60
|
-
"storage": {
|
|
61
|
-
"status": "healthy",
|
|
62
|
-
"latency": 15,
|
|
63
|
-
"message": "Storage backend configured",
|
|
64
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
65
|
-
},
|
|
66
|
-
"kvStore": {
|
|
67
|
-
"status": "healthy",
|
|
68
|
-
"latency": 5,
|
|
69
|
-
"message": "KV store configured",
|
|
70
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
71
|
-
},
|
|
72
|
-
"eventBroadcaster": {
|
|
73
|
-
"status": "healthy",
|
|
74
|
-
"latency": 2,
|
|
75
|
-
"message": "Event broadcaster configured",
|
|
76
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Response (Unhealthy - HTTP 503):**
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"status": "unhealthy",
|
|
86
|
-
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
87
|
-
"components": {
|
|
88
|
-
"storage": {
|
|
89
|
-
"status": "unhealthy",
|
|
90
|
-
"latency": 5000,
|
|
91
|
-
"message": "Connection timeout",
|
|
92
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
93
|
-
},
|
|
94
|
-
"kvStore": {
|
|
95
|
-
"status": "healthy",
|
|
96
|
-
"latency": 5,
|
|
97
|
-
"message": "KV store configured",
|
|
98
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Component Details (`/health/components`)
|
|
105
|
-
|
|
106
|
-
The components endpoint returns detailed health information including circuit breaker and dead letter queue status. Always returns HTTP 200 for debugging purposes.
|
|
107
|
-
|
|
108
|
-
**Request:**
|
|
109
|
-
```http
|
|
110
|
-
GET /uploadista/health/components
|
|
111
|
-
Accept: application/json
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Response:**
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"status": "degraded",
|
|
118
|
-
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
119
|
-
"uptime": 3600000,
|
|
120
|
-
"version": "1.2.3",
|
|
121
|
-
"components": {
|
|
122
|
-
"storage": {
|
|
123
|
-
"status": "healthy",
|
|
124
|
-
"latency": 15,
|
|
125
|
-
"message": "Storage backend configured",
|
|
126
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
127
|
-
},
|
|
128
|
-
"kvStore": {
|
|
129
|
-
"status": "healthy",
|
|
130
|
-
"latency": 5,
|
|
131
|
-
"message": "KV store configured",
|
|
132
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
133
|
-
},
|
|
134
|
-
"eventBroadcaster": {
|
|
135
|
-
"status": "healthy",
|
|
136
|
-
"latency": 2,
|
|
137
|
-
"message": "Event broadcaster configured",
|
|
138
|
-
"lastCheck": "2024-01-15T10:30:00.000Z"
|
|
139
|
-
},
|
|
140
|
-
"circuitBreaker": {
|
|
141
|
-
"status": "degraded",
|
|
142
|
-
"openCircuits": 1,
|
|
143
|
-
"totalCircuits": 5,
|
|
144
|
-
"circuits": [
|
|
145
|
-
{
|
|
146
|
-
"nodeType": "image-resize",
|
|
147
|
-
"state": "open",
|
|
148
|
-
"failureCount": 10,
|
|
149
|
-
"timeSinceLastStateChange": 30000
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
|
-
"deadLetterQueue": {
|
|
154
|
-
"status": "healthy",
|
|
155
|
-
"pendingItems": 3,
|
|
156
|
-
"exhaustedItems": 0,
|
|
157
|
-
"oldestItem": "2024-01-15T10:25:00.000Z"
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Configuration
|
|
164
|
-
|
|
165
|
-
Configure health check behavior in your server configuration:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
import { createUploadistaServer } from "@uploadista/server";
|
|
169
|
-
|
|
170
|
-
const server = await createUploadistaServer({
|
|
171
|
-
// ... other config ...
|
|
172
|
-
|
|
173
|
-
healthCheck: {
|
|
174
|
-
// Timeout for dependency checks (default: 5000ms)
|
|
175
|
-
timeout: 3000,
|
|
176
|
-
|
|
177
|
-
// Enable/disable specific checks
|
|
178
|
-
checkStorage: true,
|
|
179
|
-
checkKvStore: true,
|
|
180
|
-
checkEventBroadcaster: true,
|
|
181
|
-
|
|
182
|
-
// Optional version string for deployment identification
|
|
183
|
-
version: "1.2.3"
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Kubernetes Deployment Example
|
|
189
|
-
|
|
190
|
-
```yaml
|
|
191
|
-
apiVersion: apps/v1
|
|
192
|
-
kind: Deployment
|
|
193
|
-
metadata:
|
|
194
|
-
name: uploadista-server
|
|
195
|
-
spec:
|
|
196
|
-
template:
|
|
197
|
-
spec:
|
|
198
|
-
containers:
|
|
199
|
-
- name: uploadista
|
|
200
|
-
image: your-image:latest
|
|
201
|
-
ports:
|
|
202
|
-
- containerPort: 8080
|
|
203
|
-
livenessProbe:
|
|
204
|
-
httpGet:
|
|
205
|
-
path: /uploadista/health
|
|
206
|
-
port: 8080
|
|
207
|
-
initialDelaySeconds: 5
|
|
208
|
-
periodSeconds: 10
|
|
209
|
-
timeoutSeconds: 1
|
|
210
|
-
failureThreshold: 3
|
|
211
|
-
readinessProbe:
|
|
212
|
-
httpGet:
|
|
213
|
-
path: /uploadista/ready
|
|
214
|
-
port: 8080
|
|
215
|
-
initialDelaySeconds: 10
|
|
216
|
-
periodSeconds: 5
|
|
217
|
-
timeoutSeconds: 5
|
|
218
|
-
failureThreshold: 3
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Health Status Values
|
|
222
|
-
|
|
223
|
-
| Status | Description |
|
|
224
|
-
|--------|-------------|
|
|
225
|
-
| `healthy` | All checks passed, system is fully operational |
|
|
226
|
-
| `degraded` | Some non-critical issues detected (e.g., open circuits, DLQ items), but system is functional |
|
|
227
|
-
| `unhealthy` | Critical components unavailable, system cannot serve requests |
|
|
228
|
-
|
|
229
|
-
## Response Formats
|
|
230
|
-
|
|
231
|
-
Health endpoints support content negotiation via the `Accept` header:
|
|
232
|
-
|
|
233
|
-
| Accept Header | Response Format |
|
|
234
|
-
|--------------|-----------------|
|
|
235
|
-
| `application/json` (default) | Full JSON response with status and details |
|
|
236
|
-
| `text/plain` | Simple text: "OK" or "Service Unavailable" |
|
|
237
|
-
|
|
238
|
-
## Integration with Existing Monitoring
|
|
239
|
-
|
|
240
|
-
### Circuit Breaker Integration
|
|
241
|
-
|
|
242
|
-
When circuit breakers are enabled, the `/health/components` endpoint includes:
|
|
243
|
-
- Count of open circuits
|
|
244
|
-
- Total number of circuits
|
|
245
|
-
- Individual circuit states and failure counts
|
|
246
|
-
|
|
247
|
-
Status becomes `degraded` when any circuit is open.
|
|
248
|
-
|
|
249
|
-
### Dead Letter Queue Integration
|
|
250
|
-
|
|
251
|
-
When DLQ is enabled, the `/health/components` endpoint includes:
|
|
252
|
-
- Count of pending items awaiting retry
|
|
253
|
-
- Count of exhausted items (exceeded max retries)
|
|
254
|
-
- Timestamp of oldest item in queue
|
|
255
|
-
|
|
256
|
-
Status becomes `degraded` when there are exhausted items.
|
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Health Check Service for Uploadista SDK.
|
|
3
|
-
*
|
|
4
|
-
* This module provides health checking functionality for:
|
|
5
|
-
* - Storage backends
|
|
6
|
-
* - KV stores
|
|
7
|
-
* - Event broadcasters
|
|
8
|
-
* - Circuit breaker state
|
|
9
|
-
* - Dead letter queue state
|
|
10
|
-
*
|
|
11
|
-
* @module core/health-check-service
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { DeadLetterQueueService } from "@uploadista/core/flow";
|
|
15
|
-
import type {
|
|
16
|
-
CircuitBreakerHealthSummary,
|
|
17
|
-
ComponentHealth,
|
|
18
|
-
DlqHealthSummary,
|
|
19
|
-
HealthCheckConfig,
|
|
20
|
-
HealthComponents,
|
|
21
|
-
HealthResponse,
|
|
22
|
-
HealthStatus,
|
|
23
|
-
} from "@uploadista/core/types";
|
|
24
|
-
import {
|
|
25
|
-
CircuitBreakerStoreService,
|
|
26
|
-
DEFAULT_HEALTH_CHECK_CONFIG,
|
|
27
|
-
} from "@uploadista/core/types";
|
|
28
|
-
import { Effect, Option } from "effect";
|
|
29
|
-
|
|
30
|
-
// Track server start time for uptime calculation
|
|
31
|
-
const serverStartTime = Date.now();
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Gets the server uptime in milliseconds.
|
|
35
|
-
*/
|
|
36
|
-
export function getServerUptime(): number {
|
|
37
|
-
return Date.now() - serverStartTime;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Creates a timestamp string in ISO 8601 format.
|
|
42
|
-
*/
|
|
43
|
-
export function getTimestamp(): string {
|
|
44
|
-
return new Date().toISOString();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Creates a simple liveness health response.
|
|
49
|
-
*
|
|
50
|
-
* This is used for the `/health` endpoint which should return immediately
|
|
51
|
-
* without checking any dependencies.
|
|
52
|
-
*/
|
|
53
|
-
export function createLivenessResponse(
|
|
54
|
-
config?: HealthCheckConfig,
|
|
55
|
-
): HealthResponse {
|
|
56
|
-
return {
|
|
57
|
-
status: "healthy",
|
|
58
|
-
timestamp: getTimestamp(),
|
|
59
|
-
version: config?.version,
|
|
60
|
-
uptime: getServerUptime(),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Aggregates component health statuses into an overall status.
|
|
66
|
-
*
|
|
67
|
-
* @param components - The components to aggregate
|
|
68
|
-
* @returns The overall health status
|
|
69
|
-
*/
|
|
70
|
-
export function aggregateHealthStatus(
|
|
71
|
-
components: HealthComponents,
|
|
72
|
-
): HealthStatus {
|
|
73
|
-
const statuses: HealthStatus[] = [];
|
|
74
|
-
|
|
75
|
-
// Core components (critical for readiness)
|
|
76
|
-
if (components.storage) statuses.push(components.storage.status);
|
|
77
|
-
if (components.kvStore) statuses.push(components.kvStore.status);
|
|
78
|
-
|
|
79
|
-
// Optional components (don't fail readiness)
|
|
80
|
-
// eventBroadcaster, circuitBreaker, and DLQ being degraded doesn't make system unhealthy
|
|
81
|
-
|
|
82
|
-
// If any critical component is unhealthy, overall is unhealthy
|
|
83
|
-
if (statuses.includes("unhealthy")) {
|
|
84
|
-
return "unhealthy";
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Check if any component (including optional) is degraded
|
|
88
|
-
const allStatuses: HealthStatus[] = [...statuses];
|
|
89
|
-
if (components.eventBroadcaster)
|
|
90
|
-
allStatuses.push(components.eventBroadcaster.status);
|
|
91
|
-
if (components.circuitBreaker)
|
|
92
|
-
allStatuses.push(components.circuitBreaker.status);
|
|
93
|
-
if (components.deadLetterQueue)
|
|
94
|
-
allStatuses.push(components.deadLetterQueue.status);
|
|
95
|
-
|
|
96
|
-
if (allStatuses.includes("degraded")) {
|
|
97
|
-
return "degraded";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return "healthy";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Checks storage backend health by performing a simple operation.
|
|
105
|
-
*
|
|
106
|
-
* Currently returns healthy as we don't have direct access to storage
|
|
107
|
-
* in health check context. This can be enhanced to do actual connectivity
|
|
108
|
-
* checks when storage service is available.
|
|
109
|
-
*/
|
|
110
|
-
export function checkStorageHealth(
|
|
111
|
-
_config: HealthCheckConfig,
|
|
112
|
-
): Effect.Effect<ComponentHealth, never, never> {
|
|
113
|
-
const startTime = Date.now();
|
|
114
|
-
|
|
115
|
-
// TODO: When storage service is available in health context,
|
|
116
|
-
// perform actual health check (e.g., list bucket, check credentials)
|
|
117
|
-
// For now, return healthy with a note
|
|
118
|
-
return Effect.succeed({
|
|
119
|
-
status: "healthy" as HealthStatus,
|
|
120
|
-
latency: Date.now() - startTime,
|
|
121
|
-
message: "Storage backend configured",
|
|
122
|
-
lastCheck: getTimestamp(),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Checks KV store health by performing a simple operation.
|
|
128
|
-
*
|
|
129
|
-
* Currently returns healthy as we don't have direct access to KV store
|
|
130
|
-
* in health check context. This can be enhanced to do actual connectivity
|
|
131
|
-
* checks when KV store service is available.
|
|
132
|
-
*/
|
|
133
|
-
export function checkKvStoreHealth(
|
|
134
|
-
_config: HealthCheckConfig,
|
|
135
|
-
): Effect.Effect<ComponentHealth, never, never> {
|
|
136
|
-
const startTime = Date.now();
|
|
137
|
-
|
|
138
|
-
// TODO: When KV store service is available in health context,
|
|
139
|
-
// perform actual health check (e.g., get/set test key)
|
|
140
|
-
// For now, return healthy with a note
|
|
141
|
-
return Effect.succeed({
|
|
142
|
-
status: "healthy" as HealthStatus,
|
|
143
|
-
latency: Date.now() - startTime,
|
|
144
|
-
message: "KV store configured",
|
|
145
|
-
lastCheck: getTimestamp(),
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Checks event broadcaster health.
|
|
151
|
-
*
|
|
152
|
-
* Currently returns healthy as we don't have direct access to event broadcaster
|
|
153
|
-
* in health check context.
|
|
154
|
-
*/
|
|
155
|
-
export function checkEventBroadcasterHealth(
|
|
156
|
-
_config: HealthCheckConfig,
|
|
157
|
-
): Effect.Effect<ComponentHealth, never, never> {
|
|
158
|
-
const startTime = Date.now();
|
|
159
|
-
|
|
160
|
-
// TODO: When event broadcaster service is available in health context,
|
|
161
|
-
// perform actual health check
|
|
162
|
-
return Effect.succeed({
|
|
163
|
-
status: "healthy" as HealthStatus,
|
|
164
|
-
latency: Date.now() - startTime,
|
|
165
|
-
message: "Event broadcaster configured",
|
|
166
|
-
lastCheck: getTimestamp(),
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Gets circuit breaker health summary from the circuit breaker store.
|
|
172
|
-
*
|
|
173
|
-
* Uses the optional service pattern to check if circuit breaker is available.
|
|
174
|
-
*/
|
|
175
|
-
export function getCircuitBreakerSummary(): Effect.Effect<
|
|
176
|
-
CircuitBreakerHealthSummary | undefined,
|
|
177
|
-
never,
|
|
178
|
-
never
|
|
179
|
-
> {
|
|
180
|
-
return Effect.gen(function* () {
|
|
181
|
-
const cbStoreOption = yield* Effect.serviceOption(
|
|
182
|
-
CircuitBreakerStoreService,
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
if (Option.isNone(cbStoreOption)) {
|
|
186
|
-
// Circuit breaker not configured
|
|
187
|
-
return undefined;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const cbStore = cbStoreOption.value;
|
|
191
|
-
const statsResult = yield* Effect.either(cbStore.getAllStats());
|
|
192
|
-
|
|
193
|
-
if (statsResult._tag === "Left") {
|
|
194
|
-
// Error getting stats - return degraded status
|
|
195
|
-
return {
|
|
196
|
-
status: "degraded" as HealthStatus,
|
|
197
|
-
openCircuits: 0,
|
|
198
|
-
totalCircuits: 0,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const stats = statsResult.right;
|
|
203
|
-
const circuits = Array.from(stats.values());
|
|
204
|
-
const openCircuits = circuits.filter((c) => c.state === "open").length;
|
|
205
|
-
const totalCircuits = circuits.length;
|
|
206
|
-
|
|
207
|
-
// Determine status based on open circuits
|
|
208
|
-
let status: HealthStatus = "healthy";
|
|
209
|
-
if (openCircuits > 0) {
|
|
210
|
-
status = "degraded";
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return {
|
|
214
|
-
status,
|
|
215
|
-
openCircuits,
|
|
216
|
-
totalCircuits,
|
|
217
|
-
circuits: circuits.map((c) => ({
|
|
218
|
-
nodeType: c.nodeType,
|
|
219
|
-
state: c.state,
|
|
220
|
-
failureCount: c.failureCount,
|
|
221
|
-
timeSinceLastStateChange: c.timeSinceLastStateChange,
|
|
222
|
-
})),
|
|
223
|
-
};
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Gets dead letter queue health summary from the DLQ service.
|
|
229
|
-
*
|
|
230
|
-
* Uses the optional service pattern to check if DLQ is available.
|
|
231
|
-
*/
|
|
232
|
-
export function getDlqSummary(): Effect.Effect<
|
|
233
|
-
DlqHealthSummary | undefined,
|
|
234
|
-
never,
|
|
235
|
-
never
|
|
236
|
-
> {
|
|
237
|
-
return Effect.gen(function* () {
|
|
238
|
-
const dlqOption = yield* DeadLetterQueueService.optional;
|
|
239
|
-
|
|
240
|
-
if (Option.isNone(dlqOption)) {
|
|
241
|
-
// DLQ not configured
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const dlq = dlqOption.value;
|
|
246
|
-
const statsResult = yield* Effect.either(dlq.getStats());
|
|
247
|
-
|
|
248
|
-
if (statsResult._tag === "Left") {
|
|
249
|
-
// Error getting stats - return degraded status
|
|
250
|
-
return {
|
|
251
|
-
status: "degraded" as HealthStatus,
|
|
252
|
-
pendingItems: 0,
|
|
253
|
-
exhaustedItems: 0,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const stats = statsResult.right;
|
|
258
|
-
|
|
259
|
-
// Determine status based on exhausted items
|
|
260
|
-
let status: HealthStatus = "healthy";
|
|
261
|
-
if (stats.byStatus.exhausted > 0) {
|
|
262
|
-
status = "degraded";
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
status,
|
|
267
|
-
pendingItems: stats.byStatus.pending,
|
|
268
|
-
exhaustedItems: stats.byStatus.exhausted,
|
|
269
|
-
oldestItem: stats.oldestItem?.toISOString(),
|
|
270
|
-
};
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Performs a full readiness check including all configured dependencies.
|
|
276
|
-
*
|
|
277
|
-
* @param config - Health check configuration
|
|
278
|
-
* @returns Health response with component details
|
|
279
|
-
*/
|
|
280
|
-
export function performReadinessCheck(
|
|
281
|
-
config: HealthCheckConfig = {},
|
|
282
|
-
): Effect.Effect<HealthResponse, never, never> {
|
|
283
|
-
const effectiveConfig = { ...DEFAULT_HEALTH_CHECK_CONFIG, ...config };
|
|
284
|
-
|
|
285
|
-
return Effect.gen(function* () {
|
|
286
|
-
const components: HealthComponents = {};
|
|
287
|
-
|
|
288
|
-
// Check storage if enabled
|
|
289
|
-
if (effectiveConfig.checkStorage) {
|
|
290
|
-
components.storage = yield* checkStorageHealth(effectiveConfig);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Check KV store if enabled
|
|
294
|
-
if (effectiveConfig.checkKvStore) {
|
|
295
|
-
components.kvStore = yield* checkKvStoreHealth(effectiveConfig);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Check event broadcaster if enabled
|
|
299
|
-
if (effectiveConfig.checkEventBroadcaster) {
|
|
300
|
-
components.eventBroadcaster =
|
|
301
|
-
yield* checkEventBroadcasterHealth(effectiveConfig);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Aggregate status
|
|
305
|
-
const status = aggregateHealthStatus(components);
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
status,
|
|
309
|
-
timestamp: getTimestamp(),
|
|
310
|
-
version: config.version,
|
|
311
|
-
uptime: getServerUptime(),
|
|
312
|
-
components,
|
|
313
|
-
};
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Performs a full component health check including circuit breaker and DLQ.
|
|
319
|
-
*
|
|
320
|
-
* @param config - Health check configuration
|
|
321
|
-
* @returns Health response with all component details
|
|
322
|
-
*/
|
|
323
|
-
export function performComponentsCheck(
|
|
324
|
-
config: HealthCheckConfig = {},
|
|
325
|
-
): Effect.Effect<HealthResponse, never, never> {
|
|
326
|
-
const effectiveConfig = { ...DEFAULT_HEALTH_CHECK_CONFIG, ...config };
|
|
327
|
-
|
|
328
|
-
return Effect.gen(function* () {
|
|
329
|
-
const components: HealthComponents = {};
|
|
330
|
-
|
|
331
|
-
// Check core components
|
|
332
|
-
if (effectiveConfig.checkStorage) {
|
|
333
|
-
components.storage = yield* checkStorageHealth(effectiveConfig);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (effectiveConfig.checkKvStore) {
|
|
337
|
-
components.kvStore = yield* checkKvStoreHealth(effectiveConfig);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (effectiveConfig.checkEventBroadcaster) {
|
|
341
|
-
components.eventBroadcaster =
|
|
342
|
-
yield* checkEventBroadcasterHealth(effectiveConfig);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Check optional components (circuit breaker and DLQ)
|
|
346
|
-
const circuitBreakerSummary = yield* getCircuitBreakerSummary();
|
|
347
|
-
if (circuitBreakerSummary) {
|
|
348
|
-
components.circuitBreaker = circuitBreakerSummary;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const dlqSummary = yield* getDlqSummary();
|
|
352
|
-
if (dlqSummary) {
|
|
353
|
-
components.deadLetterQueue = dlqSummary;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Aggregate status
|
|
357
|
-
const status = aggregateHealthStatus(components);
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
status,
|
|
361
|
-
timestamp: getTimestamp(),
|
|
362
|
-
version: config.version,
|
|
363
|
-
uptime: getServerUptime(),
|
|
364
|
-
components,
|
|
365
|
-
};
|
|
366
|
-
});
|
|
367
|
-
}
|