@friggframework/core 2.0.0-next.5 → 2.0.0-next.51

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.
Files changed (267) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +179 -0
  7. package/application/commands/user-commands.js +213 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +2 -7
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +307 -0
  15. package/credential/repositories/credential-repository-postgres.js +313 -0
  16. package/credential/repositories/credential-repository.js +302 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  20. package/database/adapters/lambda-invoker.js +97 -0
  21. package/database/config.js +154 -0
  22. package/database/encryption/README.md +684 -0
  23. package/database/encryption/encryption-schema-registry.js +141 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/logger.js +79 -0
  26. package/database/encryption/prisma-encryption-extension.js +222 -0
  27. package/database/index.js +25 -12
  28. package/database/models/WebsocketConnection.js +16 -10
  29. package/database/models/readme.md +1 -0
  30. package/database/prisma.js +222 -0
  31. package/database/repositories/health-check-repository-factory.js +43 -0
  32. package/database/repositories/health-check-repository-interface.js +87 -0
  33. package/database/repositories/health-check-repository-mongodb.js +91 -0
  34. package/database/repositories/health-check-repository-postgres.js +82 -0
  35. package/database/repositories/health-check-repository.js +108 -0
  36. package/database/repositories/migration-status-repository-s3.js +137 -0
  37. package/database/use-cases/check-database-health-use-case.js +29 -0
  38. package/database/use-cases/check-database-state-use-case.js +81 -0
  39. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  40. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  41. package/database/use-cases/get-migration-status-use-case.js +93 -0
  42. package/database/use-cases/run-database-migration-use-case.js +137 -0
  43. package/database/use-cases/test-encryption-use-case.js +253 -0
  44. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  45. package/database/utils/mongodb-collection-utils.js +91 -0
  46. package/database/utils/mongodb-schema-init.js +106 -0
  47. package/database/utils/prisma-runner.js +400 -0
  48. package/database/utils/prisma-schema-parser.js +182 -0
  49. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  50. package/encrypt/Cryptor.js +34 -168
  51. package/encrypt/index.js +1 -2
  52. package/encrypt/test-encrypt.js +0 -2
  53. package/generated/prisma-mongodb/client.d.ts +1 -0
  54. package/generated/prisma-mongodb/client.js +4 -0
  55. package/generated/prisma-mongodb/default.d.ts +1 -0
  56. package/generated/prisma-mongodb/default.js +4 -0
  57. package/generated/prisma-mongodb/edge.d.ts +1 -0
  58. package/generated/prisma-mongodb/edge.js +334 -0
  59. package/generated/prisma-mongodb/index-browser.js +316 -0
  60. package/generated/prisma-mongodb/index.d.ts +22898 -0
  61. package/generated/prisma-mongodb/index.js +359 -0
  62. package/generated/prisma-mongodb/package.json +183 -0
  63. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  64. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  65. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  66. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  67. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  68. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  69. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  70. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  71. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  72. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  73. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  74. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  75. package/generated/prisma-mongodb/schema.prisma +362 -0
  76. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  77. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  78. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  79. package/generated/prisma-mongodb/wasm.js +341 -0
  80. package/generated/prisma-postgresql/client.d.ts +1 -0
  81. package/generated/prisma-postgresql/client.js +4 -0
  82. package/generated/prisma-postgresql/default.d.ts +1 -0
  83. package/generated/prisma-postgresql/default.js +4 -0
  84. package/generated/prisma-postgresql/edge.d.ts +1 -0
  85. package/generated/prisma-postgresql/edge.js +356 -0
  86. package/generated/prisma-postgresql/index-browser.js +338 -0
  87. package/generated/prisma-postgresql/index.d.ts +25072 -0
  88. package/generated/prisma-postgresql/index.js +381 -0
  89. package/generated/prisma-postgresql/package.json +183 -0
  90. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  91. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  92. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  93. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  94. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  95. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  96. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  97. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  98. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  99. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  100. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  101. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  102. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  103. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  104. package/generated/prisma-postgresql/schema.prisma +345 -0
  105. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  106. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  107. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  108. package/generated/prisma-postgresql/wasm.js +363 -0
  109. package/handlers/WEBHOOKS.md +653 -0
  110. package/handlers/app-definition-loader.js +38 -0
  111. package/handlers/app-handler-helpers.js +56 -0
  112. package/handlers/backend-utils.js +180 -0
  113. package/handlers/database-migration-handler.js +227 -0
  114. package/handlers/integration-event-dispatcher.js +54 -0
  115. package/handlers/routers/HEALTHCHECK.md +342 -0
  116. package/handlers/routers/auth.js +15 -0
  117. package/handlers/routers/db-migration.handler.js +29 -0
  118. package/handlers/routers/db-migration.js +256 -0
  119. package/handlers/routers/health.js +519 -0
  120. package/handlers/routers/integration-defined-routers.js +45 -0
  121. package/handlers/routers/integration-webhook-routers.js +67 -0
  122. package/handlers/routers/user.js +63 -0
  123. package/handlers/routers/websocket.js +57 -0
  124. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  125. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  126. package/handlers/workers/db-migration.js +352 -0
  127. package/handlers/workers/integration-defined-workers.js +27 -0
  128. package/index.js +77 -22
  129. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  130. package/integrations/index.js +12 -10
  131. package/integrations/integration-base.js +296 -54
  132. package/integrations/integration-router.js +381 -182
  133. package/integrations/options.js +1 -1
  134. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  135. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  136. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  137. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  138. package/integrations/repositories/integration-mapping-repository.js +156 -0
  139. package/integrations/repositories/integration-repository-factory.js +44 -0
  140. package/integrations/repositories/integration-repository-interface.js +127 -0
  141. package/integrations/repositories/integration-repository-mongo.js +303 -0
  142. package/integrations/repositories/integration-repository-postgres.js +352 -0
  143. package/integrations/repositories/process-repository-factory.js +46 -0
  144. package/integrations/repositories/process-repository-interface.js +90 -0
  145. package/integrations/repositories/process-repository-mongo.js +190 -0
  146. package/integrations/repositories/process-repository-postgres.js +217 -0
  147. package/integrations/tests/doubles/dummy-integration-class.js +83 -0
  148. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  149. package/integrations/use-cases/create-integration.js +83 -0
  150. package/integrations/use-cases/create-process.js +128 -0
  151. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  152. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  153. package/integrations/use-cases/get-integration-for-user.js +78 -0
  154. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  155. package/integrations/use-cases/get-integration-instance.js +83 -0
  156. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  157. package/integrations/use-cases/get-possible-integrations.js +27 -0
  158. package/integrations/use-cases/get-process.js +87 -0
  159. package/integrations/use-cases/index.js +19 -0
  160. package/integrations/use-cases/load-integration-context.js +71 -0
  161. package/integrations/use-cases/update-integration-messages.js +44 -0
  162. package/integrations/use-cases/update-integration-status.js +32 -0
  163. package/integrations/use-cases/update-integration.js +93 -0
  164. package/integrations/use-cases/update-process-metrics.js +201 -0
  165. package/integrations/use-cases/update-process-state.js +119 -0
  166. package/integrations/utils/map-integration-dto.js +37 -0
  167. package/jest-global-setup-noop.js +3 -0
  168. package/jest-global-teardown-noop.js +3 -0
  169. package/logs/logger.js +0 -4
  170. package/{module-plugin → modules}/entity.js +1 -1
  171. package/{module-plugin → modules}/index.js +0 -8
  172. package/modules/module-factory.js +56 -0
  173. package/modules/module.js +221 -0
  174. package/modules/repositories/module-repository-factory.js +33 -0
  175. package/modules/repositories/module-repository-interface.js +129 -0
  176. package/modules/repositories/module-repository-mongo.js +377 -0
  177. package/modules/repositories/module-repository-postgres.js +426 -0
  178. package/modules/repositories/module-repository.js +316 -0
  179. package/{module-plugin → modules}/requester/requester.js +1 -0
  180. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  181. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  182. package/modules/tests/doubles/test-module-factory.js +16 -0
  183. package/modules/tests/doubles/test-module-repository.js +39 -0
  184. package/modules/use-cases/get-entities-for-user.js +32 -0
  185. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  186. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  187. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  188. package/modules/use-cases/get-module.js +55 -0
  189. package/modules/use-cases/process-authorization-callback.js +122 -0
  190. package/modules/use-cases/refresh-entity-options.js +59 -0
  191. package/modules/use-cases/test-module-auth.js +55 -0
  192. package/modules/utils/map-module-dto.js +18 -0
  193. package/package.json +82 -50
  194. package/prisma-mongodb/schema.prisma +362 -0
  195. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  196. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  197. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  198. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  199. package/prisma-postgresql/schema.prisma +345 -0
  200. package/queues/queuer-util.js +28 -15
  201. package/syncs/manager.js +468 -443
  202. package/syncs/repositories/sync-repository-factory.js +38 -0
  203. package/syncs/repositories/sync-repository-interface.js +109 -0
  204. package/syncs/repositories/sync-repository-mongo.js +239 -0
  205. package/syncs/repositories/sync-repository-postgres.js +319 -0
  206. package/syncs/sync.js +0 -1
  207. package/token/repositories/token-repository-factory.js +33 -0
  208. package/token/repositories/token-repository-interface.js +131 -0
  209. package/token/repositories/token-repository-mongo.js +212 -0
  210. package/token/repositories/token-repository-postgres.js +257 -0
  211. package/token/repositories/token-repository.js +219 -0
  212. package/types/core/index.d.ts +2 -2
  213. package/types/integrations/index.d.ts +2 -6
  214. package/types/module-plugin/index.d.ts +5 -59
  215. package/types/syncs/index.d.ts +0 -2
  216. package/user/repositories/user-repository-factory.js +46 -0
  217. package/user/repositories/user-repository-interface.js +198 -0
  218. package/user/repositories/user-repository-mongo.js +291 -0
  219. package/user/repositories/user-repository-postgres.js +350 -0
  220. package/user/tests/doubles/test-user-repository.js +72 -0
  221. package/user/use-cases/authenticate-user.js +127 -0
  222. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  223. package/user/use-cases/create-individual-user.js +61 -0
  224. package/user/use-cases/create-organization-user.js +47 -0
  225. package/user/use-cases/create-token-for-user-id.js +30 -0
  226. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  227. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  228. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  229. package/user/use-cases/login-user.js +122 -0
  230. package/user/user.js +93 -0
  231. package/utils/backend-path.js +38 -0
  232. package/utils/index.js +6 -0
  233. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  234. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  235. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  236. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  237. package/websocket/repositories/websocket-connection-repository.js +161 -0
  238. package/database/models/State.js +0 -9
  239. package/database/models/Token.js +0 -70
  240. package/database/mongo.js +0 -45
  241. package/encrypt/Cryptor.test.js +0 -32
  242. package/encrypt/encrypt.js +0 -132
  243. package/encrypt/encrypt.test.js +0 -1069
  244. package/errors/base-error.test.js +0 -32
  245. package/errors/fetch-error.test.js +0 -79
  246. package/errors/halt-error.test.js +0 -11
  247. package/errors/validation-errors.test.js +0 -120
  248. package/integrations/create-frigg-backend.js +0 -31
  249. package/integrations/integration-factory.js +0 -251
  250. package/integrations/integration-mapping.js +0 -43
  251. package/integrations/integration-model.js +0 -46
  252. package/integrations/integration-user.js +0 -144
  253. package/integrations/test/integration-base.test.js +0 -144
  254. package/lambda/TimeoutCatcher.test.js +0 -68
  255. package/logs/logger.test.js +0 -76
  256. package/module-plugin/auther.js +0 -393
  257. package/module-plugin/credential.js +0 -22
  258. package/module-plugin/entity-manager.js +0 -70
  259. package/module-plugin/manager.js +0 -169
  260. package/module-plugin/module-factory.js +0 -61
  261. package/module-plugin/requester/requester.test.js +0 -28
  262. package/module-plugin/test/auther.test.js +0 -97
  263. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  264. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  265. /package/{module-plugin → modules}/requester/basic.js +0 -0
  266. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  267. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,342 @@
