@adalo/metrics 0.0.0-staging.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.
Files changed (85) hide show
  1. package/.env.example +14 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc +61 -0
  4. package/.github/pull_request_template.md +14 -0
  5. package/.github/workflows/code-style.yml +29 -0
  6. package/.github/workflows/deploy-staging.yml +34 -0
  7. package/.github/workflows/deploy.yml +29 -0
  8. package/.github/workflows/tests.yml +17 -0
  9. package/.idea/codeStyles/Project.xml +101 -0
  10. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  11. package/.idea/git_toolbox_prj.xml +15 -0
  12. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  13. package/.idea/jsLibraryMappings.xml +6 -0
  14. package/.idea/prettier.xml +6 -0
  15. package/.idea/vcs.xml +6 -0
  16. package/.prettierrc +10 -0
  17. package/README-health.md +234 -0
  18. package/README.md +120 -0
  19. package/__tests__/metricsRedisClient.test.js +138 -0
  20. package/babel.config.js +20 -0
  21. package/lib/health/databaseChecker.d.ts +43 -0
  22. package/lib/health/databaseChecker.d.ts.map +1 -0
  23. package/lib/health/databaseChecker.js +189 -0
  24. package/lib/health/databaseChecker.js.map +1 -0
  25. package/lib/health/healthCheckCache.d.ts +59 -0
  26. package/lib/health/healthCheckCache.d.ts.map +1 -0
  27. package/lib/health/healthCheckCache.js +187 -0
  28. package/lib/health/healthCheckCache.js.map +1 -0
  29. package/lib/health/healthCheckClient.d.ts +124 -0
  30. package/lib/health/healthCheckClient.d.ts.map +1 -0
  31. package/lib/health/healthCheckClient.js +324 -0
  32. package/lib/health/healthCheckClient.js.map +1 -0
  33. package/lib/health/healthCheckUtils.d.ts +52 -0
  34. package/lib/health/healthCheckUtils.d.ts.map +1 -0
  35. package/lib/health/healthCheckUtils.js +129 -0
  36. package/lib/health/healthCheckUtils.js.map +1 -0
  37. package/lib/health/healthCheckWorker.d.ts +2 -0
  38. package/lib/health/healthCheckWorker.d.ts.map +1 -0
  39. package/lib/health/healthCheckWorker.js +70 -0
  40. package/lib/health/healthCheckWorker.js.map +1 -0
  41. package/lib/index.d.ts +10 -0
  42. package/lib/index.d.ts.map +1 -0
  43. package/lib/index.js +105 -0
  44. package/lib/index.js.map +1 -0
  45. package/lib/metrics/baseMetricsClient.d.ts +174 -0
  46. package/lib/metrics/baseMetricsClient.d.ts.map +1 -0
  47. package/lib/metrics/baseMetricsClient.js +428 -0
  48. package/lib/metrics/baseMetricsClient.js.map +1 -0
  49. package/lib/metrics/metricsClient.d.ts +95 -0
  50. package/lib/metrics/metricsClient.d.ts.map +1 -0
  51. package/lib/metrics/metricsClient.js +239 -0
  52. package/lib/metrics/metricsClient.js.map +1 -0
  53. package/lib/metrics/metricsDatabaseClient.d.ts +74 -0
  54. package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -0
  55. package/lib/metrics/metricsDatabaseClient.js +218 -0
  56. package/lib/metrics/metricsDatabaseClient.js.map +1 -0
  57. package/lib/metrics/metricsQueueRedisClient.d.ts +57 -0
  58. package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -0
  59. package/lib/metrics/metricsQueueRedisClient.js +277 -0
  60. package/lib/metrics/metricsQueueRedisClient.js.map +1 -0
  61. package/lib/metrics/metricsRedisClient.d.ts +71 -0
  62. package/lib/metrics/metricsRedisClient.d.ts.map +1 -0
  63. package/lib/metrics/metricsRedisClient.js +370 -0
  64. package/lib/metrics/metricsRedisClient.js.map +1 -0
  65. package/lib/redisUtils.d.ts +53 -0
  66. package/lib/redisUtils.d.ts.map +1 -0
  67. package/lib/redisUtils.js +140 -0
  68. package/lib/redisUtils.js.map +1 -0
  69. package/package.json +66 -0
  70. package/scripts/README.md +43 -0
  71. package/scripts/clearMetrics.js +6 -0
  72. package/src/health/databaseChecker.js +183 -0
  73. package/src/health/healthCheckCache.js +216 -0
  74. package/src/health/healthCheckClient.js +347 -0
  75. package/src/health/healthCheckUtils.js +125 -0
  76. package/src/health/healthCheckWorker.js +71 -0
  77. package/src/index.ts +9 -0
  78. package/src/metrics/baseMetricsClient.js +494 -0
  79. package/src/metrics/metricsClient.js +284 -0
  80. package/src/metrics/metricsDatabaseClient.js +236 -0
  81. package/src/metrics/metricsQueueRedisClient.js +352 -0
  82. package/src/metrics/metricsRedisClient.js +417 -0
  83. package/src/redisUtils.js +155 -0
  84. package/tsconfig.json +19 -0
  85. package/tsconfig.types.json +11 -0
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+
3
+ const {
4
+ getRedisClientType,
5
+ REDIS_V4,
6
+ IOREDIS,
7
+ REDIS_V3
8
+ } = require('../redisUtils');
9
+
10
+ /**
11
+ * HealthCheckCache provides a shared cache layer for health check results.
12
+ * It uses Redis if available for cross-process sharing, with graceful fallback
13
+ * to in-memory cache if Redis is not configured or unavailable.
14
+ */
15
+ class HealthCheckCache {
16
+ /**
17
+ * @param {Object} options
18
+ * @param {any} [options.redisClient]
19
+ * @param {string} [options.cacheKey] - Redis key (e.g. 'health:database:status')
20
+ * @param {string} [options.appName] - Used when cacheKey not set: healthcheck:${appName}
21
+ * @param {number} [options.staleThresholdMs]
22
+ */
23
+ constructor(options = {}) {
24
+ this.redisClient = options.redisClient || null;
25
+ this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app';
26
+ this.staleThresholdMs = options.staleThresholdMs ?? 180_000;
27
+ this.cacheKey = options.cacheKey || `healthcheck:${this.appName}`;
28
+
29
+ /** In-memory fallback cache */
30
+ this._memoryCache = null;
31
+ this._memoryCacheTimestamp = null;
32
+ if (this.redisClient) {
33
+ this._redisClientType = getRedisClientType(this.redisClient);
34
+ this._redisAvailable = true;
35
+ } else {
36
+ this._redisAvailable = false;
37
+ console.warn(`[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Checks if Redis is available and working.
43
+ * @returns {Promise<boolean>}
44
+ * @private
45
+ */
46
+ async _checkRedisAvailable() {
47
+ if (!this.redisClient || !this._redisAvailable) {
48
+ return false;
49
+ }
50
+ try {
51
+ let pong;
52
+ if (this._redisClientType === REDIS_V3) {
53
+ pong = await new Promise((resolve, reject) => {
54
+ this.redisClient.ping((err, result) => {
55
+ if (err) reject(err);else resolve(result);
56
+ });
57
+ });
58
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
59
+ pong = await this.redisClient.ping();
60
+ } else {
61
+ return false;
62
+ }
63
+ return pong === 'PONG';
64
+ } catch (err) {
65
+ if (this._redisAvailable) {
66
+ console.warn(`[HealthCheckCache] Redis became unavailable: ${err.message}`);
67
+ this._redisAvailable = false;
68
+ }
69
+ return false;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Gets cached health check result from Redis (if available) or in-memory cache.
75
+ * Throws error if Redis is configured but read fails (so caller can return proper error format).
76
+ * @returns {Promise<Object | null>} Cached result or null
77
+ * @throws {Error} If Redis is configured but read fails
78
+ */
79
+ async get() {
80
+ if (this.redisClient) {
81
+ try {
82
+ let cachedStr;
83
+ if (this._redisClientType === REDIS_V3) {
84
+ cachedStr = await new Promise((resolve, reject) => {
85
+ this.redisClient.get(this.cacheKey, (err, result) => {
86
+ if (err) reject(err);else resolve(result);
87
+ });
88
+ });
89
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
90
+ cachedStr = await this.redisClient.get(this.cacheKey);
91
+ }
92
+ if (cachedStr) {
93
+ try {
94
+ const cached = JSON.parse(cachedStr);
95
+ if (cached.result && cached.timestamp) {
96
+ this._memoryCache = cached.result;
97
+ this._memoryCacheTimestamp = cached.timestamp;
98
+ return cached.result;
99
+ }
100
+ } catch (parseErr) {
101
+ console.warn(`[HealthCheckCache] Failed to parse Redis cache:`, parseErr.message);
102
+ }
103
+ }
104
+ return null;
105
+ } catch (redisErr) {
106
+ this._redisAvailable = false;
107
+ throw new Error(`Redis cache read failed: ${redisErr.message}`);
108
+ }
109
+ }
110
+ if (this._memoryCache && this._memoryCacheTimestamp) {
111
+ return this._memoryCache;
112
+ }
113
+ return null;
114
+ }
115
+
116
+ /**
117
+ * Sets cached health check result in Redis (if available) and in-memory.
118
+ * @param {Object} result - Health check result to cache
119
+ * @returns {Promise<void>}
120
+ */
121
+ async set(result) {
122
+ const cacheData = {
123
+ result,
124
+ timestamp: Date.now()
125
+ };
126
+ this._memoryCache = result;
127
+ this._memoryCacheTimestamp = cacheData.timestamp;
128
+ if (await this._checkRedisAvailable()) {
129
+ try {
130
+ const cacheStr = JSON.stringify(cacheData);
131
+ const ttlSeconds = Math.ceil(this.staleThresholdMs / 1000) + 60;
132
+ if (this._redisClientType === REDIS_V3) {
133
+ await new Promise((resolve, reject) => {
134
+ this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {
135
+ if (err) reject(err);else resolve();
136
+ });
137
+ });
138
+ } else if (this._redisClientType === REDIS_V4) {
139
+ await this.redisClient.set(this.cacheKey, cacheStr, {
140
+ EX: ttlSeconds
141
+ });
142
+ } else if (this._redisClientType === IOREDIS) {
143
+ await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr);
144
+ }
145
+ } catch (redisErr) {
146
+ console.warn(`[HealthCheckCache] Redis write failed (in-memory cache updated):`, redisErr.message);
147
+ this._redisAvailable = false;
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Clears the cache (both Redis and in-memory).
154
+ * @returns {Promise<void>}
155
+ */
156
+ async clear() {
157
+ this._memoryCache = null;
158
+ this._memoryCacheTimestamp = null;
159
+ if (await this._checkRedisAvailable()) {
160
+ try {
161
+ if (this._redisClientType === REDIS_V3) {
162
+ await new Promise((resolve, reject) => {
163
+ this.redisClient.del(this.cacheKey, err => {
164
+ if (err) reject(err);else resolve();
165
+ });
166
+ });
167
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
168
+ await this.redisClient.del(this.cacheKey);
169
+ }
170
+ } catch (redisErr) {
171
+ console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message);
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Checks if Redis is configured and available.
178
+ * @returns {boolean}
179
+ */
180
+ isRedisAvailable() {
181
+ return this._redisAvailable && this.redisClient !== null;
182
+ }
183
+ }
184
+ module.exports = {
185
+ HealthCheckCache
186
+ };
187
+ //# sourceMappingURL=healthCheckCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthCheckCache.js","names":["getRedisClientType","REDIS_V4","IOREDIS","REDIS_V3","require","HealthCheckCache","constructor","options","redisClient","appName","process","env","BUILD_APP_NAME","staleThresholdMs","cacheKey","_memoryCache","_memoryCacheTimestamp","_redisClientType","_redisAvailable","console","warn","_checkRedisAvailable","pong","Promise","resolve","reject","ping","err","result","message","get","cachedStr","cached","JSON","parse","timestamp","parseErr","redisErr","Error","set","cacheData","Date","now","cacheStr","stringify","ttlSeconds","Math","ceil","setex","EX","clear","del","isRedisAvailable","module","exports"],"sources":["../../src/health/healthCheckCache.js"],"sourcesContent":["const {\n getRedisClientType,\n REDIS_V4,\n IOREDIS,\n REDIS_V3,\n} = require('../redisUtils')\n\n/**\n * HealthCheckCache provides a shared cache layer for health check results.\n * It uses Redis if available for cross-process sharing, with graceful fallback\n * to in-memory cache if Redis is not configured or unavailable.\n */\nclass HealthCheckCache {\n /**\n * @param {Object} options\n * @param {any} [options.redisClient]\n * @param {string} [options.cacheKey] - Redis key (e.g. 'health:database:status')\n * @param {string} [options.appName] - Used when cacheKey not set: healthcheck:${appName}\n * @param {number} [options.staleThresholdMs]\n */\n constructor(options = {}) {\n this.redisClient = options.redisClient || null\n this.appName =\n options.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.staleThresholdMs = options.staleThresholdMs ?? 180_000\n this.cacheKey = options.cacheKey || `healthcheck:${this.appName}`\n\n /** In-memory fallback cache */\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (this.redisClient) {\n this._redisClientType = getRedisClientType(this.redisClient)\n this._redisAvailable = true\n } else {\n this._redisAvailable = false\n console.warn(\n `[HealthCheckCache] Redis not configured for ${this.appName}, using in-memory cache only (not shared across processes)`\n )\n }\n }\n\n /**\n * Checks if Redis is available and working.\n * @returns {Promise<boolean>}\n * @private\n */\n async _checkRedisAvailable() {\n if (!this.redisClient || !this._redisAvailable) {\n return false\n }\n\n try {\n let pong\n if (this._redisClientType === REDIS_V3) {\n pong = await new Promise((resolve, reject) => {\n this.redisClient.ping((err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n pong = await this.redisClient.ping()\n } else {\n return false\n }\n return pong === 'PONG'\n } catch (err) {\n if (this._redisAvailable) {\n console.warn(\n `[HealthCheckCache] Redis became unavailable: ${err.message}`\n )\n this._redisAvailable = false\n }\n return false\n }\n }\n\n /**\n * Gets cached health check result from Redis (if available) or in-memory cache.\n * Throws error if Redis is configured but read fails (so caller can return proper error format).\n * @returns {Promise<Object | null>} Cached result or null\n * @throws {Error} If Redis is configured but read fails\n */\n async get() {\n if (this.redisClient) {\n try {\n let cachedStr\n if (this._redisClientType === REDIS_V3) {\n cachedStr = await new Promise((resolve, reject) => {\n this.redisClient.get(this.cacheKey, (err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n cachedStr = await this.redisClient.get(this.cacheKey)\n }\n\n if (cachedStr) {\n try {\n const cached = JSON.parse(cachedStr)\n if (cached.result && cached.timestamp) {\n this._memoryCache = cached.result\n this._memoryCacheTimestamp = cached.timestamp\n return cached.result\n }\n } catch (parseErr) {\n console.warn(\n `[HealthCheckCache] Failed to parse Redis cache:`,\n parseErr.message\n )\n }\n }\n return null\n } catch (redisErr) {\n this._redisAvailable = false\n throw new Error(`Redis cache read failed: ${redisErr.message}`)\n }\n }\n\n if (this._memoryCache && this._memoryCacheTimestamp) {\n return this._memoryCache\n }\n\n return null\n }\n\n /**\n * Sets cached health check result in Redis (if available) and in-memory.\n * @param {Object} result - Health check result to cache\n * @returns {Promise<void>}\n */\n async set(result) {\n const cacheData = {\n result,\n timestamp: Date.now(),\n }\n\n this._memoryCache = result\n this._memoryCacheTimestamp = cacheData.timestamp\n\n if (await this._checkRedisAvailable()) {\n try {\n const cacheStr = JSON.stringify(cacheData)\n const ttlSeconds = Math.ceil(this.staleThresholdMs / 1000) + 60\n\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (this._redisClientType === REDIS_V4) {\n await this.redisClient.set(this.cacheKey, cacheStr, {\n EX: ttlSeconds,\n })\n } else if (this._redisClientType === IOREDIS) {\n await this.redisClient.setex(this.cacheKey, ttlSeconds, cacheStr)\n }\n } catch (redisErr) {\n console.warn(\n `[HealthCheckCache] Redis write failed (in-memory cache updated):`,\n redisErr.message\n )\n this._redisAvailable = false\n }\n }\n }\n\n /**\n * Clears the cache (both Redis and in-memory).\n * @returns {Promise<void>}\n */\n async clear() {\n this._memoryCache = null\n this._memoryCacheTimestamp = null\n\n if (await this._checkRedisAvailable()) {\n try {\n if (this._redisClientType === REDIS_V3) {\n await new Promise((resolve, reject) => {\n this.redisClient.del(this.cacheKey, err => {\n if (err) reject(err)\n else resolve()\n })\n })\n } else if (\n this._redisClientType === REDIS_V4 ||\n this._redisClientType === IOREDIS\n ) {\n await this.redisClient.del(this.cacheKey)\n }\n } catch (redisErr) {\n console.warn(`[HealthCheckCache] Redis clear failed:`, redisErr.message)\n }\n }\n }\n\n /**\n * Checks if Redis is configured and available.\n * @returns {boolean}\n */\n isRedisAvailable() {\n return this._redisAvailable && this.redisClient !== null\n }\n}\n\nmodule.exports = { HealthCheckCache }\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,124 @@
1
+ export type HealthResource = {
2
+ env: string;
3
+ url?: string;
4
+ } | {
5
+ env: string;
6
+ client?: any;
7
+ };
8
+ /**
9
+ * @typedef {{ env: string, url?: string } | { env: string, client?: any }} HealthResource
10
+ */
11
+ export class HealthCheckClient {
12
+ /**
13
+ * @param {Object} options
14
+ * @param {HealthResource[]} options.resources - Must include Redis resource with client
15
+ * @param {Object} [options.config]
16
+ * @param {string} [options.appName] - For cache key: healthcheck:${appName}
17
+ * @param {string} [options.cacheKey] - Redis key (overrides appName)
18
+ */
19
+ constructor(options?: {
20
+ resources: HealthResource[];
21
+ config?: Object | undefined;
22
+ appName?: string | undefined;
23
+ cacheKey?: string | undefined;
24
+ });
25
+ healthConfig: {
26
+ constructor?: Function | undefined;
27
+ toString?: (() => string) | undefined;
28
+ toLocaleString?: (() => string) | undefined;
29
+ valueOf?: (() => Object) | undefined;
30
+ hasOwnProperty?: ((v: PropertyKey) => boolean) | undefined;
31
+ isPrototypeOf?: ((v: Object) => boolean) | undefined;
32
+ propertyIsEnumerable?: ((v: PropertyKey) => boolean) | undefined;
33
+ checkIntervalMs: number;
34
+ staleThresholdMs: number;
35
+ checkTimeoutMs: number;
36
+ maxDbConnectLatencyMs: number;
37
+ };
38
+ appName: string;
39
+ prefixLogs: string;
40
+ _refreshPromise: any;
41
+ /** @type {Map<string, { pool: any, type: string }>} */
42
+ _databasePools: Map<string, {
43
+ pool: any;
44
+ type: string;
45
+ }>;
46
+ /** @type {HealthResource[]} */
47
+ _resources: HealthResource[];
48
+ _redisClientType: string | undefined;
49
+ _cache: HealthCheckCache;
50
+ _getEnv(resource: any): any;
51
+ _getRedisClientForCache(): any;
52
+ _getPool(env: any, url: any): {
53
+ pool: any;
54
+ type: string;
55
+ } | undefined;
56
+ _checkDatabase(resource: any): Promise<{
57
+ status: string;
58
+ connectMs: number;
59
+ } | {
60
+ connectMs?: any;
61
+ status: string;
62
+ error: string;
63
+ }>;
64
+ _checkRedis(resource: any): Promise<{
65
+ status: string;
66
+ error?: undefined;
67
+ } | {
68
+ status: string;
69
+ error: string;
70
+ }>;
71
+ _performHealthCheckInternal(): Promise<{
72
+ status: string;
73
+ lastCheckAt: number;
74
+ resources: {};
75
+ isStale: boolean;
76
+ config: {
77
+ constructor?: Function | undefined;
78
+ toString?: (() => string) | undefined;
79
+ toLocaleString?: (() => string) | undefined;
80
+ valueOf?: (() => Object) | undefined;
81
+ hasOwnProperty?: ((v: PropertyKey) => boolean) | undefined;
82
+ isPrototypeOf?: ((v: Object) => boolean) | undefined;
83
+ propertyIsEnumerable?: ((v: PropertyKey) => boolean) | undefined;
84
+ checkIntervalMs: number;
85
+ staleThresholdMs: number;
86
+ checkTimeoutMs: number;
87
+ maxDbConnectLatencyMs: number;
88
+ };
89
+ }>;
90
+ _formatResult(result: any, cached?: boolean): any;
91
+ performHealthCheck(): Promise<any>;
92
+ getCachedResult(): Promise<any>;
93
+ refreshCache(): Promise<{
94
+ status: string;
95
+ lastCheckAt: number;
96
+ resources: {};
97
+ isStale: boolean;
98
+ config: {
99
+ constructor?: Function | undefined;
100
+ toString?: (() => string) | undefined;
101
+ toLocaleString?: (() => string) | undefined;
102
+ valueOf?: (() => Object) | undefined;
103
+ hasOwnProperty?: ((v: PropertyKey) => boolean) | undefined;
104
+ isPrototypeOf?: ((v: Object) => boolean) | undefined;
105
+ propertyIsEnumerable?: ((v: PropertyKey) => boolean) | undefined;
106
+ checkIntervalMs: number;
107
+ staleThresholdMs: number;
108
+ checkTimeoutMs: number;
109
+ maxDbConnectLatencyMs: number;
110
+ };
111
+ }>;
112
+ clearCache(): void;
113
+ healthHandler(): (req: any, res: any) => Promise<void>;
114
+ cleanup(): Promise<void>;
115
+ }
116
+ /** @type {{ checkIntervalMs: number, staleThresholdMs: number, checkTimeoutMs: number, maxDbConnectLatencyMs: number }} */
117
+ export const DEFAULT_HEALTH_CONFIG: {
118
+ checkIntervalMs: number;
119
+ staleThresholdMs: number;
120
+ checkTimeoutMs: number;
121
+ maxDbConnectLatencyMs: number;
122
+ };
123
+ import { HealthCheckCache } from "./healthCheckCache";
124
+ //# sourceMappingURL=healthCheckClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthCheckClient.d.ts","sourceRoot":"","sources":["../../src/health/healthCheckClient.js"],"names":[],"mappings":"6BA6Ea;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE;AAD1E;;GAEG;AACH;IACE;;;;;;OAMG;IACH;QALqC,SAAS,EAAnC,cAAc,EAAE;QACC,MAAM;QACN,OAAO;QACP,QAAQ;OA2BnC;IAxBC;;;;;;;;;;;;MAAmE;IACnE,gBACgE;IAEhE,mBAAmD;IAEnD,qBAA2B;IAC3B,uDAAuD;IACvD;cAD+B,GAAG;cAAQ,MAAM;OACjB;IAE/B,+BAA+B;IAC/B,YADW,cAAc,EAAE,CACc;IAIvC,qCAAuD;IAGzD,yBAKE;IAGJ,4BAEC;IAED,+BAGC;IAED;cA5BiC,GAAG;cAAQ,MAAM;kBAsCjD;IAED;;;;;;;OAoCC;IAED;;;;;;OA4BC;IAED;;;;;;;;;;;;;;;;;;OA8BC;IAED,kDAeC;IAED,mCAgBC;IAED,gCAiBC;IAED;;;;;;;;;;;;;;;;;;OAIC;IAED,mBAEC;IAED,uDAiCC;IAED,yBASC;CACF;AAhUD,2HAA2H;AAC3H,oCADW;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,qBAAqB,EAAE,MAAM,CAAA;CAAE,CAOtH"}
@@ -0,0 +1,324 @@
1
+ "use strict";
2
+
3
+ const {
4
+ createDatabasePool,
5
+ runHealthCheck,
6
+ closePool
7
+ } = require('./databaseChecker');
8
+ const {
9
+ getRedisClientType,
10
+ REDIS_V4,
11
+ IOREDIS,
12
+ REDIS_V3
13
+ } = require('../redisUtils');
14
+ const {
15
+ HealthCheckCache
16
+ } = require('./healthCheckCache');
17
+
18
+ /**
19
+ * @param {string} name
20
+ * @returns {number | undefined}
21
+ */
22
+ function readNumberEnv(name) {
23
+ const raw = process.env[name];
24
+ if (raw == null || raw === '') return undefined;
25
+ const num = Number(raw);
26
+ return Number.isFinite(num) ? num : undefined;
27
+ }
28
+
29
+ /** @type {{ checkIntervalMs: number, staleThresholdMs: number, checkTimeoutMs: number, maxDbConnectLatencyMs: number }} */
30
+ const DEFAULT_HEALTH_CONFIG = {
31
+ checkIntervalMs: 30_000,
32
+ staleThresholdMs: 180_000,
33
+ checkTimeoutMs: 15_000,
34
+ maxDbConnectLatencyMs: readNumberEnv('HEALTH_DB_MAX_CONNECT_LATENCY_MS') ?? 1000
35
+ };
36
+ const SENSITIVE_PATTERNS = [{
37
+ pattern: /(postgres(?:ql)?|mysql|mongodb|redis|amqp):\/\/([^:]+):([^@]+)@([^:/]+)(:\d+)?\/([^\s?]+)/gi,
38
+ replacement: '$1://***:***@***$5/***'
39
+ }, {
40
+ pattern: /(\w+):\/\/([^:]+):([^@]+)@([^\s/]+)/gi,
41
+ replacement: '$1://***:***@***'
42
+ }, {
43
+ pattern: /(password|passwd|pwd|secret|token|api[_-]?key|auth[_-]?token)["\s]*[:=]["\s]*([^\s,}"]+)/gi,
44
+ replacement: '$1=***'
45
+ }, {
46
+ pattern: /(database|table|schema|role|user|relation|column|index)\s*["']([^"']+)["']/gi,
47
+ replacement: '$1 "***"'
48
+ }, {
49
+ pattern: /\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d+)?\b/g,
50
+ replacement: '***$2'
51
+ }, {
52
+ pattern: /\b(host|hostname|server)["\s]*[:=]["\s]*([^\s,}"]+)/gi,
53
+ replacement: '$1=***'
54
+ }];
55
+
56
+ /**
57
+ * @param {string} text
58
+ * @returns {string}
59
+ */
60
+ function maskSensitiveData(text) {
61
+ if (!text || typeof text !== 'string') return text;
62
+ let masked = text;
63
+ for (const {
64
+ pattern,
65
+ replacement
66
+ } of SENSITIVE_PATTERNS) {
67
+ masked = masked.replace(pattern, replacement);
68
+ }
69
+ return masked;
70
+ }
71
+
72
+ /**
73
+ * @typedef {{ env: string, url?: string } | { env: string, client?: any }} HealthResource
74
+ */
75
+ class HealthCheckClient {
76
+ /**
77
+ * @param {Object} options
78
+ * @param {HealthResource[]} options.resources - Must include Redis resource with client
79
+ * @param {Object} [options.config]
80
+ * @param {string} [options.appName] - For cache key: healthcheck:${appName}
81
+ * @param {string} [options.cacheKey] - Redis key (overrides appName)
82
+ */
83
+ constructor(options = {}) {
84
+ this.healthConfig = {
85
+ ...DEFAULT_HEALTH_CONFIG,
86
+ ...options.config
87
+ };
88
+ this.appName = options.appName || process.env.BUILD_APP_NAME || 'unknown-app';
89
+ this.prefixLogs = `[${this.appName}] [HealthCheck]`;
90
+ this._refreshPromise = null;
91
+ /** @type {Map<string, { pool: any, type: string }>} */
92
+ this._databasePools = new Map();
93
+
94
+ /** @type {HealthResource[]} */
95
+ this._resources = options.resources || [];
96
+ const redisClient = this._getRedisClientForCache();
97
+ if (redisClient) {
98
+ this._redisClientType = getRedisClientType(redisClient);
99
+ }
100
+ this._cache = new HealthCheckCache({
101
+ redisClient: redisClient || null,
102
+ cacheKey: options.cacheKey,
103
+ appName: this.appName,
104
+ staleThresholdMs: this.healthConfig.staleThresholdMs
105
+ });
106
+ }
107
+ _getEnv(resource) {
108
+ return resource.env ?? resource.name;
109
+ }
110
+ _getRedisClientForCache() {
111
+ const redisResource = this._resources.find(r => 'client' in r && r.client);
112
+ return redisResource?.client || null;
113
+ }
114
+ _getPool(env, url) {
115
+ if (!this._databasePools.has(env)) {
116
+ const {
117
+ pool,
118
+ type
119
+ } = createDatabasePool(env, url, this.healthConfig.checkTimeoutMs);
120
+ this._databasePools.set(env, {
121
+ pool,
122
+ type
123
+ });
124
+ }
125
+ return this._databasePools.get(env);
126
+ }
127
+ async _checkDatabase(resource) {
128
+ const env = this._getEnv(resource);
129
+ const url = 'url' in resource ? resource.url : process.env[env];
130
+ if (!url) {
131
+ return {
132
+ status: 'error',
133
+ error: `Env ${env} not set`
134
+ };
135
+ }
136
+ try {
137
+ const {
138
+ pool,
139
+ type
140
+ } = this._getPool(env, url);
141
+ const timings = await runHealthCheck(pool, type);
142
+ const maxConnect = this.healthConfig.maxDbConnectLatencyMs;
143
+ if (typeof maxConnect === 'number' && Number.isFinite(maxConnect) && timings?.connectMs != null && timings.connectMs > maxConnect) {
144
+ return {
145
+ status: 'error',
146
+ error: `DB connect latency ${timings.connectMs}ms exceeds ${maxConnect}ms`,
147
+ connectMs: timings.connectMs
148
+ };
149
+ }
150
+ return {
151
+ status: 'ok',
152
+ connectMs: timings.connectMs
153
+ };
154
+ } catch (err) {
155
+ const timings = err?.healthCheckTimings;
156
+ return {
157
+ status: 'error',
158
+ error: maskSensitiveData(err.message),
159
+ ...(timings && typeof timings === 'object' ? {
160
+ connectMs: timings.connectMs
161
+ } : {})
162
+ };
163
+ }
164
+ }
165
+ async _checkRedis(resource) {
166
+ const {
167
+ client
168
+ } = resource;
169
+ if (!client) return {
170
+ status: 'ok'
171
+ };
172
+ try {
173
+ let pong;
174
+ if (this._redisClientType === REDIS_V3) {
175
+ pong = await new Promise((resolve, reject) => {
176
+ client.ping((err, result) => {
177
+ if (err) reject(err);else resolve(result);
178
+ });
179
+ });
180
+ } else if (this._redisClientType === REDIS_V4 || this._redisClientType === IOREDIS) {
181
+ pong = await client.ping();
182
+ } else {
183
+ return {
184
+ status: 'error',
185
+ error: 'Unknown Redis client type'
186
+ };
187
+ }
188
+ return pong === 'PONG' ? {
189
+ status: 'ok'
190
+ } : {
191
+ status: 'error',
192
+ error: `Unexpected: ${pong}`
193
+ };
194
+ } catch (err) {
195
+ return {
196
+ status: 'error',
197
+ error: maskSensitiveData(err.message)
198
+ };
199
+ }
200
+ }
201
+ async _performHealthCheckInternal() {
202
+ const resources = {};
203
+ for (const resource of this._resources) {
204
+ const env = this._getEnv(resource);
205
+ if ('client' in resource && resource.client) {
206
+ resources[env] = await this._checkRedis(resource);
207
+ } else {
208
+ resources[env] = await this._checkDatabase(resource);
209
+ }
210
+ }
211
+ const sortedResources = Object.keys(resources).sort().reduce((acc, key) => {
212
+ acc[key] = resources[key];
213
+ return acc;
214
+ }, {});
215
+ const hasError = Object.values(resources).some(r => r.status === 'error');
216
+ const lastCheckAt = Date.now();
217
+ return {
218
+ status: hasError ? 'error' : 'ok',
219
+ lastCheckAt,
220
+ resources: sortedResources,
221
+ isStale: false,
222
+ config: this.healthConfig
223
+ };
224
+ }
225
+ _formatResult(result, cached = false) {
226
+ const isStale = !result.lastCheckAt || Date.now() - result.lastCheckAt > this.healthConfig.staleThresholdMs;
227
+ return {
228
+ ...result,
229
+ isStale,
230
+ status: isStale ? 'stale' : result.status,
231
+ ...(isStale && {
232
+ error: 'Health check data is stale, health-check worker may not be running. Resource statuses are unknown.'
233
+ }),
234
+ ...(cached && {
235
+ cached: true
236
+ })
237
+ };
238
+ }
239
+ async performHealthCheck() {
240
+ if (this._refreshPromise) {
241
+ return this._refreshPromise;
242
+ }
243
+ this._refreshPromise = this._performHealthCheckInternal().then(result => {
244
+ this._refreshPromise = null;
245
+ return this._formatResult(result);
246
+ }).catch(err => {
247
+ this._refreshPromise = null;
248
+ throw err;
249
+ });
250
+ return this._refreshPromise;
251
+ }
252
+ async getCachedResult() {
253
+ try {
254
+ const cached = await this._cache.get();
255
+ if (cached) return this._formatResult(cached);
256
+ return null;
257
+ } catch (err) {
258
+ console.error(`${this.prefixLogs} Failed to read from cache:`, err);
259
+ return {
260
+ status: 'error',
261
+ lastCheckAt: null,
262
+ resources: {},
263
+ isStale: true,
264
+ error: 'Redis unavailable, unable to read health status of other resources',
265
+ config: this.healthConfig
266
+ };
267
+ }
268
+ }
269
+ async refreshCache() {
270
+ const result = await this._performHealthCheckInternal();
271
+ await this._cache.set(result);
272
+ return result;
273
+ }
274
+ clearCache() {
275
+ this._refreshPromise = null;
276
+ }
277
+ healthHandler() {
278
+ return async (req, res) => {
279
+ try {
280
+ const result = await this.getCachedResult();
281
+ if (!result) {
282
+ res.status(503).json({
283
+ status: 'error',
284
+ lastCheckAt: null,
285
+ resources: {},
286
+ isStale: true,
287
+ error: 'No health check data yet, health-check worker may not be running',
288
+ config: this.healthConfig
289
+ });
290
+ return;
291
+ }
292
+ const statusCode = result.status === 'ok' ? 200 : 503;
293
+ res.status(statusCode).json(result);
294
+ } catch (err) {
295
+ console.error(`${this.prefixLogs} Health check failed:`, err);
296
+ res.status(503).json({
297
+ status: 'error',
298
+ lastCheckAt: null,
299
+ resources: {},
300
+ isStale: true,
301
+ error: 'Redis unavailable, unable to read health status of other resources',
302
+ config: this.healthConfig
303
+ });
304
+ }
305
+ };
306
+ }
307
+ async cleanup() {
308
+ for (const [, {
309
+ pool
310
+ }] of this._databasePools) {
311
+ try {
312
+ await closePool(pool);
313
+ } catch (err) {
314
+ console.error(`${this.prefixLogs} Error closing database pool:`, err);
315
+ }
316
+ }
317
+ this._databasePools.clear();
318
+ }
319
+ }
320
+ module.exports = {
321
+ HealthCheckClient,
322
+ DEFAULT_HEALTH_CONFIG
323
+ };
324
+ //# sourceMappingURL=healthCheckClient.js.map