+80
src/cache.ts
+80
src/cache.ts
···
602
602
}
603
603
604
604
/**
605
+
* Detailed health check with component status
606
+
* @returns Object with detailed health information
607
+
*/
608
+
async detailedHealthCheck(): Promise<{
609
+
status: "healthy" | "degraded" | "unhealthy";
610
+
checks: {
611
+
database: { status: boolean; latency?: number };
612
+
slackApi: { status: boolean; error?: string };
613
+
queueDepth: number;
614
+
memoryUsage: {
615
+
heapUsed: number;
616
+
heapTotal: number;
617
+
percentage: number;
618
+
};
619
+
};
620
+
uptime: number;
621
+
}> {
622
+
const checks = {
623
+
database: { status: false, latency: 0 },
624
+
slackApi: { status: false },
625
+
queueDepth: this.userUpdateQueue.size,
626
+
memoryUsage: {
627
+
heapUsed: 0,
628
+
heapTotal: 0,
629
+
percentage: 0,
630
+
},
631
+
};
632
+
633
+
// Check database
634
+
try {
635
+
const start = Date.now();
636
+
this.db.query("SELECT 1").get();
637
+
checks.database = { status: true, latency: Date.now() - start };
638
+
} catch (error) {
639
+
console.error("Database health check failed:", error);
640
+
}
641
+
642
+
// Check Slack API if wrapper is available
643
+
if (this.slackWrapper) {
644
+
try {
645
+
await this.slackWrapper.getUserInfo("U062UG485EE"); // Use a known test user
646
+
checks.slackApi = { status: true };
647
+
} catch (error) {
648
+
checks.slackApi = {
649
+
status: false,
650
+
error: error instanceof Error ? error.message : "Unknown error",
651
+
};
652
+
}
653
+
} else {
654
+
checks.slackApi = { status: true }; // No wrapper means not critical
655
+
}
656
+
657
+
// Check memory usage
658
+
const memUsage = process.memoryUsage();
659
+
checks.memoryUsage = {
660
+
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
661
+
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
662
+
percentage: Math.round(
663
+
(memUsage.heapUsed / memUsage.heapTotal) * 100,
664
+
),
665
+
};
666
+
667
+
// Determine overall status
668
+
let status: "healthy" | "degraded" | "unhealthy" = "healthy";
669
+
if (!checks.database.status) {
670
+
status = "unhealthy";
671
+
} else if (!checks.slackApi.status || checks.queueDepth > 100) {
672
+
status = "degraded";
673
+
} else if (checks.memoryUsage.percentage > 90) {
674
+
status = "degraded";
675
+
}
676
+
677
+
return {
678
+
status,
679
+
checks,
680
+
uptime: process.uptime(),
681
+
};
682
+
}
683
+
684
+
/**
605
685
* Sets the Slack wrapper for user updates
606
686
* @param slackWrapper SlackUserProvider instance for API calls
607
687
*/
+11
-1
src/handlers/index.ts
+11
-1
src/handlers/index.ts
···
21
21
}
22
22
23
23
export const handleHealthCheck: RouteHandlerWithAnalytics = async (
24
-
_request,
24
+
request,
25
25
recordAnalytics,
26
26
) => {
27
+
const url = new URL(request.url);
28
+
const detailed = url.searchParams.get("detailed") === "true";
29
+
30
+
if (detailed) {
31
+
const health = await cache.detailedHealthCheck();
32
+
const statusCode = health.status === "unhealthy" ? 503 : health.status === "degraded" ? 200 : 200;
33
+
await recordAnalytics(statusCode);
34
+
return Response.json(health, { status: statusCode });
35
+
}
36
+
27
37
const isHealthy = await cache.healthCheck();
28
38
if (isHealthy) {
29
39
await recordAnalytics(200);
+50
-2
src/routes/api-routes.ts
+50
-2
src/routes/api-routes.ts
···
26
26
withAnalytics("/health", "GET", handlers.handleHealthCheck),
27
27
{
28
28
summary: "Health check",
29
-
description: "Check if the service is healthy and operational",
29
+
description:
30
+
"Check if the service is healthy and operational. Add ?detailed=true for comprehensive health information including Slack API status, queue depth, and memory usage.",
30
31
tags: ["Health"],
32
+
parameters: {
33
+
query: [
34
+
queryParam(
35
+
"detailed",
36
+
"boolean",
37
+
"Return detailed health check information",
38
+
false,
39
+
false,
40
+
),
41
+
],
42
+
},
31
43
responses: Object.fromEntries([
32
44
apiResponse(200, "Service is healthy", {
33
45
type: "object",
34
46
properties: {
35
-
status: { type: "string", example: "healthy" },
47
+
status: {
48
+
type: "string",
49
+
example: "healthy",
50
+
enum: ["healthy", "degraded", "unhealthy"],
51
+
},
36
52
cache: { type: "boolean", example: true },
37
53
uptime: { type: "number", example: 123456 },
54
+
checks: {
55
+
type: "object",
56
+
description: "Detailed checks (only with ?detailed=true)",
57
+
properties: {
58
+
database: {
59
+
type: "object",
60
+
properties: {
61
+
status: { type: "boolean" },
62
+
latency: { type: "number", description: "ms" },
63
+
},
64
+
},
65
+
slackApi: {
66
+
type: "object",
67
+
properties: {
68
+
status: { type: "boolean" },
69
+
error: { type: "string" },
70
+
},
71
+
},
72
+
queueDepth: {
73
+
type: "number",
74
+
description: "Number of users queued for update",
75
+
},
76
+
memoryUsage: {
77
+
type: "object",
78
+
properties: {
79
+
heapUsed: { type: "number", description: "MB" },
80
+
heapTotal: { type: "number", description: "MB" },
81
+
percentage: { type: "number" },
82
+
},
83
+
},
84
+
},
85
+
},
38
86
},
39
87
}),
40
88
apiResponse(503, "Service is unhealthy"),