@friggframework/core 2.0.0--canary.419.f684df1.0 → 2.0.0--canary.419.6e5f240.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.
@@ -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.log('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,19 +130,24 @@ 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 });
133
+
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
+ );
139
144
 
140
145
  testSchema.plugin(Encrypt);
141
-
142
- return mongoose.models.TestEncryption ||
143
- mongoose.model('TestEncryption', testSchema);
146
+
147
+ return (
148
+ mongoose.models.TestEncryption ||
149
+ mongoose.model('TestEncryption', testSchema)
150
+ );
144
151
  };
145
152
 
146
153
  const createTestDocument = async (TestModel) => {
@@ -148,21 +155,23 @@ const createTestDocument = async (TestModel) => {
148
155
  testSecret: 'This is a secret value that should be encrypted',
149
156
  normalField: 'This is a normal field that should not be encrypted',
150
157
  nestedSecret: {
151
- value: 'This is a nested secret that should be encrypted'
152
- }
158
+ value: 'This is a nested secret that should be encrypted',
159
+ },
153
160
  };
154
161
 
155
162
  const testDoc = new TestModel(testData);
156
163
  await testDoc.save();
157
-
164
+
158
165
  return { testDoc, testData };
159
166
  };
160
167
 
161
168
  const verifyDecryption = (retrievedDoc, originalData) => {
162
- return retrievedDoc &&
169
+ return (
170
+ retrievedDoc &&
163
171
  retrievedDoc.testSecret === originalData.testSecret &&
164
172
  retrievedDoc.normalField === originalData.normalField &&
165
- retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value;
173
+ retrievedDoc.nestedSecret?.value === originalData.nestedSecret.value
174
+ );
166
175
  };
167
176
 
168
177
  const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
@@ -171,70 +180,85 @@ const verifyEncryptionInDatabase = async (testDoc, originalData, TestModel) => {
171
180
  .collection(collectionName)
172
181
  .findOne({ _id: testDoc._id });
173
182
 
174
- const secretIsEncrypted = rawDoc &&
175
- typeof rawDoc.testSecret === 'string' &&
176
- rawDoc.testSecret.includes(':') &&
183
+ const secretIsEncrypted =
184
+ rawDoc &&
185
+ typeof rawDoc.testSecret === 'string' &&
186
+ rawDoc.testSecret.includes(':') &&
177
187
  rawDoc.testSecret !== originalData.testSecret;
178
-
179
- const nestedIsEncrypted = rawDoc?.nestedSecret?.value &&
188
+
189
+ const nestedIsEncrypted =
190
+ rawDoc?.nestedSecret?.value &&
180
191
  typeof rawDoc.nestedSecret.value === 'string' &&
181
- rawDoc.nestedSecret.value.includes(':') &&
192
+ rawDoc.nestedSecret.value.includes(':') &&
182
193
  rawDoc.nestedSecret.value !== originalData.nestedSecret.value;
183
-
184
- const normalNotEncrypted = rawDoc &&
185
- rawDoc.normalField === originalData.normalField;
194
+
195
+ const normalNotEncrypted =
196
+ rawDoc && rawDoc.normalField === originalData.normalField;
186
197
 
187
198
  return {
188
199
  secretIsEncrypted,
189
200
  nestedIsEncrypted,
190
- normalNotEncrypted
201
+ normalNotEncrypted,
191
202
  };
192
203
  };
193
204
 
194
205
  const evaluateEncryptionTestResults = (decryptionWorks, encryptionResults) => {
195
- const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } = encryptionResults;
196
-
197
- if (decryptionWorks && secretIsEncrypted && nestedIsEncrypted && normalNotEncrypted) {
206
+ const { secretIsEncrypted, nestedIsEncrypted, normalNotEncrypted } =
207
+ encryptionResults;
208
+
209
+ if (
210
+ decryptionWorks &&
211
+ secretIsEncrypted &&
212
+ nestedIsEncrypted &&
213
+ normalNotEncrypted
214
+ ) {
198
215
  return {
199
216
  status: 'enabled',
200
- testResult: 'Encryption and decryption verified successfully'
217
+ testResult: 'Encryption and decryption verified successfully',
201
218
  };
202
219
  }
203
-
220
+
204
221
  if (decryptionWorks && (!secretIsEncrypted || !nestedIsEncrypted)) {
205
222
  return {
206
223
  status: 'unhealthy',
207
- testResult: 'Fields are not being encrypted in database'
224
+ testResult: 'Fields are not being encrypted in database',
208
225
  };
209
226
  }
210
-
227
+
211
228
  if (decryptionWorks && !normalNotEncrypted) {
212
229
  return {
213
230
  status: 'unhealthy',
214
- testResult: 'Normal fields are being incorrectly encrypted'
231
+ testResult: 'Normal fields are being incorrectly encrypted',
215
232
  };
216
233
  }
217
-
234
+
218
235
  return {
219
236
  status: 'unhealthy',
220
- testResult: 'Decryption failed or data mismatch'
237
+ testResult: 'Decryption failed or data mismatch',
221
238
  };
222
239
  };