1
+ # Frigg Healthcheck Endpoint Documentation
2
+
3
+ ## Overview
4
+
5
+ The Frigg service includes comprehensive healthcheck endpoints to monitor service health, connectivity, and readiness. These endpoints follow industry best practices and are designed for use with monitoring systems, load balancers, and container orchestration platforms.
6
+
7
+ ## Endpoints
8
+
9
+ ### 1. Basic Health Check
10
+ **GET** `/health`
11
+
12
+ Simple health check endpoint that returns basic service information. No authentication required. This endpoint is rate-limited at the API Gateway level.
13
+
14
+ **Response:**
15
+ ```json
16
+ {
17
+ "status": "ok",
18
+ "timestamp": "2024-01-10T12:00:00.000Z",
19
+ "service": "frigg-core-api"
20
+ }
21
+ ```
22
+
23
+ **Status Codes:**
24
+ - `200 OK` - Service is running
25
+
26
+ ### 2. Detailed Health Check
27
+ **GET** `/health/detailed`
28
+
29
+ Comprehensive health check that tests all service components and dependencies.
30
+
31
+ **Authentication Required:**
32
+ - Header: `x-api-key: YOUR_API_KEY`
33
+ - The API key must match the `HEALTH_API_KEY` environment variable
34
+
35
+ **Response:**
36
+ ```json
37
+ {
38
+ "service": "frigg-core-api",
39
+ "status": "healthy", // "healthy" or "unhealthy"
40
+ "timestamp": "2024-01-10T12:00:00.000Z",
41
+ "checks": {
42
+ "database": {
43
+ "status": "healthy",
44
+ "state": "connected",
45
+ "responseTime": 5 // milliseconds
46
+ },
47
+ "externalApis": {
48
+ "github": {
49
+ "status": "healthy",
50
+ "statusCode": 200,
51
+ "responseTime": 150,
52
+ "reachable": true
53
+ },
54
+ "npm": {
55
+ "status": "healthy",
56
+ "statusCode": 200,
57
+ "responseTime": 200,
58
+ "reachable": true
59
+ }
60
+ },
61
+ "integrations": {
62
+ "status": "healthy",
63
+ "modules": {
64
+ "count": 10,
65
+ "available": ["module1", "module2", "..."]
66
+ },
67
+ "integrations": {
68
+ "count": 5,
69
+ "available": ["integration1", "integration2", "..."]
70
+ }
71
+ }
72
+ },
73
+ "responseTime": 250 // total endpoint response time in milliseconds
74
+ }
75
+ ```
76
+
77
+ **Status Codes:**
78
+ - `200 OK` - Service is healthy (all components operational)
79
+ - `503 Service Unavailable` - Service is unhealthy (any component failure)
80
+ - `401 Unauthorized` - Missing or invalid x-api-key header
81
+
82
+ ### 3. Liveness Probe
83
+ **GET** `/health/live`
84
+
85
+ Kubernetes-style liveness probe. Returns whether the service process is alive.
86
+
87
+ **Authentication Required:**
88
+ - Header: `x-api-key: YOUR_API_KEY`
89
+
90
+ **Response:**
91
+ ```json
92
+ {
93
+ "status": "alive",
94
+ "timestamp": "2024-01-10T12:00:00.000Z"
95
+ }
96
+ ```
97
+
98
+ **Status Codes:**
99
+ - `200 OK` - Service process is alive
100
+
101
+ ### 4. Readiness Probe
102
+ **GET** `/health/ready`
103
+
104
+ Kubernetes-style readiness probe. Returns whether the service is ready to receive traffic.
105
+
106
+ **Authentication Required:**
107
+ - Header: `x-api-key: YOUR_API_KEY`
108
+
109
+ **Response:**
110
+ ```json
111
+ {
112
+ "ready": true,
113
+ "timestamp": "2024-01-10T12:00:00.000Z",
114
+ "checks": {
115
+ "database": true,
116
+ "modules": true
117
+ }
118
+ }
119
+ ```
120
+
121
+ **Status Codes:**
122
+ - `200 OK` - Service is ready
123
+ - `503 Service Unavailable` - Service is not ready
124
+
125
+ ## Health Status Definitions
126
+
127
+ - **healthy**: All components are functioning normally
128
+ - **unhealthy**: Any component is failing, service may not function properly
129
+
130
+ ## Component Checks
131
+
132
+ ### Database Connectivity
133
+ - Checks database connection state
134
+ - Performs ping test with 2-second timeout if connected
135
+ - Reports connection state and response time
136
+ - Database type is not exposed for security reasons
137
+
138
+ ### External API Connectivity
139
+ - Tests connectivity to external services (GitHub, npm registry)
140
+ - Configurable timeout (default: 5 seconds)
141
+ - Reports reachability and response times
142
+ - Uses Promise.all for parallel checking
143
+
144
+ ### Integration Status
145
+ - Verifies available modules and integrations are loaded
146
+ - Reports counts and lists of available components
147
+
148
+ ## Usage Examples
149
+
150
+ ### Monitoring Systems
151
+ Configure your monitoring system to poll `/health/detailed` every 30-60 seconds:
152
+ ```bash
153
+ curl -H "x-api-key: YOUR_API_KEY" https://your-frigg-instance.com/health/detailed
154
+ ```
155
+
156
+ ### Load Balancer Health Checks
157
+ Configure load balancers to use the simple `/health` endpoint:
158
+ ```bash
159
+ curl https://your-frigg-instance.com/health
160
+ ```
161
+
162
+ ### Kubernetes Configuration
163
+ ```yaml
164
+ livenessProbe:
165
+ httpGet:
166
+ path: /health/live
167
+ port: 8080
168
+ httpHeaders:
169
+ - name: x-api-key
170
+ value: YOUR_API_KEY
171
+ periodSeconds: 10
172
+ timeoutSeconds: 5
173
+
174
+ readinessProbe:
175
+ httpGet:
176
+ path: /health/ready
177
+ port: 8080
178
+ httpHeaders:
179
+ - name: x-api-key
180
+ value: YOUR_API_KEY
181
+ initialDelaySeconds: 30
182
+ periodSeconds: 10
183
+ ```
184
+
185
+ ## Customization
186
+
187
+ ### Adding External API Checks
188
+ To add more external API checks, modify the `externalAPIs` array in the health router:
189
+ ```javascript
190
+ const externalAPIs = [
191
+ { name: 'github', url: 'https://api.github.com/status' },
192
+ { name: 'npm', url: 'https://registry.npmjs.org' },
193
+ { name: 'your-api', url: 'https://your-api.com/health' }
194
+ ];
195
+ ```
196
+
197
+ ### Adjusting Timeouts
198
+ The default timeout for external API checks is 5 seconds. Database ping timeout is set to 2 seconds:
199
+ ```javascript
200
+ const checkExternalAPI = (url, timeout = 5000) => {
201
+ // ...
202
+ };
203
+
204
+ await mongoose.connection.db.admin().ping({ maxTimeMS: 2000 });
205
+ ```
206
+
207
+ ## Best Practices
208
+
209
+ 1. **Authentication**: Basic `/health` endpoint requires no authentication, but detailed endpoints require `x-api-key` header
210
+ 2. **Rate Limiting**: Configure rate limiting at the API Gateway level to prevent abuse
211
+ 3. **Fast Response**: Health checks should respond quickly (< 1 second)
212
+ 4. **Strict Status Codes**: Return 503 for any non-healthy state to ensure proper alerting
213
+ 5. **Detailed Logging**: Failed health checks are logged for debugging
214
+ 6. **Security**: No sensitive information (DB types, versions) exposed in responses
215
+ 7. **Lambda Considerations**: Uptime and memory metrics not included as they're not relevant in serverless
216
+
217
+ ## Troubleshooting
218
+
219
+ ### Database Connection Issues
220
+ - Check `MONGO_URI` environment variable
221
+ - Verify network connectivity to MongoDB
222
+ - Check MongoDB server status
223
+
224
+ ### External API Failures
225
+ - May indicate network issues or external service downtime
226
+ - Service reports "unhealthy" status if any external API is unreachable
227
+
228
+ ## Security Considerations
229
+
230
+ - Basic health endpoint requires no authentication for monitoring compatibility
231
+ - Detailed endpoints require `x-api-key` header authentication
232
+ - Health endpoints do not expose sensitive information
233
+ - Database connection strings and credentials are never included in responses
234
+ - External API checks use read-only endpoints
235
+ - Rate limiting should be configured at the API Gateway level
236
+ - Consider IP whitelisting for health endpoints in production
237
+
238
+ ## Environment Variables
239
+
240
+ - `HEALTH_API_KEY`: Required API key for accessing detailed health endpoints
241
+
242
+ ## TODO: DDD/Hexagonal Architecture Refactoring
243
+
244
+ ### Current Architecture Issues
245
+
246
+ The health router (health.js, 677 lines) currently violates DDD/Hexagonal Architecture principles:
247
+
248
+ **✅ What's Good:**
249
+ - Database access properly abstracted through `HealthCheckRepository`
250
+ - `CheckDatabaseHealthUseCase` and `TestEncryptionUseCase` correctly implement use case pattern
251
+ - All tests passing, no breaking changes
252
+
253
+ **❌ Architecture Violations:**
254
+ 1. **Handler contains significant business logic** - Functions like `getEncryptionConfiguration()`, `checkEncryptionHealth()`, `checkKmsDecryptCapability()`, `detectVpcConfiguration()`, `checkExternalAPIs()`, and `checkIntegrations()` contain business logic that should be in use cases
255
+ 2. **Direct infrastructure dependencies** - Handler directly uses `https`, `http`, Node.js `dns`, and factory modules instead of accessing through repositories
256
+ 3. **Mixed concerns** - Single file handles HTTP routing, business logic, infrastructure detection, and response formatting
257
+ 4. **Violates dependency rule** - Handler should only call use cases, never repositories or contain business logic
258
+
259
+ ### Proposed Refactoring Plan
260
+
261
+ #### Priority 1: Extract Core Health Check Use Cases (Immediate)
262
+
263
+ **New Use Cases:**
264
+ 1. `CheckEncryptionHealthUseCase` - Orchestrate encryption testing with configuration checks (from health.js:122-181)
265
+ 2. `CheckKmsConnectivityUseCase` - Test KMS decrypt capability (from health.js:339-490)
266
+ 3. `DetectNetworkConfigurationUseCase` - VPC and network detection (from health.js:244-336)
267
+
268
+ **New Repositories:**
269
+ 1. `EncryptionConfigRepository` - Get encryption mode, bypass rules (from health.js:98-120)
270
+ 2. `KmsRepository` - KMS connectivity testing, decrypt capability checks
271
+ 3. `NetworkRepository` - DNS resolution, VPC detection, TCP connectivity tests
272
+
273
+ #### Priority 2: Extract External Service Checks
274
+
275
+ **New Use Cases:**
276
+ 4. `CheckExternalServicesUseCase` - Check external API availability (from health.js:183-209)
277
+
278
+ **New Repositories:**
279
+ 4. `ExternalServiceRepository` - HTTP-based service health checking with timeout handling
280
+
281
+ #### Priority 3: Extract Integration Checks
282
+
283
+ **New Use Cases:**
284
+ 5. `CheckIntegrationAvailabilityUseCase` - Verify integrations and modules loaded (from health.js:211-231)
285
+
286
+ **Extend Existing:**
287
+ - Add `getAvailableIntegrations()` and `getAvailableModules()` methods to existing `IntegrationRepository`
288
+
289
+ ### Architectural Principles to Follow
290
+
291
+ **The Handler Should Only:**
292
+ - Define routes
293
+ - Call use cases
294
+ - Map use case results to HTTP responses
295
+ - Handle HTTP-specific concerns (status codes, headers)
296
+
297
+ **The Rule:**
298
+ > "Handlers (adapters) should only call use cases, never repositories or business logic directly"
299
+
300
+ **Dependency Direction:**
301
+ ```
302
+ Handler (Adapter Layer)
303
+ ↓ calls
304
+ Use Cases (Application Layer)
305
+ ↓ calls
306
+ Repositories (Infrastructure Layer)
307
+ ↓ calls
308
+ External Systems (Database, APIs, AWS Services)
309
+ ```
310
+
311
+ ### Expected Outcome
312
+
313
+ - Reduce health.js from **677 lines to ~100-150 lines**
314
+ - All business logic moved to use cases
315
+ - All infrastructure access moved to repositories
316
+ - Handler becomes thin HTTP adapter
317
+ - Improved testability (use cases testable without HTTP context)
318
+ - Better reusability (use cases usable in CLI tools, background jobs, etc.)
319
+
320
+ ### Implementation Status
321
+
322
+ - [ ] P1: Extract `CheckEncryptionHealthUseCase`
323
+ - [ ] P1: Create `EncryptionConfigRepository`
324
+ - [ ] P1: Extract `CheckKmsConnectivityUseCase`
325
+ - [ ] P1: Create `KmsRepository`
326
+ - [ ] P1: Extract `DetectNetworkConfigurationUseCase`
327
+ - [ ] P1: Create `NetworkRepository`
328
+ - [ ] P2: Extract `CheckExternalServicesUseCase`
329
+ - [ ] P2: Create `ExternalServiceRepository`
330
+ - [ ] P3: Extract `CheckIntegrationAvailabilityUseCase`
331
+ - [ ] P3: Extend existing `IntegrationRepository`
332
+
333
+ ### Future Considerations (Optional)
334
+
335
+ **Domain Models (Value Objects):**
336
+ - `HealthCheckResult` - Overall health check result with status, checks, timestamp
337
+ - `DatabaseHealth` - Database-specific health information
338
+ - `EncryptionHealth` - Encryption-specific health information
339
+ - `ServiceHealth` - Generic external service health
340
+ - `NetworkConfiguration` - VPC and network detection results
341
+
342
+ These would replace plain objects and provide type safety and business logic encapsulation.
@@ -0,0 +1,15 @@
1
+ const { createIntegrationRouter } = require('@friggframework/core');
2
+ const { createAppHandler } = require('./../app-handler-helpers');
3
+
4
+ const router = createIntegrationRouter();
5
+
6
+ router.route('/api/integrations/redirect/:appId').get((req, res) => {
7
+ res.redirect(
8
+ `${process.env.FRONTEND_URI}/redirect/${req.params.appId
9
+ }?${new URLSearchParams(req.query)}`
10
+ );
11
+ });
12
+
13
+ const handler = createAppHandler('HTTP Event: Auth', router);
14
+
15
+ module.exports = { handler };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Database Migration Router Lambda Handler
3
+ *
4
+ * Minimal Lambda wrapper that avoids loading core/index.js
5
+ * (which would try to load user/** modules excluded from migration packages)
6
+ *
7
+ * This handler is intentionally simpler than health.handler.js to avoid dependencies.
8
+ */
9
+
10
+ const serverlessHttp = require('serverless-http');
11
+ const express = require('express');
12
+ const cors = require('cors');
13
+ const dbMigrationRouter = require('./db-migration');
14
+
15
+ // Create minimal Express app
16
+ const app = express();
17
+ app.use(cors());
18
+ app.use(express.json());
19
+ app.use(dbMigrationRouter);
20
+
21
+ // Error handler
22
+ app.use((err, req, res, next) => {
23
+ console.error('Error:', err);
24
+ res.status(500).json({ message: 'Internal Server Error' });
25
+ });
26
+
27
+ // Export as .handler property (Lambda config: db-migration.handler)
28
+ module.exports.handler = serverlessHttp(app);
29
+
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Database Migration Router
3
+ *
4
+ * HTTP API for triggering and monitoring database migrations.
5
+ *
6
+ * Endpoints:
7
+ * - GET /db-migrate/status - Check if migrations are pending
8
+ * - POST /db-migrate - Trigger async migration (queues job)
9
+ * - GET /db-migrate/:processId - Check migration status
10
+ *
11
+ * Security:
12
+ * - Requires ADMIN_API_KEY header for all requests
13
+ *
14
+ * Architecture:
15
+ * - Router (Adapter Layer) → Use Cases (Domain) → Repositories (Infrastructure)
16
+ * - Follows DDD/Hexagonal architecture
17
+ */
18
+
19
+ const { Router } = require('express');
20
+ const catchAsyncError = require('express-async-handler');
21
+ const { MigrationStatusRepositoryS3 } = require('../../database/repositories/migration-status-repository-s3');
22
+ const {
23
+ TriggerDatabaseMigrationUseCase,
24
+ ValidationError: TriggerValidationError,
25
+ } = require('../../database/use-cases/trigger-database-migration-use-case');
26
+ const {
27
+ GetMigrationStatusUseCase,
28
+ ValidationError: GetValidationError,
29
+ NotFoundError,
30
+ } = require('../../database/use-cases/get-migration-status-use-case');
31
+ const { LambdaInvoker } = require('../../database/adapters/lambda-invoker');
32
+ const {
33
+ GetDatabaseStateViaWorkerUseCase,
34
+ } = require('../../database/use-cases/get-database-state-via-worker-use-case');
35
+
36
+ const router = Router();
37
+
38
+ // Dependency injection
39
+ // Use S3 repository to avoid User table dependency (chicken-and-egg problem)
40
+ const bucketName = process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
41
+ const migrationStatusRepository = new MigrationStatusRepositoryS3(bucketName);
42
+
43
+ const triggerMigrationUseCase = new TriggerDatabaseMigrationUseCase({
44
+ migrationStatusRepository,
45
+ // Note: QueuerUtil is used directly in the use case (static utility)
46
+ });
47
+ const getStatusUseCase = new GetMigrationStatusUseCase({ migrationStatusRepository });
48
+
49
+ // Lambda invocation for database state check (keeps router lightweight)
50
+ const lambdaInvoker = new LambdaInvoker();
51
+ const workerFunctionName = process.env.WORKER_FUNCTION_NAME ||
52
+ `${process.env.SERVICE || 'unknown'}-${process.env.STAGE || 'production'}-dbMigrationWorker`;
53
+
54
+ const getDatabaseStateUseCase = new GetDatabaseStateViaWorkerUseCase({
55
+ lambdaInvoker,
56
+ workerFunctionName,
57
+ });
58
+
59
+ /**
60
+ * Admin API key validation middleware
61
+ * Matches pattern from health.js:72-88
62
+ */
63
+ const validateApiKey = (req, res, next) => {
64
+ const apiKey = req.headers['x-frigg-admin-api-key'];
65
+
66
+ if (!apiKey || apiKey !== process.env.ADMIN_API_KEY) {
67
+ console.error('Unauthorized access attempt to db-migrate endpoint');
68
+ return res.status(401).json({
69
+ status: 'error',
70
+ message: 'Unauthorized - x-frigg-admin-api-key header required',
71
+ });
72
+ }
73
+
74
+ next();
75
+ };
76
+
77
+ // Apply API key validation to all routes
78
+ router.use(validateApiKey);
79
+
80
+ /**
81
+ * POST /db-migrate
82
+ *
83
+ * Trigger database migration (async via SQS queue)
84
+ *
85
+ * Request body:
86
+ * {
87
+ * userId: string (optional, defaults to 'admin'),
88
+ * dbType: 'postgresql' | 'mongodb',
89
+ * stage: string (e.g., 'production', 'dev')
90
+ * }
91
+ *
92
+ * Response (202 Accepted):
93
+ * {
94
+ * success: true,
95
+ * processId: string,
96
+ * state: 'INITIALIZING',
97
+ * statusUrl: string,
98
+ * message: string
99
+ * }
100
+ */
101
+ router.post(
102
+ '/db-migrate',
103
+ catchAsyncError(async (req, res) => {
104
+ // Migration infrastructure is PostgreSQL-only, so hardcode dbType
105
+ const dbType = 'postgresql';
106
+ const { stage } = req.body;
107
+ // TODO: Extract userId from JWT token when auth is implemented
108
+ const userId = req.body.userId || 'admin';
109
+
110
+ console.log(`Migration trigger request: dbType=${dbType}, stage=${stage || 'auto-detect'}, userId=${userId}`);
111
+
112
+ try {
113
+ const result = await triggerMigrationUseCase.execute({
114
+ userId,
115
+ dbType,
116
+ stage,
117
+ });
118
+
119
+ // 202 Accepted - request accepted but not completed
120
+ res.status(202).json(result);
121
+ } catch (error) {
122
+ // Handle validation errors (400 Bad Request)
123
+ if (error instanceof TriggerValidationError) {
124
+ return res.status(400).json({
125
+ success: false,
126
+ error: error.message,
127
+ });
128
+ }
129
+
130
+ // Re-throw other errors for global error handler
131
+ throw error;
132
+ }
133
+ })
134
+ );
135
+
136
+ /**
137
+ * GET /db-migrate/status
138
+ *
139
+ * Check if database has pending migrations
140
+ *
141
+ * Query params:
142
+ * - stage: string (optional, defaults to STAGE env var or 'production')
143
+ *
144
+ * Response (200 OK):
145
+ * {
146
+ * upToDate: boolean,
147
+ * pendingMigrations: number,
148
+ * dbType: 'postgresql',
149
+ * stage: string,
150
+ * recommendation?: string (if migrations pending),
151
+ * error?: string (if database check failed)
152
+ * }
153
+ */
154
+ router.get(
155
+ '/db-migrate/status',
156
+ catchAsyncError(async (req, res) => {
157
+ const stage = req.query.stage || process.env.STAGE || 'production';
158
+
159
+ console.log(`Checking database state: stage=${stage}, worker=${workerFunctionName}`);
160
+
161
+ try {
162
+ // Invoke worker Lambda to check database state
163
+ const status = await getDatabaseStateUseCase.execute(stage);
164
+
165
+ res.status(200).json(status);
166
+ } catch (error) {
167
+ // Log full error for debugging
168
+ console.error('Database state check failed:', error);
169
+
170
+ // Return sanitized error to client
171
+ return res.status(500).json({
172
+ success: false,
173
+ error: 'Failed to check database state',
174
+ details: error.message,
175
+ });
176
+ }
177
+ })
178
+ );
179
+
180
+ /**
181
+ * GET /db-migrate/:migrationId
182
+ *
183
+ * Get migration status by migration ID
184
+ *
185
+ * Response (200 OK):
186
+ * {
187
+ * processId: string,
188
+ * type: 'DATABASE_MIGRATION',
189
+ * state: 'INITIALIZING' | 'RUNNING' | 'COMPLETED' | 'FAILED',
190
+ * context: {
191
+ * dbType: string,
192
+ * stage: string,
193
+ * migrationCommand: string (if started)
194
+ * },
195
+ * results: {
196
+ * success: boolean (if completed),
197
+ * duration: string (if completed),
198
+ * error: string (if failed)
199
+ * },
200
+ * createdAt: string,
201
+ * updatedAt: string
202
+ * }
203
+ */
204
+ router.get(
205
+ '/db-migrate/:migrationId',
206
+ catchAsyncError(async (req, res) => {
207
+ const { migrationId } = req.params;
208
+ const stage = req.query.stage || process.env.STAGE || 'production';
209
+
210
+ console.log(`Migration status request: migrationId=${migrationId}, stage=${stage}`);
211
+
212
+ try {
213
+ const status = await getStatusUseCase.execute(migrationId, stage);
214
+
215
+ res.status(200).json(status);
216
+ } catch (error) {
217
+ // Handle not found errors (404 Not Found)
218
+ if (error instanceof NotFoundError) {
219
+ return res.status(404).json({
220
+ success: false,
221
+ error: error.message,
222
+ });
223
+ }
224
+
225
+ // Handle validation errors (400 Bad Request)
226
+ if (error instanceof GetValidationError) {
227
+ return res.status(400).json({
228
+ success: false,
229
+ error: error.message,
230
+ });
231
+ }
232
+
233
+ // Re-throw other errors for global error handler
234
+ throw error;
235
+ }
236
+ })
237
+ );
238
+
239
+ // Minimal Lambda handler (avoids app-handler-helpers which loads core/index.js → user/**)
240
+ const serverlessHttp = require('serverless-http');
241
+ const express = require('express');
242
+ const cors = require('cors');
243
+
244
+ const app = express();
245
+ app.use(cors());
246
+ app.use(express.json());
247
+ app.use(router);
248
+ app.use((err, req, res, next) => {
249
+ console.error('Migration Router Error:', err);
250
+ res.status(500).json({ message: 'Internal Server Error' });
251
+ });
252
+
253
+ const handler = serverlessHttp(app);
254
+
255
+ module.exports = { handler, router };
256
+