@friggframework/core 2.0.0-next.38 → 2.0.0-next.39

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/database/mongo.js CHANGED
@@ -5,14 +5,16 @@
5
5
  const { Encrypt } = require('../encrypt');
6
6
  const { mongoose } = require('./mongoose');
7
7
  const { debug, flushDebugLog } = require('../logs');
8
+ const { findNearestBackendPackageJson } = require('../utils');
9
+ const path = require('path');
10
+ const fs = require('fs');
8
11
 
9
12
  mongoose.plugin(Encrypt);
10
13
  mongoose.set('applyPluginsToDiscriminators', true); // Needed for LHEncrypt
11
14
 
12
- // Buffering means mongoose will queue up operations if it gets
13
- // With serverless, better to fail fast if not connected.
14
- // disconnected from MongoDB and send them when it reconnects.
15
- const mongoConfig = {
15
+ // Load app definition to check for DocumentDB configuration
16
+ let appDefinition = {};
17
+ let mongoConfig = {
16
18
  useNewUrlParser: true,
17
19
  bufferCommands: false, // Disable mongoose buffering
18
20
  autoCreate: false, // Disable because auto creation does not work without buffering
@@ -28,9 +30,133 @@ const connectToDatabase = async () => {
28
30
  return;
29
31
  }
30
32
 
33
+ console.log('🔗 Connecting to database...');
34
+
35
+ // Load appDefinition inside the function
36
+ try {
37
+ console.log(
38
+ '🔍 Loading app definition for DocumentDB configuration...'
39
+ );
40
+
41
+ const backendPath = findNearestBackendPackageJson();
42
+ if (!backendPath) {
43
+ throw new Error('Could not find backend package.json');
44
+ }
45
+
46
+ const backendDir = path.dirname(backendPath);
47
+ const backendFilePath = path.join(backendDir, 'index.js');
48
+ if (!fs.existsSync(backendFilePath)) {
49
+ throw new Error('Could not find index.js');
50
+ }
51
+
52
+ const backend = require(backendFilePath);
53
+ appDefinition = backend.Definition;
54
+
55
+ console.log('📁 AppDefinition content:', JSON.stringify(appDefinition));
56
+
57
+ // Add DocumentDB TLS configuration if enabled
58
+ if (appDefinition.database?.documentDB?.enable === true) {
59
+ console.log('📄 DocumentDB configuration detected, enabling TLS');
60
+ console.log('📁 Current working directory:', process.cwd());
61
+ console.log(
62
+ '📋 App definition database config:',
63
+ JSON.stringify(appDefinition.database, null, 2)
64
+ );
65
+
66
+ mongoConfig.tls = true;
67
+
68
+ // Set TLS CA file path if specified
69
+ if (appDefinition.database.documentDB.tlsCAFile) {
70
+ const tlsCAFile = appDefinition.database.documentDB.tlsCAFile;
71
+
72
+ // Basic safety: reject obviously dangerous paths
73
+ if (tlsCAFile.includes('..') || path.isAbsolute(tlsCAFile)) {
74
+ console.warn(
75
+ '⚠️ Rejecting potentially unsafe tlsCAFile path:',
76
+ tlsCAFile
77
+ );
78
+ } else {
79
+ const tlsCAFilePath = path.resolve(
80
+ process.cwd(),
81
+ tlsCAFile
82
+ );
83
+
84
+ console.log('📄 DocumentDB TLS CA file configured:');
85
+ console.log(' 📎 Original path:', tlsCAFile);
86
+ console.log(' 📎 Resolved path:', tlsCAFilePath);
87
+ console.log(
88
+ ' 📄 File exists:',
89
+ fs.existsSync(tlsCAFilePath)
90
+ );
91
+
92
+ // Only set tlsCAFile if the file actually exists
93
+ if (fs.existsSync(tlsCAFilePath)) {
94
+ mongoConfig.tlsCAFile = tlsCAFilePath;
95
+ console.log('✅ TLS CA file configured successfully');
96
+ } else {
97
+ throw new Error(
98
+ `TLS CA file not found at ${tlsCAFilePath}`
99
+ );
100
+ }
101
+
102
+ // Debug directory listing (only in development)
103
+ if (process.env.NODE_ENV !== 'production') {
104
+ try {
105
+ console.log('📁 Current directory contents:');
106
+ fs.readdirSync(process.cwd()).forEach((item) => {
107
+ const stats = fs.statSync(
108
+ path.join(process.cwd(), item)
109
+ );
110
+ console.log(
111
+ ` ${
112
+ stats.isDirectory() ? '📁' : '📄'
113
+ } ${item}`
114
+ );
115
+ });
116
+
117
+ const securityDir = path.join(
118
+ process.cwd(),
119
+ 'security'
120
+ );
121
+ if (fs.existsSync(securityDir)) {
122
+ console.log('📁 Security directory contents:');
123
+ fs.readdirSync(securityDir).forEach((item) => {
124
+ console.log(` 📄 ${item}`);
125
+ });
126
+ } else {
127
+ console.log(
128
+ '❌ Security directory does not exist at:',
129
+ securityDir
130
+ );
131
+ }
132
+ } catch (error) {
133
+ console.log(
134
+ '❌ Error listing directory contents:',
135
+ error.message
136
+ );
137
+ }
138
+ }
139
+ }
140
+ }
141
+ } else {
142
+ console.log(
143
+ '📄 DocumentDB not enabled, using standard MongoDB configuration'
144
+ );
145
+ }
146
+ } catch (error) {
147
+ console.error('❌ Error loading app definition:', error.message);
148
+ debug(
149
+ 'Could not load app definition for DocumentDB configuration:',
150
+ error.message
151
+ );
152
+ }
153
+
154
+ console.log('🔗 MongoDB URI:', process.env.MONGO_URI ? 'SET' : 'NOT SET');
155
+ console.log('🔧 Final mongoConfig:', JSON.stringify(mongoConfig, null, 2));
156
+
31
157
  debug('=> using new database connection');
32
158
  await mongoose.connect(process.env.MONGO_URI, mongoConfig);
33
- debug('Connection state:', mongoose.STATES[mongoose.connection.readyState]);
159
+ debug('Connection state:', mongoose.STATES[mongoose.connection.readyState]);
34
160
  mongoose.connection.on('error', (error) => flushDebugLog(error));
35
161
  };
36
162
 
@@ -9,18 +9,19 @@ const router = Router();
9
9
 
10
10
  const validateApiKey = (req, res, next) => {
11
11
  const apiKey = req.headers['x-api-key'];
12
-
12
+
13
13
  if (req.path === '/health') {
14
14
  return next();
15
15
  }
16
-
16
+
17
17
  if (!apiKey || apiKey !== process.env.HEALTH_API_KEY) {
18
+ console.error('Unauthorized access attempt to health endpoint');
18
19
  return res.status(401).json({
19
20
  status: 'error',
20
- message: 'Unauthorized'
21
+ message: 'Unauthorized',
21
22
  });
22
23
  }
23
-
24
+
24
25
  next();
25
26
  };
26
27
 
@@ -30,7 +31,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
30
31
  return new Promise((resolve) => {
31
32
  const protocol = url.startsWith('https:') ? https : http;
32
33
  const startTime = Date.now();
33
-
34
+
34
35
  try {
35
36
  const request = protocol.get(url, { timeout }, (res) => {
36
37
  const responseTime = Date.now() - startTime;
@@ -38,7 +39,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
38
39
  status: 'healthy',
39
40
  statusCode: res.statusCode,
40
41
  responseTime,
41
- reachable: res.statusCode < 500
42
+ reachable: res.statusCode < 500,
42
43
  });
43
44
  });
44
45
 
@@ -47,7 +48,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
47
48
  status: 'unhealthy',
48
49
  error: error.message,
49
50
  responseTime: Date.now() - startTime,
50
- reachable: false
51
+ reachable: false,
51
52
  });
