@friggframework/core 2.0.0--canary.419.29d9541.0 → 2.0.0--canary.425.dd575ef.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 +5 -131
- package/handlers/routers/health.js +87 -141
- package/package.json +5 -5
package/database/mongo.js
CHANGED
|
@@ -5,16 +5,14 @@
|
|
|
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');
|
|
11
8
|
|
|
12
9
|
mongoose.plugin(Encrypt);
|
|
13
10
|
mongoose.set('applyPluginsToDiscriminators', true); // Needed for LHEncrypt
|
|
14
11
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
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 = {
|
|
18
16
|
useNewUrlParser: true,
|
|
19
17
|
bufferCommands: false, // Disable mongoose buffering
|
|
20
18
|
autoCreate: false, // Disable because auto creation does not work without buffering
|
|
@@ -30,133 +28,9 @@ const connectToDatabase = async () => {
|
|
|
30
28
|
return;
|
|
31
29
|
}
|
|
32
30
|
|
|
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
|
-
|
|
157
31
|
debug('=> using new database connection');
|
|
158
32
|
await mongoose.connect(process.env.MONGO_URI, mongoConfig);
|
|
159
|
-
debug('Connection state:',
|
|
33
|
+
debug('Connection state:', mongoose.STATES[mongoose.connection.readyState]);
|
|
160
34
|
mongoose.connection.on('error', (error) => flushDebugLog(error));
|
|
161
35
|
};
|
|
162
36
|
|
|
@@ -9,19 +9,18 @@ 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');
|
|
19
18
|
return res.status(401).json({
|
|
20
19
|
status: 'error',
|
|
21
|
-
message: 'Unauthorized'
|
|
20
|
+
message: 'Unauthorized'
|
|
22
21
|
});
|
|
23
22
|
}
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
next();
|
|
26
25
|
};
|
|
27
26
|
|
|
@@ -31,7 +30,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
|
|
|
31
30
|
return new Promise((resolve) => {
|
|
32
31
|
const protocol = url.startsWith('https:') ? https : http;
|
|
33
32
|
const startTime = Date.now();
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
try {
|
|
36
35
|
const request = protocol.get(url, { timeout }, (res) => {
|
|
37
36
|
const responseTime = Date.now() - startTime;
|
|
@@ -39,7 +38,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
|
|
|
39
38
|
status: 'healthy',
|
|
40
39
|
statusCode: res.statusCode,
|
|
41
40
|
responseTime,
|
|
42
|
-
reachable: res.statusCode < 500
|
|
41
|
+
reachable: res.statusCode < 500
|
|
43
42
|
});
|
|
44
43
|
});
|
|
45
44
|
|
|
@@ -48,7 +47,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
|
|
|
48
47
|
status: 'unhealthy',
|
|
49
48
|
error: error.message,
|
|
50
49
|
responseTime: Date.now() - startTime,
|
|
51
|
-
reachable: false
|
|
50
|
+
reachable: false
|
|
52
51
|
});
|
|
53
52
|
});
|
|
54
53
|
|
|
@@ -58,7 +57,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
|
|
|
58
57
|
status: 'timeout',
|
|
59
58
|
error: 'Request timeout',
|
|
60
59
|
responseTime: timeout,
|
|
61
|
-
reachable: false
|
|
60
|
+
reachable: false
|
|
62
61
|
});
|
|
63
62
|
});
|
|
64
63
|
} catch (error) {
|
|
@@ -66,7 +65,7 @@ const checkExternalAPI = (url, timeout = 5000) => {
|
|
|
66
65
|
status: 'error',
|
|
67
66
|
error: error.message,
|
|
68
67
|
responseTime: Date.now() - startTime,
|
|
69
|
-
reachable: false
|
|
68
|
+
reachable: false
|
|
70
69
|
});
|
|
71
70
|
}
|
|
72
71
|
});
|
|
@@ -77,14 +76,14 @@ const getDatabaseState = () => {
|
|
|
77
76
|
0: 'disconnected',
|
|
78
77
|
1: 'connected',
|
|
79
78
|
2: 'connecting',
|
|
80
|
-
3: 'disconnecting'
|
|
79
|
+
3: 'disconnecting'
|
|
81
80
|
};
|
|
82
81
|
const readyState = mongoose.connection.readyState;
|
|
83
|
-
|
|
82
|
+
|
|
84
83
|
return {
|
|
85
84
|
readyState,
|
|
86
85
|
stateName: stateMap[readyState],
|
|
87
|
-
isConnected: readyState === 1
|
|
86
|
+
isConnected: readyState === 1
|
|
88
87
|
};
|
|
89
88
|
};
|
|
90
89
|
|
|
@@ -92,7 +91,7 @@ const checkDatabaseHealth = async () => {
|
|
|
92
91
|
const { stateName, isConnected } = getDatabaseState();
|
|
93
92
|
const result = {
|
|
94
93
|
status: isConnected ? 'healthy' : 'unhealthy',
|
|
95
|
-
state: stateName
|
|
94
|
+
state: stateName
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
if (isConnected) {
|
|
@@ -105,20 +104,19 @@ const checkDatabaseHealth = async () => {
|
|
|
105
104
|
};
|
|
106
105
|
|
|
107
106
|
const getEncryptionConfiguration = () => {
|
|
108
|
-
const { STAGE, BYPASS_ENCRYPTION_STAGE, KMS_KEY_ARN, AES_KEY_ID } =
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
const { STAGE, BYPASS_ENCRYPTION_STAGE, KMS_KEY_ARN, AES_KEY_ID } = process.env;
|
|
108
|
+
|
|
111
109
|
const defaultBypassStages = ['dev', 'test', 'local'];
|
|
112
110
|
const useEnv = BYPASS_ENCRYPTION_STAGE !== undefined;
|
|
113
111
|
const bypassStages = useEnv
|
|
114
112
|
? BYPASS_ENCRYPTION_STAGE.split(',').map((s) => s.trim())
|
|
115
113
|
: defaultBypassStages;
|
|
116
|
-
|
|
114
|
+
|
|
117
115
|
const isBypassed = bypassStages.includes(STAGE);
|
|
118
116
|
const hasAES = AES_KEY_ID && AES_KEY_ID.trim() !== '';
|
|
119
117
|
const hasKMS = KMS_KEY_ARN && KMS_KEY_ARN.trim() !== '' && !hasAES;
|
|
120
118
|
const mode = hasAES ? 'aes' : hasKMS ? 'kms' : 'none';
|
|
121
|
-
|
|
119
|
+
|
|
122
120
|
return {
|
|
123
121
|
stage: STAGE || null,
|
|
124
122
|
isBypassed,
|
|
@@ -130,24 +128,19 @@ const getEncryptionConfiguration = () => {
|
|
|
130
128
|
|
|
131
129
|
const createTestEncryptionModel = () => {
|
|
132
130
|
const { Encrypt } = require('./../../encrypt');
|
|
133
|
-
|
|
134
|
-
const testSchema = new mongoose.Schema(
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
{ timestamps: false }
|
|
143
|
-
);
|
|
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 });
|
|
144
139
|
|
|
145
140
|
testSchema.plugin(Encrypt);
|
|
146
|
-
|
|
147
|
-
return
|
|
148
|
-
mongoose.
|
|
149
|
-
mongoose.model('TestEncryption', testSchema)
|
|
150
|
-
);
|
|
141
|
+
|
|
142
|
+
return mongoose.models.TestEncryption ||
|
|
143
|
+
mongoose.model('TestEncryption', testSchema);
|
|
151
144
|
};
|
|
152
145
|
|
|
153
146
|
const createTestDocument = async (TestModel) => {
|
|
@@ -155,23 +148,21 @@ const createTestDocument = async (TestModel) => {
|
|
|
155
148
|
testSecret: 'This is a secret value that should be encrypted',
|
|
156
149
|
normalField: 'This is a normal field that should not be encrypted',
|
|
157
150
|
nestedSecret: {
|
|
158
|
-
value: 'This is a nested secret that should be encrypted'
|
|
159
|
-
}
|
|
151
|
+
value: 'This is a nested secret that should be encrypted'
|
|
152
|
+
}
|
|
160
153
|
};
|
|
161
154
|
|
|
162
155
|
const testDoc = new TestModel(testData);
|
|
163
156
|
await testDoc.save();
|
|
164
|
-
|
|
157
|
+
|
|
165
158
|
return { testDoc, testData };
|
|
166
159
|
};
|
|
167
160
|
|
|
168
161
|
const verifyDecryption = (retrievedDoc, originalData) => {
|
|
169
|
-
return
|
|
170
|
-
retrievedDoc &&
|
|
162
|
+
return retrievedDoc &&
|
|
171
163
|
retrievedDoc.testSecret === originalData.testSecret &&
|
|
172
164
|
retrievedDoc.normalField === originalData.normalField &&
|
|
173
|
-
retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value
|
|
174
|
-
);
|
|
165
|
+
retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value;
|
|
175
166
|
};
|
|
176
167
|
|
|
177
168
|
const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
|
|
@@ -180,85 +171,70 @@ const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
|
|
|
180
171
|
.collection(collectionName)
|
|
181
172
|
.findOne({ _id: testDoc._id });
|
|
182
173
|
|
|
183
|
-
const secretIsEncrypted =
|
|
184
|
-
rawDoc &&
|
|
185
|
-
|
|
186
|
-
rawDoc.testSecret.includes(':') &&
|
|
174
|
+
const secretIsEncrypted = rawDoc &&
|
|
175
|
+
typeof rawDoc.testSecret === 'string' &&
|
|
176
|
+
rawDoc.testSecret.includes(':') &&
|
|
187
177
|
rawDoc.testSecret !== originalData.testSecret;
|
|
188
|
-
|
|
189
|
-
const nestedIsEncrypted =
|
|
190
|
-
rawDoc?.nestedSecret?.value &&
|
|
178
|
+
|
|
179
|
+
const nestedIsEncrypted = rawDoc?.nestedSecret?.value &&
|
|
191
180
|
typeof rawDoc.nestedSecret.value === 'string' &&
|
|
192
|
-
rawDoc.nestedSecret.value.includes(':') &&
|
|
181
|
+
rawDoc.nestedSecret.value.includes(':') &&
|
|
193
182
|
rawDoc.nestedSecret.value !== originalData.nestedSecret.value;
|
|
194
|
-
|
|
195
|
-
const normalNotEncrypted =
|
|
196
|
-
rawDoc
|
|
183
|
+
|
|
184
|
+
const normalNotEncrypted = rawDoc &&
|
|
185
|
+
rawDoc.normalField === originalData.normalField;
|
|
197
186
|
|
|
198
187
|
return {
|
|
199
188
|
secretIsEncrypted,
|
|
200
189
|
nestedIsEncrypted,
|
|
201
|
-
normalNotEncrypted
|
|
190
|
+
normalNotEncrypted
|
|
202
191
|
};
|
|
203
192
|
};
|
|
204
193
|
|
|
205
194
|
const evaluateEncryptionTestResults = (decryptionWorks, encryptionResults) => {
|
|
206
|
-
const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
decryptionWorks &&
|
|
211
|
-
secretIsEncrypted &&
|
|
212
|
-
nestedIsEncrypted &&
|
|
213
|
-
normalNotEncrypted
|
|
214
|
-
) {
|
|
195
|
+
const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } = encryptionResults;
|
|
196
|
+
|
|
197
|
+
if (decryptionWorks && secretIsEncrypted && nestedIsEncrypted && normalNotEncrypted) {
|
|
215
198
|
return {
|
|
216
199
|
status: 'enabled',
|
|
217
|
-
testResult: 'Encryption and decryption verified successfully'
|
|
200
|
+
testResult: 'Encryption and decryption verified successfully'
|
|
218
201
|
};
|
|
219
202
|
}
|
|
220
|
-
|
|
203
|
+
|
|
221
204
|
if (decryptionWorks && (!secretIsEncrypted || !nestedIsEncrypted)) {
|
|
222
205
|
return {
|
|
223
206
|
status: 'unhealthy',
|
|
224
|
-
testResult: 'Fields are not being encrypted in database'
|
|
207
|
+
testResult: 'Fields are not being encrypted in database'
|
|
225
208
|
};
|
|
226
209
|
}
|
|
227
|
-
|
|
210
|
+
|
|
228
211
|
if (decryptionWorks && !normalNotEncrypted) {
|
|
229
212
|
return {
|
|
230
213
|
status: 'unhealthy',
|
|
231
|
-
testResult: 'Normal fields are being incorrectly encrypted'
|
|
214
|
+
testResult: 'Normal fields are being incorrectly encrypted'
|
|
232
215
|
};
|
|
233
216
|
}
|
|
234
|
-
|
|
217
|
+
|
|
235
218
|
return {
|
|
236
219
|
status: 'unhealthy',
|
|
237
|
-
testResult: 'Decryption failed or data mismatch'
|
|
220
|
+
testResult: 'Decryption failed or data mismatch'
|
|
238
221
|
};
|
|
239
222
|
};
|
|
240
223
|
|
|
241
224
|
const testEncryption = async () => {
|
|
242
225
|
const TestModel = createTestEncryptionModel();
|
|
243
226
|
const { testDoc, testData } = await createTestDocument(TestModel);
|
|
244
|
-
|
|
227
|
+
|
|
245
228
|
try {
|
|
246
229
|
const retrievedDoc = await TestModel.findById(testDoc._id);
|
|
247
230
|
const decryptionWorks = verifyDecryption(retrievedDoc, testData);
|
|
248
|
-
const encryptionResults = await verifyEncryptionInDatabase(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
const evaluation = evaluateEncryptionTestResults(
|
|
255
|
-
decryptionWorks,
|
|
256
|
-
encryptionResults
|
|
257
|
-
);
|
|
258
|
-
|
|
231
|
+
const encryptionResults = await verifyEncryptionInDatabase(testDoc, testData, TestModel);
|
|
232
|
+
|
|
233
|
+
const evaluation = evaluateEncryptionTestResults(decryptionWorks, encryptionResults);
|
|
234
|
+
|
|
259
235
|
return {
|
|
260
236
|
...evaluation,
|
|
261
|
-
encryptionWorks: decryptionWorks
|
|
237
|
+
encryptionWorks: decryptionWorks
|
|
262
238
|
};
|
|
263
239
|
} finally {
|
|
264
240
|
await TestModel.deleteOne({ _id: testDoc._id });
|
|
@@ -267,12 +243,12 @@ const testEncryption = async () => {
|
|
|
267
243
|
|
|
268
244
|
const checkEncryptionHealth = async () => {
|
|
269
245
|
const config = getEncryptionConfiguration();
|
|
270
|
-
|
|
246
|
+
|
|
271
247
|
if (config.isBypassed || config.mode === 'none') {
|
|
272
|
-
const testResult = config.isBypassed
|
|
273
|
-
? 'Encryption bypassed for this stage'
|
|
248
|
+
const testResult = config.isBypassed
|
|
249
|
+
? 'Encryption bypassed for this stage'
|
|
274
250
|
: 'No encryption keys configured';
|
|
275
|
-
|
|
251
|
+
|
|
276
252
|
return {
|
|
277
253
|
status: 'disabled',
|
|
278
254
|
mode: config.mode,
|
|
@@ -282,14 +258,14 @@ const checkEncryptionHealth = async () => {
|
|
|
282
258
|
encryptionWorks: false,
|
|
283
259
|
debug: {
|
|
284
260
|
hasKMS: config.hasKMS,
|
|
285
|
-
hasAES: config.hasAES
|
|
286
|
-
}
|
|
261
|
+
hasAES: config.hasAES
|
|
262
|
+
}
|
|
287
263
|
};
|
|
288
264
|
}
|
|
289
265
|
|
|
290
266
|
try {
|
|
291
267
|
const testResults = await testEncryption();
|
|
292
|
-
|
|
268
|
+
|
|
293
269
|
return {
|
|
294
270
|
...testResults,
|
|
295
271
|
mode: config.mode,
|
|
@@ -297,8 +273,8 @@ const checkEncryptionHealth = async () => {
|
|
|
297
273
|
stage: config.stage,
|
|
298
274
|
debug: {
|
|
299
275
|
hasKMS: config.hasKMS,
|
|
300
|
-
hasAES: config.hasAES
|
|
301
|
-
}
|
|
276
|
+
hasAES: config.hasAES
|
|
277
|
+
}
|
|
302
278
|
};
|
|
303
279
|
} catch (error) {
|
|
304
280
|
return {
|
|
@@ -310,8 +286,8 @@ const checkEncryptionHealth = async () => {
|
|
|
310
286
|
encryptionWorks: false,
|
|
311
287
|
debug: {
|
|
312
288
|
hasKMS: config.hasKMS,
|
|
313
|
-
hasAES: config.hasAES
|
|
314
|
-
}
|
|
289
|
+
hasAES: config.hasAES
|
|
290
|
+
}
|
|
315
291
|
};
|
|
316
292
|
}
|
|
317
293
|
};
|
|
@@ -319,28 +295,25 @@ const checkEncryptionHealth = async () => {
|
|
|
319
295
|
const checkExternalAPIs = async () => {
|
|
320
296
|
const apis = [
|
|
321
297
|
{ name: 'github', url: 'https://api.github.com/status' },
|
|
322
|
-
{ name: 'npm', url: 'https://registry.npmjs.org' }
|
|
298
|
+
{ name: 'npm', url: 'https://registry.npmjs.org' }
|
|
323
299
|
];
|
|
324
300
|
|
|
325
301
|
const results = await Promise.all(
|
|
326
|
-
apis.map(
|
|
327
|
-
checkExternalAPI(api.url).then(
|
|
328
|
-
name: api.name,
|
|
329
|
-
...result,
|
|
330
|
-
}))
|
|
302
|
+
apis.map(api =>
|
|
303
|
+
checkExternalAPI(api.url).then(result => ({ name: api.name, ...result }))
|
|
331
304
|
)
|
|
332
305
|
);
|
|
333
|
-
|
|
306
|
+
|
|
334
307
|
const apiStatuses = {};
|
|
335
308
|
let allReachable = true;
|
|
336
|
-
|
|
309
|
+
|
|
337
310
|
results.forEach(({ name, ...checkResult }) => {
|
|
338
311
|
apiStatuses[name] = checkResult;
|
|
339
312
|
if (!checkResult.reachable) {
|
|
340
313
|
allReachable = false;
|
|
341
314
|
}
|
|
342
315
|
});
|
|
343
|
-
|
|
316
|
+
|
|
344
317
|
return { apiStatuses, allReachable };
|
|
345
318
|
};
|
|
346
319
|
|
|
@@ -348,7 +321,7 @@ const checkIntegrations = () => {
|
|
|
348
321
|
const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
|
|
349
322
|
? moduleFactory.moduleTypes
|
|
350
323
|
: [];
|
|
351
|
-
|
|
324
|
+
|
|
352
325
|
const integrationTypes = Array.isArray(integrationFactory.integrationTypes)
|
|
353
326
|
? integrationFactory.integrationTypes
|
|
354
327
|
: [];
|
|
@@ -372,7 +345,7 @@ const buildHealthCheckResponse = (startTime) => {
|
|
|
372
345
|
status: 'healthy',
|
|
373
346
|
timestamp: new Date().toISOString(),
|
|
374
347
|
checks: {},
|
|
375
|
-
calculateResponseTime: () => Date.now() - startTime
|
|
348
|
+
calculateResponseTime: () => Date.now() - startTime
|
|
376
349
|
};
|
|
377
350
|
};
|
|
378
351
|
|
|
@@ -380,15 +353,13 @@ router.get('/health', async (_req, res) => {
|
|
|
380
353
|
const status = {
|
|
381
354
|
status: 'ok',
|
|
382
355
|
timestamp: new Date().toISOString(),
|
|
383
|
-
service: 'frigg-core-api'
|
|
356
|
+
service: 'frigg-core-api'
|
|
384
357
|
};
|
|
385
358
|
|
|
386
359
|
res.status(200).json(status);
|
|
387
360
|
});
|
|
388
361
|
|
|
389
362
|
router.get('/health/detailed', async (_req, res) => {
|
|
390
|
-
// eslint-disable-next-line no-console
|
|
391
|
-
console.log('Starting detailed health check');
|
|
392
363
|
const startTime = Date.now();
|
|
393
364
|
const response = buildHealthCheckResponse(startTime);
|
|
394
365
|
|
|
@@ -398,16 +369,12 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
398
369
|
if (!dbState.isConnected) {
|
|
399
370
|
response.status = 'unhealthy';
|
|
400
371
|
}
|
|
401
|
-
// eslint-disable-next-line no-console
|
|
402
|
-
console.log('Database check completed:', response.checks.database);
|
|
403
372
|
} catch (error) {
|
|
404
373
|
response.checks.database = {
|
|
405
374
|
status: 'unhealthy',
|
|
406
|
-
error: error.message
|
|
375
|
+
error: error.message
|
|
407
376
|
};
|
|
408
377
|
response.status = 'unhealthy';
|
|
409
|
-
// eslint-disable-next-line no-console
|
|
410
|
-
console.log('Database check error:', error.message);
|
|
411
378
|
}
|
|
412
379
|
|
|
413
380
|
try {
|
|
@@ -415,16 +382,12 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
415
382
|
if (response.checks.encryption.status === 'unhealthy') {
|
|
416
383
|
response.status = 'unhealthy';
|
|
417
384
|
}
|
|
418
|
-
// eslint-disable-next-line no-console
|
|
419
|
-
console.log('Encryption check completed:', response.checks.encryption);
|
|
420
385
|
} catch (error) {
|
|
421
386
|
response.checks.encryption = {
|
|
422
387
|
status: 'unhealthy',
|
|
423
|
-
error: error.message
|
|
388
|
+
error: error.message
|
|
424
389
|
};
|
|
425
390
|
response.status = 'unhealthy';
|
|
426
|
-
// eslint-disable-next-line no-console
|
|
427
|
-
console.log('Encryption check error:', error.message);
|
|
428
391
|
}
|
|
429
392
|
|
|
430
393
|
const { apiStatuses, allReachable } = await checkExternalAPIs();
|
|
@@ -432,24 +395,15 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
432
395
|
if (!allReachable) {
|
|
433
396
|
response.status = 'unhealthy';
|
|
434
397
|
}
|
|
435
|
-
// eslint-disable-next-line no-console
|
|
436
|
-
console.log('External APIs check completed:', response.checks.externalApis);
|
|
437
398
|
|
|
438
399
|
try {
|
|
439
400
|
response.checks.integrations = checkIntegrations();
|
|
440
|
-
// eslint-disable-next-line no-console
|
|
441
|
-
console.log(
|
|
442
|
-
'Integrations check completed:',
|
|
443
|
-
response.checks.integrations
|
|
444
|
-
);
|
|
445
401
|
} catch (error) {
|
|
446
402
|
response.checks.integrations = {
|
|
447
403
|
status: 'unhealthy',
|
|
448
|
-
error: error.message
|
|
404
|
+
error: error.message
|
|
449
405
|
};
|
|
450
406
|
response.status = 'unhealthy';
|
|
451
|
-
// eslint-disable-next-line no-console
|
|
452
|
-
console.log('Integrations check error:', error.message);
|
|
453
407
|
}
|
|
454
408
|
|
|
455
409
|
response.responseTime = response.calculateResponseTime();
|
|
@@ -457,27 +411,19 @@ router.get('/health/detailed', async (_req, res) => {
|
|
|
457
411
|
|
|
458
412
|
const statusCode = response.status === 'healthy' ? 200 : 503;
|
|
459
413
|
res.status(statusCode).json(response);
|
|
460
|
-
|
|
461
|
-
// eslint-disable-next-line no-console
|
|
462
|
-
console.log(
|
|
463
|
-
'Final health status:',
|
|
464
|
-
response.status,
|
|
465
|
-
'Response time:',
|
|
466
|
-
response.responseTime
|
|
467
|
-
);
|
|
468
414
|
});
|
|
469
415
|
|
|
470
416
|
router.get('/health/live', (_req, res) => {
|
|
471
417
|
res.status(200).json({
|
|
472
418
|
status: 'alive',
|
|
473
|
-
timestamp: new Date().toISOString()
|
|
419
|
+
timestamp: new Date().toISOString()
|
|
474
420
|
});
|
|
475
421
|
});
|
|
476
422
|
|
|
477
423
|
router.get('/health/ready', async (_req, res) => {
|
|
478
424
|
const dbState = getDatabaseState();
|
|
479
425
|
const isDbReady = dbState.isConnected;
|
|
480
|
-
|
|
426
|
+
|
|
481
427
|
let areModulesReady = false;
|
|
482
428
|
try {
|
|
483
429
|
const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
|
|
@@ -495,11 +441,11 @@ router.get('/health/ready', async (_req, res) => {
|
|
|
495
441
|
timestamp: new Date().toISOString(),
|
|
496
442
|
checks: {
|
|
497
443
|
database: isDbReady,
|
|
498
|
-
modules: areModulesReady
|
|
499
|
-
}
|
|
444
|
+
modules: areModulesReady
|
|
445
|
+
}
|
|
500
446
|
});
|
|
501
447
|
});
|
|
502
448
|
|
|
503
449
|
const handler = createAppHandler('HTTP Event: Health', router);
|
|
504
450
|
|
|
505
|
-
module.exports = { handler, router };
|
|
451
|
+
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--canary.
|
|
4
|
+
"version": "2.0.0--canary.425.dd575ef.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.
|
|
26
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
27
|
-
"@friggframework/test": "2.0.0--canary.
|
|
25
|
+
"@friggframework/eslint-config": "2.0.0--canary.425.dd575ef.0",
|
|
26
|
+
"@friggframework/prettier-config": "2.0.0--canary.425.dd575ef.0",
|
|
27
|
+
"@friggframework/test": "2.0.0--canary.425.dd575ef.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": "dd575ef50858dae736ba1b59ccb006ac706fac4f"
|
|
60
60
|
}
|