223
240
 
224
241
  const testEncryption = async () => {
225
242
  const TestModel = createTestEncryptionModel();
226
243
  const { testDoc, testData } = await createTestDocument(TestModel);
227
-
244
+
228
245
  try {
229
246
  const retrievedDoc = await TestModel.findById(testDoc._id);
230
247
  const decryptionWorks = verifyDecryption(retrievedDoc, testData);
231
- const encryptionResults = await verifyEncryptionInDatabase(testDoc, testData, TestModel);
232
-
233
- const evaluation = evaluateEncryptionTestResults(decryptionWorks, encryptionResults);
234
-
248
+ const encryptionResults = await verifyEncryptionInDatabase(
249
+ testDoc,
250
+ testData,
251
+ TestModel
252
+ );
253
+
254
+ const evaluation = evaluateEncryptionTestResults(
255
+ decryptionWorks,
256
+ encryptionResults
257
+ );
258
+
235
259
  return {
236
260
  ...evaluation,
237
- encryptionWorks: decryptionWorks
261
+ encryptionWorks: decryptionWorks,
238
262
  };
239
263
  } finally {
240
264
  await TestModel.deleteOne({ _id: testDoc._id });
@@ -243,12 +267,12 @@ const testEncryption = async () => {
243
267
 
244
268
  const checkEncryptionHealth = async () => {
245
269
  const config = getEncryptionConfiguration();
246
-
270
+
247
271
  if (config.isBypassed || config.mode === 'none') {
248
- const testResult = config.isBypassed
249
- ? 'Encryption bypassed for this stage'
272
+ const testResult = config.isBypassed
273
+ ? 'Encryption bypassed for this stage'
250
274
  : 'No encryption keys configured';
251
-
275
+
252
276
  return {
253
277
  status: 'disabled',
254
278
  mode: config.mode,
@@ -258,14 +282,14 @@ const checkEncryptionHealth = async () => {
258
282
  encryptionWorks: false,
259
283
  debug: {
260
284
  hasKMS: config.hasKMS,
261
- hasAES: config.hasAES
262
- }
285
+ hasAES: config.hasAES,
286
+ },
263
287
  };
264
288
  }
265
289
 
266
290
  try {
267
291
  const testResults = await testEncryption();
268
-
292
+
269
293
  return {
270
294
  ...testResults,
271
295
  mode: config.mode,
@@ -273,8 +297,8 @@ const checkEncryptionHealth = async () => {
273
297
  stage: config.stage,
274
298
  debug: {
275
299
  hasKMS: config.hasKMS,
276
- hasAES: config.hasAES
277
- }
300
+ hasAES: config.hasAES,
301
+ },
278
302
  };
279
303
  } catch (error) {
280
304
  return {
@@ -286,8 +310,8 @@ const checkEncryptionHealth = async () => {
286
310
  encryptionWorks: false,
287
311
  debug: {
288
312
  hasKMS: config.hasKMS,
289
- hasAES: config.hasAES
290
- }
313
+ hasAES: config.hasAES,
314
+ },
291
315
  };
292
316
  }
293
317
  };
@@ -295,25 +319,28 @@ const checkEncryptionHealth = async () => {
295
319
  const checkExternalAPIs = async () => {
296
320
  const apis = [
297
321
  { name: 'github', url: 'https://api.github.com/status' },
298
- { name: 'npm', url: 'https://registry.npmjs.org' }
322
+ { name: 'npm', url: 'https://registry.npmjs.org' },
299
323
  ];
300
324
 
301
325
  const results = await Promise.all(
302
- apis.map(api =>
303
- checkExternalAPI(api.url).then(result => ({ name: api.name, ...result }))
326
+ apis.map((api) =>
327
+ checkExternalAPI(api.url).then((result) => ({
328
+ name: api.name,
329
+ ...result,
330
+ }))
304
331
  )
305
332
  );
306
-
333
+
307
334
  const apiStatuses = {};
308
335
  let allReachable = true;
309
-
336
+
310
337
  results.forEach(({ name, ...checkResult }) => {
311
338
  apiStatuses[name] = checkResult;
312
339
  if (!checkResult.reachable) {
313
340
  allReachable = false;
314
341
  }
315
342
  });
316
-
343
+
317
344
  return { apiStatuses, allReachable };
318
345
  };
319
346
 
@@ -321,7 +348,7 @@ const checkIntegrations = () => {
321
348
  const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
322
349
  ? moduleFactory.moduleTypes
323
350
  : [];
324
-
351
+
325
352
  const integrationTypes = Array.isArray(integrationFactory.integrationTypes)
326
353
  ? integrationFactory.integrationTypes
327
354
  : [];
@@ -345,7 +372,7 @@ const buildHealthCheckResponse = (startTime) => {
345
372
  status: 'healthy',
346
373
  timestamp: new Date().toISOString(),
347
374
  checks: {},
348
- calculateResponseTime: () => Date.now() - startTime
375
+ calculateResponseTime: () => Date.now() - startTime,
349
376
  };
350
377
  };
351
378
 
@@ -353,7 +380,7 @@ router.get('/health', async (_req, res) => {
353
380
  const status = {
354
381
  status: 'ok',
355
382
  timestamp: new Date().toISOString(),
356
- service: 'frigg-core-api'
383
+ service: 'frigg-core-api',
357
384
  };
358
385
 
359
386
  res.status(200).json(status);
@@ -372,7 +399,7 @@ router.get('/health/detailed', async (_req, res) => {
372
399
  } catch (error) {
373
400
  response.checks.database = {
374
401
  status: 'unhealthy',
375
- error: error.message
402
+ error: error.message,
376
403
  };
377
404
  response.status = 'unhealthy';
378
405
  }
@@ -385,7 +412,7 @@ router.get('/health/detailed', async (_req, res) => {
385
412
  } catch (error) {
386
413
  response.checks.encryption = {
387
414
  status: 'unhealthy',
388
- error: error.message
415
+ error: error.message,
389
416
  };
390
417
  response.status = 'unhealthy';
391
418
  }
@@ -401,7 +428,7 @@ router.get('/health/detailed', async (_req, res) => {
401
428
  } catch (error) {
402
429
  response.checks.integrations = {
403
430
  status: 'unhealthy',
404
- error: error.message
431
+ error: error.message,
405
432
  };
406
433
  response.status = 'unhealthy';
407
434
  }
@@ -416,14 +443,14 @@ router.get('/health/detailed', async (_req, res) => {
416
443
  router.get('/health/live', (_req, res) => {
417
444
  res.status(200).json({
418
445
  status: 'alive',
419
- timestamp: new Date().toISOString()
446
+ timestamp: new Date().toISOString(),
420
447
  });
421
448
  });
422
449
 
423
450
  router.get('/health/ready', async (_req, res) => {
424
451
  const dbState = getDatabaseState();
425
452
  const isDbReady = dbState.isConnected;
426
-
453
+
427
454
  let areModulesReady = false;
428
455
  try {
429
456
  const moduleTypes = Array.isArray(moduleFactory.moduleTypes)
@@ -441,11 +468,11 @@ router.get('/health/ready', async (_req, res) => {
441
468
  timestamp: new Date().toISOString(),
442
469
  checks: {
443
470
  database: isDbReady,
444
- modules: areModulesReady
445
- }
471
+ modules: areModulesReady,
472
+ },
446
473
  });
447
474
  });
448
475
 
449
476
  const handler = createAppHandler('HTTP Event: Health', router);
450
477
 
451
- module.exports = { handler, router };
478
+ 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.419.f684df1.0",
4
+ "version": "2.0.0--canary.419.6e5f240.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.419.f684df1.0",
26
- "@friggframework/prettier-config": "2.0.0--canary.419.f684df1.0",
27
- "@friggframework/test": "2.0.0--canary.419.f684df1.0",
25
+ "@friggframework/eslint-config": "2.0.0--canary.419.6e5f240.0",
26
+ "@friggframework/prettier-config": "2.0.0--canary.419.6e5f240.0",
27
+ "@friggframework/test": "2.0.0--canary.419.6e5f240.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": "f684df1e01022a10465fd21c39317f3105ab5ef2"
59
+ "gitHead": "6e5f240c068ad7882b6e30c6976419d24cb8e5c2"
60
60
  }