52
53
  });
53
54
 
@@ -57,7 +58,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
57
58
  status: 'timeout',
58
59
  error: 'Request timeout',
59
60
  responseTime: timeout,
60
- reachable: false
61
+ reachable: false,
61
62
  });
62
63
  });
63
64
  } catch (error) {
@@ -65,7 +66,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
65
66
  status: 'error',
66
67
  error: error.message,
67
68
  responseTime: Date.now() - startTime,
68
- reachable: false
69
+ reachable: false,
69
70
  });
70
71
  }
71
72
  });
@@ -76,14 +77,14 @@ const getDatabaseState = () => {
76
77
  0: 'disconnected',
77
78
  1: 'connected',
78
79
  2: 'connecting',
79
- 3: 'disconnecting'
80
+ 3: 'disconnecting',
80
81
  };
81
82
  const readyState = mongoose.connection.readyState;
82
-
83
+
83
84
  return {
84
85
  readyState,
85
86
  stateName: stateMap[readyState],
86
- isConnected: readyState === 1
87
+ isConnected: readyState === 1,
87
88
  };
88
89
  };
89
90
 
@@ -91,7 +92,7 @@ const checkDatabaseHealth = async () => {
91
92
  const { stateName, isConnected } = getDatabaseState();
92
93
  const result = {
93
94
  status: isConnected ? 'healthy' : 'unhealthy',
94
- state: stateName
95
+ state: stateName,
95
96
  };
96
97
 
97
98
  if (isConnected) {
@@ -104,19 +105,20 @@ const checkDatabaseHealth = async () => {
104
105
  };
105
106
 
106
107
  const getEncryptionConfiguration = () => {
107
- const { STAGE, BYPASS_ENCRYPTION_STAGE, KMS_KEY_ARN, AES_KEY_ID } = process.env;
108
-
108
+ const { STAGE, BYPASS_ENCRYPTION_STAGE, KMS_KEY_ARN, AES_KEY_ID } =
109
+ process.env;
110
+
109
111
  const defaultBypassStages = ['dev', 'test', 'local'];
110
112
  const useEnv = BYPASS_ENCRYPTION_STAGE !== undefined;
111
113
  const bypassStages = useEnv
112
114
  ? BYPASS_ENCRYPTION_STAGE.split(',').map((s) => s.trim())
113
115
  : defaultBypassStages;
114
-
116
+
115
117
  const isBypassed = bypassStages.includes(STAGE);
116
118
  const hasAES = AES_KEY_ID && AES_KEY_ID.trim() !== '';
117
119
  const hasKMS = KMS_KEY_ARN && KMS_KEY_ARN.trim() !== '' && !hasAES;
118
120
  const mode = hasAES ? 'aes' : hasKMS ? 'kms' : 'none';
119
-
121
+
120
122
  return {
121
123
  stage: STAGE || null,
122
124
  isBypassed,
@@ -128,41 +130,33 @@ const getEncryptionConfiguration = () => {
128
130
 
129
131
  const createTestEncryptionModel = () => {
130
132
  const { Encrypt } = require('./../../encrypt');
131
-
132
- const testSchema = new mongoose.Schema({
133
- testSecret: { type: String, lhEncrypt: true },
134
- normalField: { type: String },
135
- nestedSecret: {
136
- value: { type: String, lhEncrypt: true }
137
- }
138
- }, { timestamps: false });
139
133
 
140
- testSchema.plugin(Encrypt);
141
-
142
- return mongoose.models.TestEncryption ||
143
- mongoose.model('TestEncryption', testSchema);
144
- };
134
+ const testSchema = new mongoose.Schema(
135
+ {
136
+ testSecret: { type: String, lhEncrypt: true },
137
+ normalField: { type: String },
138
+ nestedSecret: {
139
+ value: { type: String, lhEncrypt: true },
140
+ },
141
+ },
142
+ { timestamps: false }
143
+ );
145
144
 
146
- const createTestDocument = async (TestModel) => {
147
- const testData = {
148
- testSecret: 'This is a secret value that should be encrypted',
149
- normalField: 'This is a normal field that should not be encrypted',
150
- nestedSecret: {
151
- value: 'This is a nested secret that should be encrypted'
152
- }
153
- };
145
+ testSchema.plugin(Encrypt);
154
146
 
155
- const testDoc = new TestModel(testData);
156
- await testDoc.save();
157
-
158
- return { testDoc, testData };
147
+ return (
148
+ mongoose.models.TestEncryption ||
149
+ mongoose.model('TestEncryption', testSchema)
150
+ );
159
151
  };
160
152
 
161
153
  const verifyDecryption = (retrievedDoc, originalData) => {
162
- return retrievedDoc &&
154
+ return (
155
+ retrievedDoc &&
163
156
  retrievedDoc.testSecret === originalData.testSecret &&
164
157
  retrievedDoc.normalField === originalData.normalField &&
165
- retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value;
158
+ retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value
159
+ );
166
160
  };
167
161
 
168
162
  const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
@@ -171,84 +165,144 @@ const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
171
165
  .collection(collectionName)
172
166
  .findOne({ _id: testDoc._id });
173
167
 
174
- const secretIsEncrypted = rawDoc &&
175
- typeof rawDoc.testSecret === 'string' &&
176
- rawDoc.testSecret.includes(':') &&
168
+ const secretIsEncrypted =
169
+ rawDoc &&
170
+ typeof rawDoc.testSecret === 'string' &&
171
+ rawDoc.testSecret.includes(':') &&
177
172
  rawDoc.testSecret !== originalData.testSecret;
178
-
179
- const nestedIsEncrypted = rawDoc?.nestedSecret?.value &&
173
+
174
+ const nestedIsEncrypted =
175
+ rawDoc?.nestedSecret?.value &&
180
176
  typeof rawDoc.nestedSecret.value === 'string' &&
181
- rawDoc.nestedSecret.value.includes(':') &&
177
+ rawDoc.nestedSecret.value.includes(':') &&
182
178
  rawDoc.nestedSecret.value !== originalData.nestedSecret.value;
183
-
184
- const normalNotEncrypted = rawDoc &&
185
- rawDoc.normalField === originalData.normalField;
179
+
180
+ const normalNotEncrypted =
181
+ rawDoc && rawDoc.normalField === originalData.normalField;
186
182
 
187
183
  return {
188
184
  secretIsEncrypted,
189
185
  nestedIsEncrypted,
190
- normalNotEncrypted
186
+ normalNotEncrypted,
191
187
  };
192
188
  };
193
189
 
194
190
  const evaluateEncryptionTestResults = (decryptionWorks, encryptionResults) => {
195
- const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } = encryptionResults;
196
-
197
- if (decryptionWorks && secretIsEncrypted && nestedIsEncrypted && normalNotEncrypted) {
191
+ const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } =
192
+ encryptionResults;
193
+
194
+ if (
195
+ decryptionWorks &&
196
+ secretIsEncrypted &&
197
+ nestedIsEncrypted &&
198
+ normalNotEncrypted
199
+ ) {
198
200
  return {
199
201
  status: 'enabled',
200
- testResult: 'Encryption and decryption verified successfully'
202
+ testResult: 'Encryption and decryption verified successfully',
201
203
  };
202
204
  }
203
-
205
+
204
206
  if (decryptionWorks && (!secretIsEncrypted || !nestedIsEncrypted)) {
205
207
  return {
206
208
  status: 'unhealthy',
207
- testResult: 'Fields are not being encrypted in database'
209
+ testResult: 'Fields are not being encrypted in database',
208
210
  };
209
211
  }
210
-
212
+
211
213
  if (decryptionWorks && !normalNotEncrypted) {
212
214
  return {
213
215
  status: 'unhealthy',
214
- testResult: 'Normal fields are being incorrectly encrypted'
216
+ testResult: 'Normal fields are being incorrectly encrypted',
215
217
  };
216
218
  }
217
-
219
+
218
220
  return {
219
221
  status: 'unhealthy',
220
- testResult: 'Decryption failed or data mismatch'
222
+ testResult: 'Decryption failed or data mismatch',
221
223
  };
222
224
  };
223
225
 
226
+ const withTimeout = (promise, ms, errorMessage) => {
227
+ return Promise.race([
228
+ promise,
229
+ new Promise((_, reject) =>
230
+ setTimeout(() => reject(new Error(errorMessage)), ms)
231
+ ),
232
+ ]);
233
+ };
234
+
224
235
  const testEncryption = async () => {
236
+ // eslint-disable-next-line no-console
237
+ console.log('Starting encryption test');
225
238
  const TestModel = createTestEncryptionModel();
226
- const { testDoc, testData } = await createTestDocument(TestModel);
227
-
239
+ // eslint-disable-next-line no-console
240
+ console.log('Test model created');
241
+
242
+ const testData = {
243
+ testSecret: 'This is a secret value that should be encrypted',
244
+ normalField: 'This is a normal field that should not be encrypted',
245
+ nestedSecret: {
246
+ value: 'This is a nested secret that should be encrypted',
247
+ },
248
+ };
249
+
250
+ const testDoc = new TestModel(testData);
251
+ await withTimeout(testDoc.save(), 5000, 'Save operation timed out');
252
+ // eslint-disable-next-line no-console
253
+ console.log('Test document saved');
254
+
228
255
  try {
229
- const retrievedDoc = await TestModel.findById(testDoc._id);
256
+ const retrievedDoc = await withTimeout(
257
+ TestModel.findById(testDoc._id),
258
+ 5000,
259
+ 'Find operation timed out'
260
+ );
261
+ // eslint-disable-next-line no-console
262
+ console.log('Test document retrieved');
230
263
  const decryptionWorks = verifyDecryption(retrievedDoc, testData);
231
- const encryptionResults = await verifyEncryptionInDatabase(testDoc, testData, TestModel);
232
-
233
- const evaluation = evaluateEncryptionTestResults(decryptionWorks, encryptionResults);
234
-
264
+ const encryptionResults = await withTimeout(
265
+ verifyEncryptionInDatabase(testDoc, testData, TestModel),
266
+ 5000,
267
+ 'Database verification timed out'
268
+ );
269
+ // eslint-disable-next-line no-console
270
+ console.log('Encryption verification completed');
271
+
272
+ const evaluation = evaluateEncryptionTestResults(
273
+ decryptionWorks,
274
+ encryptionResults
275
+ );
276
+
235
277
  return {
236
278
  ...evaluation,
237
- encryptionWorks: decryptionWorks
279
+ encryptionWorks: decryptionWorks,
238
280
  };
239
281
  } finally {
240
- await TestModel.deleteOne({ _id: testDoc._id });
282
+ await withTimeout(
283
+ TestModel.deleteOne({ _id: testDoc._id }),
284
+ 5000,
285
+ 'Delete operation timed out'
286
+ );
287
+ // eslint-disable-next-line no-console
288
+ console.log('Test document deleted');
241
289
  }
242
290
  };
243
291
 
244
292
  const checkEncryptionHealth = async () => {
245
293
  const config = getEncryptionConfiguration();
246
-
294
+
247
295
  if (config.isBypassed || config.mode === 'none') {
248
- const testResult = config.isBypassed
249
- ? 'Encryption bypassed for this stage'
296
+ // eslint-disable-next-line no-console
297
+ console.log('Encryption check bypassed:', {
298
+ stage: config.stage,
299
+ mode: config.mode,
300
+ });
301
+
302
+ const testResult = config.isBypassed
303
+ ? 'Encryption bypassed for this stage'
250
304
  : 'No encryption keys configured';
251
-
305
+
252
306
  return {
253
307
  status: 'disabled',
254
308
  mode: config.mode,
@@ -258,14 +312,14 @@ const checkEncryptionHealth = async () => {
258
312
  encryptionWorks: false,
259
313
  debug: {
260
314
  hasKMS: config.hasKMS,
261
- hasAES: config.hasAES
262
- }
315
+ hasAES: config.hasAES,
316
+ },
263
317
  };
264
318
  }
265
319
 
266
320
  try {
267
321
  const testResults = await testEncryption();
268
-
322
+
269
323
  return {
270
324
  ...testResults,
271
325
  mode: config.mode,
@@ -273,8 +327,8 @@ const checkEncryptionHealth = async () => {
273
327
  stage: config.stage,
274
328
  debug: {
275
329
  hasKMS: config.hasKMS,
276
- hasAES: config.hasAES
277
- }
330
+ hasAES: config.hasAES,
331
+ },
278
332
  };
279
333
  } catch (error) {
280
334
  return {
@@ -286,8 +340,8 @@ const checkEncryptionHealth = async () => {
286
340
  encryptionWorks: false,
287
341
  debug: {
288
342
  hasKMS: config.hasKMS,
289
- hasAES: config.hasAES
290
- }
343
+ hasAES: config.hasAES,
344
+ },
291
345
  };
292
346
  }
293
347
  };
@@ -295,25 +349,28 @@ const checkEncryptionHealth = async () => {
295
349
  const checkExternalAPIs = async () => {
296
350
  const apis = [
297
351
  { name: 'github', url: 'https://api.github.com/status' },
298
- { name: 'npm', url: 'https://registry.npmjs.org' }
352
+ { name: 'npm', url: 'https://registry.npmjs.org' },
299
353
  ];
300
354
 
301
355
  const results = await Promise.all(
302
- apis.map(api =>
303
- checkExternalAPI(api.url).then(result => ({ name: api.name, ...result }))
356
+ apis.map((api) =>
357
+ checkExternalAPI(api.url).then((result) => ({
358
+ name: api.name,
359
+ ...result,
360
+ }))
304
361
  )
305
362
  );
306
-
363
+
307
364
  const apiStatuses = {};
308
365
  let allReachable = true;
309
-
366
+
310
367
  results.forEach(({ name, ...checkResult }) => {
311
368
  apiStatuses[name] = checkResult;
312
369
  if (!checkResult.reachable) {
313
370
  allReachable = false;
314
371
  }
315
372
  });
316
-
373
+
317
374
  return { apiStatuses, allReachable };
318
375
  };
319
376
 
@@ -321,7 +378,7 @@ const checkIntegrations = () => {
321
378
  const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
322
379
  ? moduleFactory.moduleTypes
323
380
  : [];
324
-
381
+
325
382
  const integrationTypes = Array.isArray(integrationFactory.integrationTypes)
326
383
  ? integrationFactory.integrationTypes
327
384
  : [];
@@ -345,7 +402,7 @@ const buildHealthCheckResponse = (startTime) => {
345
402
  status: 'healthy',
346
403
  timestamp: new Date().toISOString(),
347
404
  checks: {},
348
- calculateResponseTime: () => Date.now() - startTime
405
+ calculateResponseTime: () => Date.now() - startTime,
349
406
  };
350
407
  };
351
408
 
@@ -353,13 +410,15 @@ router.get('/health', async (_req, res) => {
353
410
  const status = {
354
411
  status: 'ok',
355
412
  timestamp: new Date().toISOString(),
356
- service: 'frigg-core-api'
413
+ service: 'frigg-core-api',
357
414
  };
358
415
 
359
416
  res.status(200).json(status);
360
417
  });
361
418
 
362
419
  router.get('/health/detailed', async (_req, res) => {
420
+ // eslint-disable-next-line no-console
421
+ console.log('Starting detailed health check');
363
422
  const startTime = Date.now();
364
423
  const response = buildHealthCheckResponse(startTime);
365
424
 
@@ -369,12 +428,16 @@ router.get('/health/detailed', async (_req, res) => {
369
428
  if (!dbState.isConnected) {
370
429
  response.status = 'unhealthy';
371
430
  }
431
+ // eslint-disable-next-line no-console
432
+ console.log('Database check completed:', response.checks.database);
372
433
  } catch (error) {
373
434
  response.checks.database = {
374
435
  status: 'unhealthy',
375
- error: error.message
436
+ error: error.message,
376
437
  };
377
438
  response.status = 'unhealthy';
439
+ // eslint-disable-next-line no-console
440
+ console.log('Database check error:', error.message);
378
441
  }
379
442
 
380
443
  try {
@@ -382,12 +445,16 @@ router.get('/health/detailed', async (_req, res) => {
382
445
  if (response.checks.encryption.status === 'unhealthy') {
383
446
  response.status = 'unhealthy';
384
447
  }
448
+ // eslint-disable-next-line no-console
449
+ console.log('Encryption check completed:', response.checks.encryption);
385
450
  } catch (error) {
386
451
  response.checks.encryption = {
387
452
  status: 'unhealthy',
388
- error: error.message
453
+ error: error.message,
389
454
  };
390
455
  response.status = 'unhealthy';
456
+ // eslint-disable-next-line no-console
457
+ console.log('Encryption check error:', error.message);
391
458
  }
392
459
 
393
460
  const { apiStatuses, allReachable } = await checkExternalAPIs();
@@ -395,15 +462,24 @@ router.get('/health/detailed', async (_req, res) => {
395
462
  if (!allReachable) {
396
463
  response.status = 'unhealthy';
397
464
  }
465
+ // eslint-disable-next-line no-console
466
+ console.log('External APIs check completed:', response.checks.externalApis);
398
467
 
399
468
  try {
400
469
  response.checks.integrations = checkIntegrations();
470
+ // eslint-disable-next-line no-console
471
+ console.log(
472
+ 'Integrations check completed:',
473
+ response.checks.integrations
474
+ );
401
475
  } catch (error) {
402
476
  response.checks.integrations = {
403
477
  status: 'unhealthy',
404
- error: error.message
478
+ error: error.message,
405
479
  };
406
480
  response.status = 'unhealthy';
481
+ // eslint-disable-next-line no-console
482
+ console.log('Integrations check error:', error.message);
407
483
  }
408
484
 
409
485
  response.responseTime = response.calculateResponseTime();
@@ -411,19 +487,27 @@ router.get('/health/detailed', async (_req, res) => {
411
487
 
412
488
  const statusCode = response.status === 'healthy' ? 200 : 503;
413
489
  res.status(statusCode).json(response);
490
+
491
+ // eslint-disable-next-line no-console
492
+ console.log(
493
+ 'Final health status:',
494
+ response.status,
495
+ 'Response time:',
496
+ response.responseTime
497
+ );
414
498
  });
415
499
 
416
500
  router.get('/health/live', (_req, res) => {
417
501
  res.status(200).json({
418
502
  status: 'alive',
419
- timestamp: new Date().toISOString()
503
+ timestamp: new Date().toISOString(),
420
504
  });
421
505
  });
422
506
 
423
507
  router.get('/health/ready', async (_req, res) => {
424
508
  const dbState = getDatabaseState();
425
509
  const isDbReady = dbState.isConnected;
426
-
510
+
427
511
  let areModulesReady = false;
428
512
  try {
429
513
  const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
@@ -441,11 +525,11 @@ router.get('/health/ready', async (_req, res) => {
441
525
  timestamp: new Date().toISOString(),
442
526
  checks: {
443
527
  database: isDbReady,
444
- modules: areModulesReady
445
- }
528
+ modules: areModulesReady,
529
+ },
446
530
  });
447
531
  });
448
532
 
449
533
  const handler = createAppHandler('HTTP Event: Health', router);
450
534
 
451
- module.exports = { handler, router };
535
+ module.exports = { handler, router };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.38",
4
+ "version": "2.0.0-next.39",
5
5
  "dependencies": {
6
6
  "@hapi/boom": "^10.0.1",
7
7
  "aws-sdk": "^2.1200.0",
@@ -22,9 +22,9 @@
22
22
  "uuid": "^9.0.1"
23
23
  },
24
24
  "devDependencies": {
25
- "@friggframework/eslint-config": "2.0.0-next.38",
26
- "@friggframework/prettier-config": "2.0.0-next.38",
27
- "@friggframework/test": "2.0.0-next.38",
25
+ "@friggframework/eslint-config": "2.0.0-next.39",
26
+ "@friggframework/prettier-config": "2.0.0-next.39",
27
+ "@friggframework/test": "2.0.0-next.39",
28
28
  "@types/lodash": "4.17.15",
29
29
  "@typescript-eslint/eslint-plugin": "^8.0.0",
30
30
  "chai": "^4.3.6",
@@ -56,5 +56,5 @@
56
56
  "publishConfig": {
57
57
  "access": "public"
58
58
  },
59
- "gitHead": "a9c9c28fd9abdc8c96a38b8ab0fbb3cbf7d89960"
59
+ "gitHead": "38a7b885828dca923a6ea0d2e297d0048ba06822"
60
60
  }