@friggframework/core 2.0.0--canary.427.c8ff14b.0 → 2.0.0--canary.427.68e753a.0

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
 
@@ -15,6 +15,7 @@ const validateApiKey = (req, res, 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
21
  message: 'Unauthorized',
@@ -149,69 +150,6 @@ const createTestEncryptionModel = () => {
149
150
  );
150
151
  };
151
152
 
152
- const createTestDocument = async (TestModel) => {
153
- const testData = {
154
- testSecret: 'This is a secret value that should be encrypted',
155
- normalField: 'This is a normal field that should not be encrypted',
156
- nestedSecret: {
157
- value: 'This is a nested secret that should be encrypted',
158
- },
159
- };
160
-
161
- console.log('🔧 Creating test document with encryption...');
162
- const testDoc = new TestModel(testData);
163
-
164
- // Add timeout and detailed error logging
165
- const savePromise = testDoc.save();
166
- const timeoutPromise = new Promise((_, reject) => {
167
- setTimeout(
168
- () =>
169
- reject(new Error('Save operation timed out after 30 seconds')),
170
- 30000
171
- );
172
- });
173
-
174
- try {
175
- console.log('💾 Attempting to save document...');
176
- const startTime = Date.now();
177
-
178
- await Promise.race([savePromise, timeoutPromise]);
179
-
180
- const duration = Date.now() - startTime;
181
- console.log(`✅ Document saved successfully in ${duration}ms`);
182
-
183
- return { testDoc, testData };
184
- } catch (error) {
185
- console.error('❌ Save operation failed:', {
186
- errorName: error.name,
187
- errorMessage: error.message,
188
- errorStack: error.stack,
189
- mongooseConnectionState: testDoc.db.readyState,
190
- modelName: TestModel.modelName,
191
- });
192
-
193
- // Try to get more details about the connection
194
- if (mongoose.connection && mongoose.connection.db) {
195
- try {
196
- const admin = mongoose.connection.db.admin();
197
- const serverStatus = await admin.serverStatus();
198
- console.log('📊 DocumentDB Server Status:', {
199
- version: serverStatus.version,
200
- uptime: serverStatus.uptime,
201
- connections: serverStatus.connections,
202
- });
203
- } catch (statusError) {
204
- console.error(
205
- '❌ Could not get server status:',
206
- statusError.message
207
- );
208
- }
209
- }
210
-
211
- throw error;
212
- }
213
- };
214
-
215
153
  const verifyDecryption = (retrievedDoc, originalData) => {
216
154
  return (
217
155
  retrievedDoc &&
@@ -285,18 +223,95 @@ const evaluateEncryptionTestResults = (decryptionWorks, encryptionResults) => {
285
223
  };
286
224
  };
287
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
+
288
235
  const testEncryption = async () => {
236
+ // eslint-disable-next-line no-console
237
+ console.log('Starting encryption test');
289
238
  const TestModel = createTestEncryptionModel();
290
- const { testDoc, testData } = await createTestDocument(TestModel);
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
+
252
+ try {
253
+ // eslint-disable-next-line no-console
254
+ console.log('Attempting to save document with encryption...');
255
+ const startTime = Date.now();
256
+
257
+ await withTimeout(
258
+ testDoc.save(),
259
+ 30000,
260
+ 'Save operation timed out after 30 seconds'
261
+ );
262
+
263
+ const duration = Date.now() - startTime;
264
+ // eslint-disable-next-line no-console
265
+ console.log(`Test document saved successfully in ${duration}ms`);
266
+ } catch (error) {
267
+ // eslint-disable-next-line no-console
268
+ console.error('Save operation failed:', {
269
+ errorName: error.name,
270
+ errorMessage: error.message,
271
+ errorStack: error.stack,
272
+ mongooseConnectionState: testDoc.db.readyState,
273
+ modelName: TestModel.modelName,
274
+ });
275
+
276
+ // Try to get more details about the connection
277
+ if (mongoose.connection && mongoose.connection.db) {
278
+ try {
279
+ const admin = mongoose.connection.db.admin();
280
+ const serverStatus = await admin.serverStatus();
281
+ // eslint-disable-next-line no-console
282
+ console.log('DocumentDB Server Status:', {
283
+ version: serverStatus.version,
284
+ uptime: serverStatus.uptime,
285
+ connections: serverStatus.connections,
286
+ });
287
+ } catch (statusError) {
288
+ // eslint-disable-next-line no-console
289
+ console.error(
290
+ 'Could not get server status:',
291
+ statusError.message
292
+ );
293
+ }
294
+ }
295
+
296
+ throw error;
297
+ }
291
298
 
292
299
  try {
293
- const retrievedDoc = await TestModel.findById(testDoc._id);
300
+ const retrievedDoc = await withTimeout(
301
+ TestModel.findById(testDoc._id),
302
+ 5000,
303
+ 'Find operation timed out'
304
+ );
305
+ // eslint-disable-next-line no-console
306
+ console.log('Test document retrieved');
294
307
  const decryptionWorks = verifyDecryption(retrievedDoc, testData);
295
- const encryptionResults = await verifyEncryptionInDatabase(
296
- testDoc,
297
- testData,
298
- TestModel
308
+ const encryptionResults = await withTimeout(
309
+ verifyEncryptionInDatabase(testDoc, testData, TestModel),
310
+ 5000,
311
+ 'Database verification timed out'
299
312
  );
313
+ // eslint-disable-next-line no-console
314
+ console.log('Encryption verification completed');
300
315
 
301
316
  const evaluation = evaluateEncryptionTestResults(
302
317
  decryptionWorks,
@@ -308,7 +323,13 @@ const testEncryption = async () => {
308
323
  encryptionWorks: decryptionWorks,
309
324
  };
310
325
  } finally {
311
- await TestModel.deleteOne({ _id: testDoc._id });
326
+ await withTimeout(
327
+ TestModel.deleteOne({ _id: testDoc._id }),
328
+ 5000,
329
+ 'Delete operation timed out'
330
+ );
331
+ // eslint-disable-next-line no-console
332
+ console.log('Test document deleted');
312
333
  }
313
334
  };
314
335
 
@@ -316,6 +337,12 @@ const checkEncryptionHealth = async () => {
316
337
  const config = getEncryptionConfiguration();
317
338
 
318
339
  if (config.isBypassed || config.mode === 'none') {
340
+ // eslint-disable-next-line no-console
341
+ console.log('Encryption check bypassed:', {
342
+ stage: config.stage,
343
+ mode: config.mode,
344
+ });
345
+
319
346
  const testResult = config.isBypassed
320
347
  ? 'Encryption bypassed for this stage'
321
348
  : 'No encryption keys configured';
@@ -434,6 +461,8 @@ router.get('/health', async (_req, res) => {
434
461
  });
435
462
 
436
463
  router.get('/health/detailed', async (_req, res) => {
464
+ // eslint-disable-next-line no-console
465
+ console.log('Starting detailed health check');
437
466
  const startTime = Date.now();
438
467
  const response = buildHealthCheckResponse(startTime);
439
468
 
@@ -443,12 +472,16 @@ router.get('/health/detailed', async (_req, res) => {
443
472
  if (!dbState.isConnected) {
444
473
  response.status = 'unhealthy';
445
474
  }
475
+ // eslint-disable-next-line no-console
476
+ console.log('Database check completed:', response.checks.database);
446
477
  } catch (error) {
447
478
  response.checks.database = {
448
479
  status: 'unhealthy',
449
480
  error: error.message,
450
481
  };
451
482
  response.status = 'unhealthy';
483
+ // eslint-disable-next-line no-console
484
+ console.log('Database check error:', error.message);
452
485
  }
453
486
 
454
487
  try {
@@ -456,12 +489,16 @@ router.get('/health/detailed', async (_req, res) => {
456
489
  if (response.checks.encryption.status === 'unhealthy') {
457
490
  response.status = 'unhealthy';
458
491
  }
492
+ // eslint-disable-next-line no-console
493
+ console.log('Encryption check completed:', response.checks.encryption);
459
494
  } catch (error) {
460
495
  response.checks.encryption = {
461
496
  status: 'unhealthy',
462
497
  error: error.message,
463
498
  };
464
499
  response.status = 'unhealthy';
500
+ // eslint-disable-next-line no-console
501
+ console.log('Encryption check error:', error.message);
465
502
  }
466
503
 
467
504
  const { apiStatuses, allReachable } = await checkExternalAPIs();
@@ -469,15 +506,24 @@ router.get('/health/detailed', async (_req, res) => {
469
506
  if (!allReachable) {
470
507
  response.status = 'unhealthy';
471
508
  }
509
+ // eslint-disable-next-line no-console
510
+ console.log('External APIs check completed:', response.checks.externalApis);
472
511
 
473
512
  try {
474
513
  response.checks.integrations = checkIntegrations();
514
+ // eslint-disable-next-line no-console
515
+ console.log(
516
+ 'Integrations check completed:',
517
+ response.checks.integrations
518
+ );
475
519
  } catch (error) {
476
520
  response.checks.integrations = {
477
521
  status: 'unhealthy',
478
522
  error: error.message,
479
523
  };
480
524
  response.status = 'unhealthy';
525
+ // eslint-disable-next-line no-console
526
+ console.log('Integrations check error:', error.message);
481
527
  }
482
528
 
483
529
  response.responseTime = response.calculateResponseTime();
@@ -485,6 +531,14 @@ router.get('/health/detailed', async (_req, res) => {
485
531
 
486
532
  const statusCode = response.status === 'healthy' ? 200 : 503;
487
533
  res.status(statusCode).json(response);
534
+
535
+ // eslint-disable-next-line no-console
536
+ console.log(
537
+ 'Final health status:',
538
+ response.status,
539
+ 'Response time:',
540
+ response.responseTime
541
+ );
488
542
  });
489
543
 
490
544
  router.get('/health/live', (_req, res) => {
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--canary.427.c8ff14b.0",
4
+ "version": "2.0.0--canary.427.68e753a.0",
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--canary.427.c8ff14b.0",
26
- "@friggframework/prettier-config": "2.0.0--canary.427.c8ff14b.0",
27
- "@friggframework/test": "2.0.0--canary.427.c8ff14b.0",
25
+ "@friggframework/eslint-config": "2.0.0--canary.427.68e753a.0",
26
+ "@friggframework/prettier-config": "2.0.0--canary.427.68e753a.0",
27
+ "@friggframework/test": "2.0.0--canary.427.68e753a.0",
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": "c8ff14b4f6c1a21bd6c254ad12f1c9963b0374db"
59
+ "gitHead": "68e753a98bf315deb2004e4f5c6ab58495e04d2b"
60
60
  }