@onlineapps/service-common 1.0.1 → 1.0.3
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/onlineapps-service-common-1.0.1.tgz +0 -0
- package/package.json +3 -2
- package/src/index.js +32 -2
- package/src/infrastructure/waitForHealthCheckQueueReady.js +21 -9
- package/src/infrastructure/waitForInfrastructureReady.js +50 -15
- package/src/redisClient.js +134 -0
- package/src/runtime-config.js +134 -0
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Common utilities for both infrastructure services and business services",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"prepublishOnly": "../../scripts/pre-publish-compatibility-check.sh",
|
|
8
|
+
"postpublish": "../../scripts/update-manifest-from-npm.sh && ../../scripts/update-all-services.sh",
|
|
7
9
|
"test": "jest",
|
|
8
10
|
"test:unit": "jest tests/unit",
|
|
9
11
|
"test:integration": "jest tests/integration"
|
|
@@ -27,4 +29,3 @@
|
|
|
27
29
|
"node": ">=14.0.0"
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
|
-
|
package/src/index.js
CHANGED
|
@@ -2,22 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @onlineapps/service-common
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Common utilities for both infrastructure services and business services.
|
|
7
7
|
* Provides shared functionality that is used across the entire system.
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* This library is for ALL services (infrastructure and business).
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const { waitForInfrastructureReady } = require('./infrastructure/waitForInfrastructureReady');
|
|
13
13
|
const { waitForHealthCheckQueueReady } = require('./infrastructure/waitForHealthCheckQueueReady');
|
|
14
14
|
const { sendMonitoringFailFallbackEmail } = require('./reporting/monitoringFallbackEmail');
|
|
15
|
+
const {
|
|
16
|
+
buildRedisUrl,
|
|
17
|
+
createRedisClient,
|
|
18
|
+
connectRedis
|
|
19
|
+
} = require('./redisClient');
|
|
20
|
+
const {
|
|
21
|
+
requireEnv,
|
|
22
|
+
optionalEnv,
|
|
23
|
+
optionalNumberEnv,
|
|
24
|
+
getCriticalConfig,
|
|
25
|
+
getOptionalInfraConfig,
|
|
26
|
+
getServiceConfig,
|
|
27
|
+
getInfrastructureHealthConfig
|
|
28
|
+
} = require('./runtime-config');
|
|
15
29
|
|
|
16
30
|
module.exports = {
|
|
17
31
|
// Infrastructure readiness utilities (used by both infrastructure and business services)
|
|
18
32
|
waitForInfrastructureReady,
|
|
19
33
|
waitForHealthCheckQueueReady,
|
|
34
|
+
|
|
35
|
+
// Redis utilities (shared between services and tests)
|
|
36
|
+
buildRedisUrl,
|
|
37
|
+
createRedisClient,
|
|
38
|
+
connectRedis,
|
|
39
|
+
|
|
40
|
+
// Configuration helpers (NO FALLBACKS for critical infrastructure)
|
|
41
|
+
requireEnv,
|
|
42
|
+
optionalEnv,
|
|
43
|
+
optionalNumberEnv,
|
|
44
|
+
getCriticalConfig,
|
|
45
|
+
getOptionalInfraConfig,
|
|
46
|
+
getServiceConfig,
|
|
47
|
+
getInfrastructureHealthConfig,
|
|
48
|
+
|
|
20
49
|
// Reporting utilities
|
|
21
50
|
sendMonitoringFailFallbackEmail
|
|
22
51
|
};
|
|
23
52
|
|
|
53
|
+
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { connectRedis, buildRedisUrl } = require('../redisClient');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* waitForHealthCheckQueueReady.js
|
|
5
7
|
*
|
|
@@ -31,7 +33,9 @@
|
|
|
31
33
|
* @throws {Error} - If timeout is reached
|
|
32
34
|
*/
|
|
33
35
|
async function waitForHealthCheckQueueReady(options = {}) {
|
|
34
|
-
const redisUrl =
|
|
36
|
+
const redisUrl =
|
|
37
|
+
options.redisUrl ||
|
|
38
|
+
buildRedisUrl({ env: process.env, defaults: { host: 'api_node_cache', port: '6379' } });
|
|
35
39
|
const maxWait = options.maxWait || parseInt(process.env.INFRASTRUCTURE_HEALTH_QUEUE_WAIT_MAX_TIME) || 60000; // 1 minute
|
|
36
40
|
const checkInterval = options.checkInterval || parseInt(process.env.INFRASTRUCTURE_HEALTH_QUEUE_WAIT_CHECK_INTERVAL) || 2000; // 2 seconds
|
|
37
41
|
const logger = options.logger || console;
|
|
@@ -55,15 +59,23 @@ async function waitForHealthCheckQueueReady(options = {}) {
|
|
|
55
59
|
log(`[HealthCheckQueueReady] Max wait: ${maxWait}ms, Check interval: ${checkInterval}ms`);
|
|
56
60
|
|
|
57
61
|
try {
|
|
58
|
-
// Connect to Redis
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// Connect to Redis using shared helper (consistent config + fail-fast behaviour)
|
|
63
|
+
redis = await connectRedis({
|
|
64
|
+
purpose: 'wait-for-health-check-queue-ready',
|
|
65
|
+
logger: {
|
|
66
|
+
info: (message, meta = {}) => {
|
|
67
|
+
const suffix = meta && meta.url ? ` (${meta.url})` : '';
|
|
68
|
+
log(`[HealthCheckQueueReady] ${message}${suffix}`);
|
|
69
|
+
},
|
|
70
|
+
error: (message, meta = {}) => {
|
|
71
|
+
const detail = meta && meta.error ? ` ${meta.error}` : '';
|
|
72
|
+
log(`[HealthCheckQueueReady] ${message}${detail}`);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
timeoutMs: 10000,
|
|
76
|
+
env: process.env,
|
|
77
|
+
defaults: { host: 'api_node_cache', port: '6379' }
|
|
64
78
|
});
|
|
65
|
-
|
|
66
|
-
await redis.connect();
|
|
67
79
|
log('[HealthCheckQueueReady] Connected to Redis');
|
|
68
80
|
|
|
69
81
|
while (Date.now() - startTime < maxWait) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { connectRedis, buildRedisUrl } = require('../redisClient');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* waitForInfrastructureReady.js
|
|
5
7
|
*
|
|
@@ -42,7 +44,9 @@
|
|
|
42
44
|
* Registry config (config.infrastructureHealth) is the source of truth for these defaults.
|
|
43
45
|
*/
|
|
44
46
|
async function waitForInfrastructureReady(options = {}) {
|
|
45
|
-
const redisUrl =
|
|
47
|
+
const redisUrl =
|
|
48
|
+
options.redisUrl ||
|
|
49
|
+
buildRedisUrl({ env: process.env, defaults: { host: 'api_node_cache', port: '6379' } });
|
|
46
50
|
const maxWait = options.maxWait || parseInt(process.env.INFRASTRUCTURE_HEALTH_WAIT_MAX_TIME) || 300000; // 5 minutes
|
|
47
51
|
const checkInterval = options.checkInterval || parseInt(process.env.INFRASTRUCTURE_HEALTH_WAIT_CHECK_INTERVAL) || 5000; // 5 seconds
|
|
48
52
|
const logger = options.logger || console;
|
|
@@ -72,23 +76,39 @@ async function waitForInfrastructureReady(options = {}) {
|
|
|
72
76
|
log(`[InfrastructureReady] Max wait: ${maxWait}ms, Check interval: ${checkInterval}ms`);
|
|
73
77
|
|
|
74
78
|
try {
|
|
75
|
-
// Connect to Redis
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Connect to Redis with timeout using shared helper (fail-fast, no infinite reconnect loops)
|
|
80
|
+
redis = await connectRedis({
|
|
81
|
+
purpose: 'wait-for-infrastructure-ready',
|
|
82
|
+
logger: {
|
|
83
|
+
error: (message, meta = {}) => {
|
|
84
|
+
const msg = meta && meta.error ? `${message} ${meta.error}` : message;
|
|
85
|
+
log(`[InfrastructureReady] ${msg}`);
|
|
86
|
+
},
|
|
87
|
+
info: (message, meta = {}) => {
|
|
88
|
+
const suffix = meta && meta.url ? ` (${meta.url})` : '';
|
|
89
|
+
log(`[InfrastructureReady] ${message}${suffix}`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
timeoutMs: 10000,
|
|
93
|
+
env: process.env,
|
|
94
|
+
defaults: { host: 'api_node_cache', port: '6379' }
|
|
81
95
|
});
|
|
82
|
-
|
|
83
|
-
await redis.connect();
|
|
84
96
|
log('[InfrastructureReady] Connected to Redis');
|
|
85
97
|
|
|
86
98
|
while (Date.now() - startTime < maxWait) {
|
|
87
99
|
attemptCount++;
|
|
88
100
|
|
|
89
101
|
try {
|
|
90
|
-
// Check Redis key: infrastructure:health:all
|
|
91
|
-
const
|
|
102
|
+
// Check Redis key: infrastructure:health:all with timeout to prevent hanging
|
|
103
|
+
const REDIS_OPERATION_TIMEOUT = 5000; // 5 seconds per operation
|
|
104
|
+
const getPromise = redis.get('infrastructure:health:all');
|
|
105
|
+
const getTimeoutPromise = new Promise((_, reject) => {
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
reject(new Error(`Redis get() operation timeout after ${REDIS_OPERATION_TIMEOUT}ms`));
|
|
108
|
+
}, REDIS_OPERATION_TIMEOUT);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const allHealthy = await Promise.race([getPromise, getTimeoutPromise]);
|
|
92
112
|
|
|
93
113
|
if (allHealthy === 'true') {
|
|
94
114
|
// All services are UP, we can proceed
|
|
@@ -100,9 +120,18 @@ async function waitForInfrastructureReady(options = {}) {
|
|
|
100
120
|
const serviceKeys = ['gateway', 'registry', 'validator', 'delivery', 'monitoring'];
|
|
101
121
|
const statuses = {};
|
|
102
122
|
for (const serviceName of serviceKeys) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
123
|
+
try {
|
|
124
|
+
const statusPromise = redis.get(`infrastructure:health:${serviceName}`);
|
|
125
|
+
const statusTimeoutPromise = new Promise((_, reject) => {
|
|
126
|
+
setTimeout(() => reject(new Error('timeout')), REDIS_OPERATION_TIMEOUT);
|
|
127
|
+
});
|
|
128
|
+
const status = await Promise.race([statusPromise, statusTimeoutPromise]);
|
|
129
|
+
if (status) {
|
|
130
|
+
statuses[serviceName] = JSON.parse(status);
|
|
131
|
+
}
|
|
132
|
+
} catch (statusError) {
|
|
133
|
+
// Ignore individual service status errors
|
|
134
|
+
log(`[InfrastructureReady] Could not get status for ${serviceName}: ${statusError.message}`);
|
|
106
135
|
}
|
|
107
136
|
}
|
|
108
137
|
if (Object.keys(statuses).length > 0) {
|
|
@@ -115,7 +144,13 @@ async function waitForInfrastructureReady(options = {}) {
|
|
|
115
144
|
log(`[InfrastructureReady] Attempt ${attemptCount}: Not all services ready (current status: ${allHealthy || 'unknown'}). Waiting ${checkInterval}ms...`);
|
|
116
145
|
|
|
117
146
|
} catch (error) {
|
|
118
|
-
|
|
147
|
+
const errorMsg = error.message || String(error);
|
|
148
|
+
log(`[InfrastructureReady] Attempt ${attemptCount}: Failed to get infrastructure health from Redis (${errorMsg}). Waiting ${checkInterval}ms...`);
|
|
149
|
+
|
|
150
|
+
// If Redis connection is lost, throw error immediately instead of retrying
|
|
151
|
+
if (errorMsg.includes('timeout') || errorMsg.includes('ECONNREFUSED') || errorMsg.includes('ENOTFOUND')) {
|
|
152
|
+
throw new Error(`[InfrastructureReady] Redis connection lost: ${errorMsg}. Cannot continue waiting for infrastructure ready.`);
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
155
|
|
|
121
156
|
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared Redis client utilities for services and tests.
|
|
5
|
+
*
|
|
6
|
+
* Responsibility:
|
|
7
|
+
* - Build Redis connection URL from environment in a consistent way
|
|
8
|
+
* - Create a configured redis client with unified error logging
|
|
9
|
+
* - (Optionally) connect with a hard timeout for tests / tools
|
|
10
|
+
*
|
|
11
|
+
* This module is intentionally small and framework-agnostic so it can be used
|
|
12
|
+
* both in infrastructure services, business services and in test utilities.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { createClient } = require('redis');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build a Redis connection URL from environment.
|
|
19
|
+
*
|
|
20
|
+
* Precedence:
|
|
21
|
+
* 1. REDIS_URL (full URL, e.g. redis://host:port/db)
|
|
22
|
+
* 2. REDIS_HOST + REDIS_PORT
|
|
23
|
+
* 3. Defaults (host/port) or localhost:6379
|
|
24
|
+
*/
|
|
25
|
+
function buildRedisUrl(options = {}) {
|
|
26
|
+
const env = options.env || process.env || {};
|
|
27
|
+
const defaults = options.defaults || {};
|
|
28
|
+
|
|
29
|
+
if (env.REDIS_URL && typeof env.REDIS_URL === 'string' && env.REDIS_URL.length > 0) {
|
|
30
|
+
return env.REDIS_URL;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const host = env.REDIS_HOST || defaults.host || 'localhost';
|
|
34
|
+
const port = env.REDIS_PORT || defaults.port || '6379';
|
|
35
|
+
|
|
36
|
+
return `redis://${host}:${port}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a Redis client with unified error logging and optional test settings.
|
|
41
|
+
*
|
|
42
|
+
* @param {Object} options
|
|
43
|
+
* @param {string} [options.purpose] - Logical purpose (for log context)
|
|
44
|
+
* @param {boolean} [options.forTests=false] - If true, disables automatic reconnect loops
|
|
45
|
+
* @param {Object} [options.logger=console] - Logger with .error/.info methods
|
|
46
|
+
* @param {Object} [options.env=process.env] - Optional env override (for tests)
|
|
47
|
+
* @param {Object} [options.defaults] - Optional default host/port overrides
|
|
48
|
+
* @returns {{ client: import('redis').RedisClientType, url: string }}
|
|
49
|
+
*/
|
|
50
|
+
function createRedisClient(options = {}) {
|
|
51
|
+
const {
|
|
52
|
+
purpose = 'default',
|
|
53
|
+
forTests = false,
|
|
54
|
+
logger = console,
|
|
55
|
+
env,
|
|
56
|
+
defaults
|
|
57
|
+
} = options;
|
|
58
|
+
|
|
59
|
+
const url = buildRedisUrl({ env, defaults });
|
|
60
|
+
|
|
61
|
+
const socketOptions = forTests
|
|
62
|
+
? {
|
|
63
|
+
// In tests we typically want fail-fast behaviour, not infinite reconnect loops
|
|
64
|
+
reconnectStrategy: false
|
|
65
|
+
}
|
|
66
|
+
: {};
|
|
67
|
+
|
|
68
|
+
const client = createClient({
|
|
69
|
+
url,
|
|
70
|
+
socket: socketOptions
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const log = logger && typeof logger.error === 'function' ? logger : console;
|
|
74
|
+
|
|
75
|
+
client.on('error', (err) => {
|
|
76
|
+
try {
|
|
77
|
+
log.error('[Redis] Error', {
|
|
78
|
+
purpose,
|
|
79
|
+
url,
|
|
80
|
+
message: err && err.message ? err.message : String(err)
|
|
81
|
+
});
|
|
82
|
+
} catch {
|
|
83
|
+
// Last-resort fallback to avoid throwing in error handler
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.error('[Redis] Error', err);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return { client, url };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create and connect a Redis client with a hard timeout (useful for tests/tools).
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} options - Same as createRedisClient plus:
|
|
96
|
+
* @param {number} [options.timeoutMs=15000] - Max time to wait for connect()
|
|
97
|
+
* @returns {Promise<import('redis').RedisClientType>}
|
|
98
|
+
*/
|
|
99
|
+
async function connectRedis(options = {}) {
|
|
100
|
+
const { timeoutMs = 15000, logger = console } = options;
|
|
101
|
+
const { client, url } = createRedisClient(options);
|
|
102
|
+
|
|
103
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
104
|
+
await client.connect();
|
|
105
|
+
if (logger && typeof logger.info === 'function') {
|
|
106
|
+
logger.info('[Redis] Connected', { url });
|
|
107
|
+
}
|
|
108
|
+
return client;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const connectPromise = client.connect();
|
|
112
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
113
|
+
setTimeout(
|
|
114
|
+
() => reject(new Error(`Redis connection timeout after ${timeoutMs}ms to ${url}`)),
|
|
115
|
+
timeoutMs
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
120
|
+
|
|
121
|
+
if (logger && typeof logger.info === 'function') {
|
|
122
|
+
logger.info('[Redis] Connected', { url });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return client;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = {
|
|
129
|
+
buildRedisUrl,
|
|
130
|
+
createRedisClient,
|
|
131
|
+
connectRedis
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Runtime Configuration Helper
|
|
5
|
+
*
|
|
6
|
+
* NO FALLBACKS for critical infrastructure connections.
|
|
7
|
+
* Services must fail fast if critical config is missing.
|
|
8
|
+
*
|
|
9
|
+
* This ensures:
|
|
10
|
+
* - Services don't silently connect to wrong infrastructure
|
|
11
|
+
* - Configuration problems are detected immediately
|
|
12
|
+
* - No hidden dependencies on hardcoded values
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get required environment variable or throw error
|
|
17
|
+
* @param {string} name - Environment variable name
|
|
18
|
+
* @param {string} description - Description for error message
|
|
19
|
+
* @returns {string} Environment variable value
|
|
20
|
+
* @throws {Error} If variable is not set
|
|
21
|
+
*/
|
|
22
|
+
function requireEnv(name, description) {
|
|
23
|
+
const value = process.env[name];
|
|
24
|
+
if (!value || value.trim() === '') {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Service configuration error: ${name} is required but not set. ${description || ''}\n` +
|
|
27
|
+
`Please set ${name} environment variable in .env file or environment.\n` +
|
|
28
|
+
`Example: ${name}=value`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get optional environment variable with default
|
|
36
|
+
* Use ONLY for non-critical values (ports, timeouts, log levels)
|
|
37
|
+
* @param {string} name - Environment variable name
|
|
38
|
+
* @param {any} defaultValue - Default value if not set
|
|
39
|
+
* @returns {any} Environment variable value or default
|
|
40
|
+
*/
|
|
41
|
+
function optionalEnv(name, defaultValue) {
|
|
42
|
+
const value = process.env[name];
|
|
43
|
+
if (value === undefined || value === null || value === '') {
|
|
44
|
+
return defaultValue;
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get optional number from environment with default
|
|
51
|
+
* @param {string} name - Environment variable name
|
|
52
|
+
* @param {number} defaultValue - Default value if not set or invalid
|
|
53
|
+
* @returns {number} Parsed number or default
|
|
54
|
+
*/
|
|
55
|
+
function optionalNumberEnv(name, defaultValue) {
|
|
56
|
+
const value = process.env[name];
|
|
57
|
+
if (!value || value.trim() === '') {
|
|
58
|
+
return defaultValue;
|
|
59
|
+
}
|
|
60
|
+
const parsed = parseInt(value, 10);
|
|
61
|
+
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get critical infrastructure configuration
|
|
66
|
+
* These MUST be set - no fallbacks allowed
|
|
67
|
+
* @returns {Object} Critical infrastructure config
|
|
68
|
+
*/
|
|
69
|
+
function getCriticalConfig() {
|
|
70
|
+
return {
|
|
71
|
+
REDIS_URL: requireEnv('REDIS_URL', 'Redis connection URL (e.g., redis://api_node_cache:6379)'),
|
|
72
|
+
RABBITMQ_URL: requireEnv('RABBITMQ_URL', 'RabbitMQ connection URL (e.g., amqp://guest:guest@api_services_queuer:5672)')
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get optional infrastructure configuration
|
|
78
|
+
* These have sensible defaults for development
|
|
79
|
+
* @returns {Object} Optional infrastructure config
|
|
80
|
+
*/
|
|
81
|
+
function getOptionalInfraConfig() {
|
|
82
|
+
return {
|
|
83
|
+
POSTGRES_URL: optionalEnv('POSTGRES_URL', null), // Optional - only for services that need it
|
|
84
|
+
RABBITMQ_MGMT_URL: optionalEnv('RABBITMQ_MGMT_URL', null),
|
|
85
|
+
RABBITMQ_DEFAULT_USER: optionalEnv('RABBITMQ_DEFAULT_USER', 'guest'),
|
|
86
|
+
RABBITMQ_DEFAULT_PASS: optionalEnv('RABBITMQ_DEFAULT_PASS', 'guest'),
|
|
87
|
+
RABBITMQ_VHOST: optionalEnv('RABBITMQ_VHOST', '/')
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get service configuration with defaults
|
|
93
|
+
* @param {Object} options - Options
|
|
94
|
+
* @param {string} [options.defaultPort] - Default port if PORT not set
|
|
95
|
+
* @param {string} [options.defaultLogLevel] - Default log level
|
|
96
|
+
* @returns {Object} Service config
|
|
97
|
+
*/
|
|
98
|
+
function getServiceConfig(options = {}) {
|
|
99
|
+
const { defaultPort = 3000, defaultLogLevel = 'info' } = options;
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
PORT: optionalNumberEnv('PORT', defaultPort),
|
|
103
|
+
LOG_LEVEL: optionalEnv('LOG_LEVEL', defaultLogLevel),
|
|
104
|
+
SERVICE_NAME: optionalEnv('SERVICE_NAME', 'unknown-service'),
|
|
105
|
+
NODE_ENV: optionalEnv('NODE_ENV', 'development')
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get infrastructure health configuration with defaults
|
|
111
|
+
* @returns {Object} Infrastructure health config
|
|
112
|
+
*/
|
|
113
|
+
function getInfrastructureHealthConfig() {
|
|
114
|
+
return {
|
|
115
|
+
queueName: optionalEnv('INFRASTRUCTURE_HEALTH_QUEUE', 'infrastructure.health.checks'),
|
|
116
|
+
publishInterval: optionalNumberEnv('INFRASTRUCTURE_HEALTH_PUBLISH_INTERVAL', 5000),
|
|
117
|
+
waitMaxTime: optionalNumberEnv('INFRASTRUCTURE_HEALTH_WAIT_MAX_TIME', 300000),
|
|
118
|
+
waitCheckInterval: optionalNumberEnv('INFRASTRUCTURE_HEALTH_WAIT_CHECK_INTERVAL', 5000),
|
|
119
|
+
healthCheckTimeout: optionalNumberEnv('INFRASTRUCTURE_HEALTH_TIMEOUT', 15000),
|
|
120
|
+
redisKeyTTL: optionalNumberEnv('INFRASTRUCTURE_HEALTH_REDIS_TTL', 30),
|
|
121
|
+
cleanupInterval: optionalNumberEnv('INFRASTRUCTURE_HEALTH_CLEANUP_INTERVAL', 10000)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
requireEnv,
|
|
127
|
+
optionalEnv,
|
|
128
|
+
optionalNumberEnv,
|
|
129
|
+
getCriticalConfig,
|
|
130
|
+
getOptionalInfraConfig,
|
|
131
|
+
getServiceConfig,
|
|
132
|
+
getInfrastructureHealthConfig
|
|
133
|
+
};
|
|
134
|
+
|