@naman_deep_singh/server-utils 1.0.6 → 1.0.8
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/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/periodic-health.d.ts +11 -0
- package/dist/periodic-health.js +68 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +22 -1
- package/dist/types.d.ts +11 -0
- package/package.json +1 -1
- package/src/index.ts +30 -21
- package/src/periodic-health.ts +83 -0
- package/src/server.ts +31 -2
- package/src/types.ts +13 -0
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,14 @@ export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
|
6
6
|
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
7
7
|
export { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth, type ValidationRule, type RateLimitConfig, type AuthConfig } from './middleware';
|
|
8
8
|
export { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
9
|
-
export
|
|
9
|
+
export { PeriodicHealthMonitor } from './periodic-health';
|
|
10
|
+
export type { ServerConfig, HealthCheckConfig, HealthCheck, GracefulShutdownConfig, ServerPlugin, SocketIOConfig, SocketInstance, PeriodicHealthCheckConfig, HealthCheckService } from './types';
|
|
10
11
|
import { ExpressServer, createServer } from './server';
|
|
11
12
|
import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
12
13
|
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
13
14
|
import { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth } from './middleware';
|
|
14
15
|
import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
16
|
+
import { PeriodicHealthMonitor } from './periodic-health';
|
|
15
17
|
declare const ServerUtils: {
|
|
16
18
|
createServer: typeof createServer;
|
|
17
19
|
ExpressServer: typeof ExpressServer;
|
|
@@ -39,5 +41,6 @@ declare const ServerUtils: {
|
|
|
39
41
|
getEnv: typeof getEnv;
|
|
40
42
|
getEnvNumber: typeof getEnvNumber;
|
|
41
43
|
getEnvBoolean: typeof getEnvBoolean;
|
|
44
|
+
PeriodicHealthMonitor: typeof PeriodicHealthMonitor;
|
|
42
45
|
};
|
|
43
46
|
export default ServerUtils;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getEnvBoolean = exports.getEnvNumber = exports.getEnv = exports.requireAuth = exports.rateLimit = exports.validateFields = exports.withAuth = exports.withRateLimit = exports.withValidation = exports.withRequestId = exports.withErrorHandler = exports.withLogging = exports.createAuthMiddleware = exports.createRateLimitMiddleware = exports.createValidationMiddleware = exports.createRequestIdMiddleware = exports.createErrorHandler = exports.createLoggingMiddleware = exports.startServerWithShutdown = exports.withGracefulShutdown = exports.createGracefulShutdown = exports.addHealthCheck = exports.withHealthCheck = exports.createHealthCheck = exports.Router = exports.createServer = exports.ExpressServer = void 0;
|
|
3
|
+
exports.PeriodicHealthMonitor = exports.getEnvBoolean = exports.getEnvNumber = exports.getEnv = exports.requireAuth = exports.rateLimit = exports.validateFields = exports.withAuth = exports.withRateLimit = exports.withValidation = exports.withRequestId = exports.withErrorHandler = exports.withLogging = exports.createAuthMiddleware = exports.createRateLimitMiddleware = exports.createValidationMiddleware = exports.createRequestIdMiddleware = exports.createErrorHandler = exports.createLoggingMiddleware = exports.startServerWithShutdown = exports.withGracefulShutdown = exports.createGracefulShutdown = exports.addHealthCheck = exports.withHealthCheck = exports.createHealthCheck = exports.Router = exports.createServer = exports.ExpressServer = void 0;
|
|
4
4
|
// Core server utilities
|
|
5
5
|
var server_1 = require("./server");
|
|
6
6
|
Object.defineProperty(exports, "ExpressServer", { enumerable: true, get: function () { return server_1.ExpressServer; } });
|
|
@@ -40,12 +40,16 @@ var utils_1 = require("./utils");
|
|
|
40
40
|
Object.defineProperty(exports, "getEnv", { enumerable: true, get: function () { return utils_1.getEnv; } });
|
|
41
41
|
Object.defineProperty(exports, "getEnvNumber", { enumerable: true, get: function () { return utils_1.getEnvNumber; } });
|
|
42
42
|
Object.defineProperty(exports, "getEnvBoolean", { enumerable: true, get: function () { return utils_1.getEnvBoolean; } });
|
|
43
|
+
// Periodic health monitoring
|
|
44
|
+
var periodic_health_1 = require("./periodic-health");
|
|
45
|
+
Object.defineProperty(exports, "PeriodicHealthMonitor", { enumerable: true, get: function () { return periodic_health_1.PeriodicHealthMonitor; } });
|
|
43
46
|
// Import all exports for default export
|
|
44
47
|
const server_2 = require("./server");
|
|
45
48
|
const health_2 = require("./health");
|
|
46
49
|
const shutdown_2 = require("./shutdown");
|
|
47
50
|
const middleware_2 = require("./middleware");
|
|
48
51
|
const utils_2 = require("./utils");
|
|
52
|
+
const periodic_health_2 = require("./periodic-health");
|
|
49
53
|
// Default export for namespace usage
|
|
50
54
|
const ServerUtils = {
|
|
51
55
|
// Server creation
|
|
@@ -78,6 +82,8 @@ const ServerUtils = {
|
|
|
78
82
|
// Utils
|
|
79
83
|
getEnv: utils_2.getEnv,
|
|
80
84
|
getEnvNumber: utils_2.getEnvNumber,
|
|
81
|
-
getEnvBoolean: utils_2.getEnvBoolean
|
|
85
|
+
getEnvBoolean: utils_2.getEnvBoolean,
|
|
86
|
+
// Periodic Health Monitoring
|
|
87
|
+
PeriodicHealthMonitor: periodic_health_2.PeriodicHealthMonitor,
|
|
82
88
|
};
|
|
83
89
|
exports.default = ServerUtils;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PeriodicHealthCheckConfig } from './types';
|
|
2
|
+
export declare class PeriodicHealthMonitor {
|
|
3
|
+
private intervals;
|
|
4
|
+
private config;
|
|
5
|
+
private serviceName;
|
|
6
|
+
constructor(config: PeriodicHealthCheckConfig, serviceName: string);
|
|
7
|
+
start(): void;
|
|
8
|
+
stop(): void;
|
|
9
|
+
private checkServiceHealth;
|
|
10
|
+
getHealthStatus(): Promise<Record<string, boolean>>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PeriodicHealthMonitor = void 0;
|
|
4
|
+
class PeriodicHealthMonitor {
|
|
5
|
+
constructor(config, serviceName) {
|
|
6
|
+
this.intervals = [];
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.serviceName = serviceName;
|
|
9
|
+
}
|
|
10
|
+
start() {
|
|
11
|
+
if (!this.config.enabled || !this.config.services?.length) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const interval = this.config.interval || 30000;
|
|
15
|
+
this.config.services.forEach(service => {
|
|
16
|
+
const intervalId = setInterval(async () => {
|
|
17
|
+
await this.checkServiceHealth(service);
|
|
18
|
+
}, interval);
|
|
19
|
+
this.intervals.push(intervalId);
|
|
20
|
+
});
|
|
21
|
+
console.log(`📊 ${this.serviceName}: Periodic health monitoring enabled (${interval}ms interval) for ${this.config.services.length} service(s)`);
|
|
22
|
+
}
|
|
23
|
+
stop() {
|
|
24
|
+
this.intervals.forEach(interval => clearInterval(interval));
|
|
25
|
+
this.intervals = [];
|
|
26
|
+
console.log(`🛑 ${this.serviceName}: Periodic health monitoring stopped`);
|
|
27
|
+
}
|
|
28
|
+
async checkServiceHealth(service) {
|
|
29
|
+
try {
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeout = service.timeout || 5000;
|
|
32
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
33
|
+
const response = await fetch(service.url, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
headers: {
|
|
37
|
+
'User-Agent': `${this.serviceName}-health-monitor`
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
clearTimeout(timeoutId);
|
|
41
|
+
if (response.ok) {
|
|
42
|
+
console.log(`🟢 ${service.name} is healthy`);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(`🔴 ${service.name} returned ${response.status}`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
52
|
+
console.log(`🔴 ${service.name} health check failed: ${errorMessage}`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Get current health status of all services
|
|
57
|
+
async getHealthStatus() {
|
|
58
|
+
if (!this.config.services?.length) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
const results = {};
|
|
62
|
+
await Promise.all(this.config.services.map(async (service) => {
|
|
63
|
+
results[service.name] = await this.checkServiceHealth(service);
|
|
64
|
+
}));
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.PeriodicHealthMonitor = PeriodicHealthMonitor;
|
package/dist/server.d.ts
CHANGED
|
@@ -54,8 +54,10 @@ export declare class ExpressServer implements ServerInstance {
|
|
|
54
54
|
private grpcServer?;
|
|
55
55
|
private rpcMethods;
|
|
56
56
|
private socketIO?;
|
|
57
|
+
private healthMonitor?;
|
|
57
58
|
constructor(name?: string, version?: string, config?: ServerConfig);
|
|
58
59
|
private setupMiddleware;
|
|
60
|
+
private setupPeriodicHealthMonitoring;
|
|
59
61
|
start(): Promise<ServerInstance>;
|
|
60
62
|
stop(): Promise<void>;
|
|
61
63
|
getInfo(): ServerInfo;
|
package/dist/server.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.ExpressServer = void 0;
|
|
|
7
7
|
exports.createServer = createServer;
|
|
8
8
|
const express_1 = __importDefault(require("express"));
|
|
9
9
|
const shutdown_1 = require("./shutdown");
|
|
10
|
+
const periodic_health_1 = require("./periodic-health");
|
|
10
11
|
const crypto_1 = __importDefault(require("crypto"));
|
|
11
12
|
class ExpressServer {
|
|
12
13
|
constructor(name = 'Express Server', version = '1.0.0', config = {}) {
|
|
@@ -26,10 +27,13 @@ class ExpressServer {
|
|
|
26
27
|
customMiddleware: config.customMiddleware || [],
|
|
27
28
|
healthCheck: config.healthCheck ?? true,
|
|
28
29
|
gracefulShutdown: config.gracefulShutdown ?? true,
|
|
29
|
-
socketIO: config.socketIO
|
|
30
|
+
socketIO: config.socketIO,
|
|
31
|
+
periodicHealthCheck: config.periodicHealthCheck || { enabled: false }
|
|
30
32
|
};
|
|
31
33
|
// Apply middleware based on configuration
|
|
32
34
|
this.setupMiddleware();
|
|
35
|
+
// Setup periodic health monitoring
|
|
36
|
+
this.setupPeriodicHealthMonitoring();
|
|
33
37
|
}
|
|
34
38
|
setupMiddleware() {
|
|
35
39
|
// Apply CORS if enabled
|
|
@@ -87,6 +91,11 @@ class ExpressServer {
|
|
|
87
91
|
});
|
|
88
92
|
}
|
|
89
93
|
}
|
|
94
|
+
setupPeriodicHealthMonitoring() {
|
|
95
|
+
if (this.config.periodicHealthCheck?.enabled) {
|
|
96
|
+
this.healthMonitor = new periodic_health_1.PeriodicHealthMonitor(this.config.periodicHealthCheck, this.config.name);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
90
99
|
async start() {
|
|
91
100
|
this.status = 'starting';
|
|
92
101
|
return new Promise((resolve, reject) => {
|
|
@@ -98,9 +107,17 @@ class ExpressServer {
|
|
|
98
107
|
(0, shutdown_1.createGracefulShutdown)(this.server, {
|
|
99
108
|
onShutdown: async () => {
|
|
100
109
|
this.status = 'stopping';
|
|
110
|
+
// Stop health monitoring during shutdown
|
|
111
|
+
if (this.healthMonitor) {
|
|
112
|
+
this.healthMonitor.stop();
|
|
113
|
+
}
|
|
101
114
|
}
|
|
102
115
|
});
|
|
103
116
|
}
|
|
117
|
+
// Start periodic health monitoring after server is running
|
|
118
|
+
if (this.healthMonitor) {
|
|
119
|
+
this.healthMonitor.start();
|
|
120
|
+
}
|
|
104
121
|
resolve(this);
|
|
105
122
|
});
|
|
106
123
|
this.server.on('error', reject);
|
|
@@ -117,6 +134,10 @@ class ExpressServer {
|
|
|
117
134
|
if (this.grpcServer) {
|
|
118
135
|
this.grpcServer.forceShutdown();
|
|
119
136
|
}
|
|
137
|
+
// Stop periodic health monitoring
|
|
138
|
+
if (this.healthMonitor) {
|
|
139
|
+
this.healthMonitor.stop();
|
|
140
|
+
}
|
|
120
141
|
// Stop Socket.IO server if running
|
|
121
142
|
if (this.socketIO) {
|
|
122
143
|
this.socketIO.close();
|
package/dist/types.d.ts
CHANGED
|
@@ -19,9 +19,20 @@ export interface ServerConfig {
|
|
|
19
19
|
healthCheck?: boolean | string;
|
|
20
20
|
gracefulShutdown?: boolean;
|
|
21
21
|
socketIO?: SocketIOConfig;
|
|
22
|
+
periodicHealthCheck?: PeriodicHealthCheckConfig;
|
|
22
23
|
name?: string;
|
|
23
24
|
version?: string;
|
|
24
25
|
}
|
|
26
|
+
export interface PeriodicHealthCheckConfig {
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
interval?: number;
|
|
29
|
+
services?: HealthCheckService[];
|
|
30
|
+
}
|
|
31
|
+
export interface HealthCheckService {
|
|
32
|
+
name: string;
|
|
33
|
+
url: string;
|
|
34
|
+
timeout?: number;
|
|
35
|
+
}
|
|
25
36
|
export interface SocketIOConfig {
|
|
26
37
|
enabled?: boolean;
|
|
27
38
|
cors?: boolean | {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -13,15 +13,15 @@ export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
|
13
13
|
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
14
14
|
|
|
15
15
|
// Middleware utilities
|
|
16
|
-
export {
|
|
17
|
-
createLoggingMiddleware,
|
|
18
|
-
createErrorHandler,
|
|
16
|
+
export {
|
|
17
|
+
createLoggingMiddleware,
|
|
18
|
+
createErrorHandler,
|
|
19
19
|
createRequestIdMiddleware,
|
|
20
20
|
createValidationMiddleware,
|
|
21
21
|
createRateLimitMiddleware,
|
|
22
22
|
createAuthMiddleware,
|
|
23
|
-
withLogging,
|
|
24
|
-
withErrorHandler,
|
|
23
|
+
withLogging,
|
|
24
|
+
withErrorHandler,
|
|
25
25
|
withRequestId,
|
|
26
26
|
withValidation,
|
|
27
27
|
withRateLimit,
|
|
@@ -41,30 +41,35 @@ export {
|
|
|
41
41
|
getEnvBoolean
|
|
42
42
|
} from './utils';
|
|
43
43
|
|
|
44
|
+
// Periodic health monitoring
|
|
45
|
+
export { PeriodicHealthMonitor } from './periodic-health';
|
|
46
|
+
|
|
44
47
|
// Types
|
|
45
|
-
export type {
|
|
46
|
-
ServerConfig,
|
|
47
|
-
HealthCheckConfig,
|
|
48
|
-
HealthCheck,
|
|
49
|
-
GracefulShutdownConfig,
|
|
48
|
+
export type {
|
|
49
|
+
ServerConfig,
|
|
50
|
+
HealthCheckConfig,
|
|
51
|
+
HealthCheck,
|
|
52
|
+
GracefulShutdownConfig,
|
|
50
53
|
ServerPlugin,
|
|
51
54
|
SocketIOConfig,
|
|
52
|
-
SocketInstance
|
|
55
|
+
SocketInstance,
|
|
56
|
+
PeriodicHealthCheckConfig,
|
|
57
|
+
HealthCheckService
|
|
53
58
|
} from './types';
|
|
54
59
|
|
|
55
60
|
// Import all exports for default export
|
|
56
61
|
import { ExpressServer, createServer } from './server';
|
|
57
62
|
import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
58
63
|
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
59
|
-
import {
|
|
60
|
-
createLoggingMiddleware,
|
|
61
|
-
createErrorHandler,
|
|
64
|
+
import {
|
|
65
|
+
createLoggingMiddleware,
|
|
66
|
+
createErrorHandler,
|
|
62
67
|
createRequestIdMiddleware,
|
|
63
68
|
createValidationMiddleware,
|
|
64
69
|
createRateLimitMiddleware,
|
|
65
70
|
createAuthMiddleware,
|
|
66
|
-
withLogging,
|
|
67
|
-
withErrorHandler,
|
|
71
|
+
withLogging,
|
|
72
|
+
withErrorHandler,
|
|
68
73
|
withRequestId,
|
|
69
74
|
withValidation,
|
|
70
75
|
withRateLimit,
|
|
@@ -74,23 +79,24 @@ import {
|
|
|
74
79
|
requireAuth
|
|
75
80
|
} from './middleware';
|
|
76
81
|
import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
82
|
+
import { PeriodicHealthMonitor } from './periodic-health';
|
|
77
83
|
|
|
78
84
|
// Default export for namespace usage
|
|
79
85
|
const ServerUtils = {
|
|
80
86
|
// Server creation
|
|
81
87
|
createServer,
|
|
82
88
|
ExpressServer,
|
|
83
|
-
|
|
89
|
+
|
|
84
90
|
// Health checks
|
|
85
91
|
createHealthCheck,
|
|
86
92
|
withHealthCheck,
|
|
87
93
|
addHealthCheck,
|
|
88
|
-
|
|
94
|
+
|
|
89
95
|
// Graceful shutdown
|
|
90
96
|
createGracefulShutdown,
|
|
91
97
|
withGracefulShutdown,
|
|
92
98
|
startServerWithShutdown,
|
|
93
|
-
|
|
99
|
+
|
|
94
100
|
// Middleware
|
|
95
101
|
createLoggingMiddleware,
|
|
96
102
|
createErrorHandler,
|
|
@@ -107,11 +113,14 @@ const ServerUtils = {
|
|
|
107
113
|
validateFields,
|
|
108
114
|
rateLimit,
|
|
109
115
|
requireAuth,
|
|
110
|
-
|
|
116
|
+
|
|
111
117
|
// Utils
|
|
112
118
|
getEnv,
|
|
113
119
|
getEnvNumber,
|
|
114
|
-
getEnvBoolean
|
|
120
|
+
getEnvBoolean,
|
|
121
|
+
|
|
122
|
+
// Periodic Health Monitoring
|
|
123
|
+
PeriodicHealthMonitor,
|
|
115
124
|
};
|
|
116
125
|
|
|
117
126
|
export default ServerUtils;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { PeriodicHealthCheckConfig, HealthCheckService } from './types';
|
|
2
|
+
|
|
3
|
+
export class PeriodicHealthMonitor {
|
|
4
|
+
private intervals: NodeJS.Timeout[] = [];
|
|
5
|
+
private config: PeriodicHealthCheckConfig;
|
|
6
|
+
private serviceName: string;
|
|
7
|
+
|
|
8
|
+
constructor(config: PeriodicHealthCheckConfig, serviceName: string) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.serviceName = serviceName;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
start(): void {
|
|
14
|
+
if (!this.config.enabled || !this.config.services?.length) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const interval = this.config.interval || 30000;
|
|
19
|
+
|
|
20
|
+
this.config.services.forEach(service => {
|
|
21
|
+
const intervalId = setInterval(async () => {
|
|
22
|
+
await this.checkServiceHealth(service);
|
|
23
|
+
}, interval);
|
|
24
|
+
|
|
25
|
+
this.intervals.push(intervalId);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
console.log(`📊 ${this.serviceName}: Periodic health monitoring enabled (${interval}ms interval) for ${this.config.services.length} service(s)`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
stop(): void {
|
|
32
|
+
this.intervals.forEach(interval => clearInterval(interval));
|
|
33
|
+
this.intervals = [];
|
|
34
|
+
console.log(`🛑 ${this.serviceName}: Periodic health monitoring stopped`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private async checkServiceHealth(service: HealthCheckService): Promise<boolean> {
|
|
38
|
+
try {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timeout = service.timeout || 5000;
|
|
41
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
42
|
+
|
|
43
|
+
const response = await fetch(service.url, {
|
|
44
|
+
method: 'GET',
|
|
45
|
+
signal: controller.signal,
|
|
46
|
+
headers: {
|
|
47
|
+
'User-Agent': `${this.serviceName}-health-monitor`
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
|
|
53
|
+
if (response.ok) {
|
|
54
|
+
console.log(`🟢 ${service.name} is healthy`);
|
|
55
|
+
return true;
|
|
56
|
+
} else {
|
|
57
|
+
console.log(`🔴 ${service.name} returned ${response.status}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
62
|
+
console.log(`🔴 ${service.name} health check failed: ${errorMessage}`);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get current health status of all services
|
|
68
|
+
async getHealthStatus(): Promise<Record<string, boolean>> {
|
|
69
|
+
if (!this.config.services?.length) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const results: Record<string, boolean> = {};
|
|
74
|
+
|
|
75
|
+
await Promise.all(
|
|
76
|
+
this.config.services.map(async service => {
|
|
77
|
+
results[service.name] = await this.checkServiceHealth(service);
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -2,6 +2,7 @@ import express from 'express';
|
|
|
2
2
|
import { Server } from 'http';
|
|
3
3
|
import { ServerConfig, SocketIOConfig, SocketInstance } from './types';
|
|
4
4
|
import { createGracefulShutdown } from './shutdown';
|
|
5
|
+
import { PeriodicHealthMonitor } from './periodic-health';
|
|
5
6
|
import crypto from 'crypto';
|
|
6
7
|
|
|
7
8
|
export interface GrpcService {
|
|
@@ -68,6 +69,7 @@ export class ExpressServer implements ServerInstance {
|
|
|
68
69
|
private grpcServer?: GrpcServerInstance;
|
|
69
70
|
private rpcMethods: RpcMethod = {};
|
|
70
71
|
private socketIO?: { close(): void };
|
|
72
|
+
private healthMonitor?: PeriodicHealthMonitor;
|
|
71
73
|
|
|
72
74
|
constructor(
|
|
73
75
|
name: string = 'Express Server',
|
|
@@ -87,11 +89,15 @@ export class ExpressServer implements ServerInstance {
|
|
|
87
89
|
customMiddleware: config.customMiddleware || [],
|
|
88
90
|
healthCheck: config.healthCheck ?? true,
|
|
89
91
|
gracefulShutdown: config.gracefulShutdown ?? true,
|
|
90
|
-
socketIO: config.socketIO
|
|
92
|
+
socketIO: config.socketIO,
|
|
93
|
+
periodicHealthCheck: config.periodicHealthCheck || { enabled: false }
|
|
91
94
|
};
|
|
92
95
|
|
|
93
96
|
// Apply middleware based on configuration
|
|
94
97
|
this.setupMiddleware();
|
|
98
|
+
|
|
99
|
+
// Setup periodic health monitoring
|
|
100
|
+
this.setupPeriodicHealthMonitoring();
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
private setupMiddleware(): void {
|
|
@@ -153,6 +159,15 @@ export class ExpressServer implements ServerInstance {
|
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
private setupPeriodicHealthMonitoring(): void {
|
|
163
|
+
if (this.config.periodicHealthCheck?.enabled) {
|
|
164
|
+
this.healthMonitor = new PeriodicHealthMonitor(
|
|
165
|
+
this.config.periodicHealthCheck,
|
|
166
|
+
this.config.name
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
156
171
|
async start(): Promise<ServerInstance> {
|
|
157
172
|
this.status = 'starting';
|
|
158
173
|
|
|
@@ -166,10 +181,19 @@ export class ExpressServer implements ServerInstance {
|
|
|
166
181
|
createGracefulShutdown(this.server!, {
|
|
167
182
|
onShutdown: async () => {
|
|
168
183
|
this.status = 'stopping';
|
|
184
|
+
// Stop health monitoring during shutdown
|
|
185
|
+
if (this.healthMonitor) {
|
|
186
|
+
this.healthMonitor.stop();
|
|
187
|
+
}
|
|
169
188
|
}
|
|
170
189
|
});
|
|
171
190
|
}
|
|
172
191
|
|
|
192
|
+
// Start periodic health monitoring after server is running
|
|
193
|
+
if (this.healthMonitor) {
|
|
194
|
+
this.healthMonitor.start();
|
|
195
|
+
}
|
|
196
|
+
|
|
173
197
|
resolve(this);
|
|
174
198
|
});
|
|
175
199
|
|
|
@@ -189,6 +213,11 @@ export class ExpressServer implements ServerInstance {
|
|
|
189
213
|
this.grpcServer.forceShutdown();
|
|
190
214
|
}
|
|
191
215
|
|
|
216
|
+
// Stop periodic health monitoring
|
|
217
|
+
if (this.healthMonitor) {
|
|
218
|
+
this.healthMonitor.stop();
|
|
219
|
+
}
|
|
220
|
+
|
|
192
221
|
// Stop Socket.IO server if running
|
|
193
222
|
if (this.socketIO) {
|
|
194
223
|
this.socketIO.close();
|
|
@@ -331,7 +360,7 @@ export class ExpressServer implements ServerInstance {
|
|
|
331
360
|
};
|
|
332
361
|
|
|
333
362
|
// Configure CORS
|
|
334
|
-
const corsConfig = config.cors === true
|
|
363
|
+
const corsConfig = config.cors === true
|
|
335
364
|
? { origin: '*', methods: ['GET', 'POST'] }
|
|
336
365
|
: config.cors || undefined;
|
|
337
366
|
|
package/src/types.ts
CHANGED
|
@@ -21,10 +21,23 @@ export interface ServerConfig {
|
|
|
21
21
|
healthCheck?: boolean | string;
|
|
22
22
|
gracefulShutdown?: boolean;
|
|
23
23
|
socketIO?: SocketIOConfig;
|
|
24
|
+
periodicHealthCheck?: PeriodicHealthCheckConfig;
|
|
24
25
|
name?: string;
|
|
25
26
|
version?: string;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
export interface PeriodicHealthCheckConfig {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
interval?: number;
|
|
32
|
+
services?: HealthCheckService[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface HealthCheckService {
|
|
36
|
+
name: string;
|
|
37
|
+
url: string;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
export interface SocketIOConfig {
|
|
29
42
|
enabled?: boolean;
|
|
30
43
|
cors?: boolean | {
|