@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 +131 -5
- package/handlers/routers/health.js +124 -70
- package/package.json +5 -5
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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
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:',
|
|
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
|
-
|
|
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
|
|
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
|
|
296
|
-
testDoc,
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
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.
|
|
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.
|
|
26
|
-
"@friggframework/prettier-config": "2.0.0--canary.427.
|
|
27
|
-
"@friggframework/test": "2.0.0--canary.427.
|
|
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": "
|
|
59
|
+
"gitHead": "68e753a98bf315deb2004e4f5c6ab58495e04d2b"
|
|
60
60
|
}
|