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