@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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.
Files changed (237) hide show
  1. package/CHANGELOG.md +66 -67
  2. package/CLAUDE.md +148 -124
  3. package/README.md +165 -130
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
  8. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
  9. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
  10. package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
  12. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  13. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  14. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  15. package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
  16. package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
  17. package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
  18. package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
  19. package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
  20. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  21. package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
  22. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  23. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  24. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  25. package/application/commands/admin-script-commands.js +348 -0
  26. package/application/commands/integration-commands.js +12 -6
  27. package/application/index.js +5 -3
  28. package/assertions/get.js +17 -7
  29. package/associations/association.js +59 -57
  30. package/core/CLAUDE.md +222 -156
  31. package/core/Worker.js +33 -12
  32. package/credential/credential-router.js +262 -0
  33. package/credential/repositories/credential-repository-documentdb.js +71 -1
  34. package/credential/repositories/credential-repository-factory.js +4 -6
  35. package/credential/repositories/credential-repository-interface.js +8 -2
  36. package/credential/repositories/credential-repository-mongo.js +45 -4
  37. package/credential/repositories/credential-repository-postgres.js +44 -3
  38. package/credential/repositories/credential-repository.js +3 -1
  39. package/credential/use-cases/delete-credential-for-user.js +50 -0
  40. package/credential/use-cases/get-credential-for-user.js +21 -3
  41. package/credential/use-cases/list-credentials-for-user.js +31 -0
  42. package/credential/use-cases/reauthorize-credential.js +103 -0
  43. package/credential/use-cases/update-authentication-status.js +5 -2
  44. package/database/MONGODB_TRANSACTION_FIX.md +72 -52
  45. package/database/config.js +13 -59
  46. package/database/documentdb-encryption-service.js +29 -13
  47. package/database/documentdb-utils.js +21 -12
  48. package/database/encryption/README.md +53 -36
  49. package/database/encryption/encryption-schema-registry.js +29 -23
  50. package/database/encryption/field-encryption-service.js +15 -4
  51. package/database/encryption/logger.js +3 -2
  52. package/database/encryption/prisma-encryption-extension.js +5 -8
  53. package/database/prisma.js +22 -14
  54. package/database/repositories/health-check-repository-factory.js +7 -5
  55. package/database/repositories/health-check-repository-interface.js +15 -5
  56. package/database/repositories/health-check-repository-postgres.js +1 -1
  57. package/database/use-cases/check-database-health-use-case.js +3 -2
  58. package/database/use-cases/check-database-state-use-case.js +8 -7
  59. package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
  60. package/database/use-cases/get-migration-status-use-case.js +4 -2
  61. package/database/use-cases/run-database-migration-use-case.js +41 -9
  62. package/database/use-cases/test-encryption-use-case.js +4 -5
  63. package/database/use-cases/trigger-database-migration-use-case.js +9 -8
  64. package/database/utils/mongodb-collection-utils.js +11 -3
  65. package/database/utils/mongodb-schema-init.js +10 -4
  66. package/database/utils/prisma-runner.js +144 -74
  67. package/database/utils/prisma-schema-parser.js +2 -2
  68. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
  69. package/encrypt/Cryptor.js +54 -49
  70. package/encrypt/aes-encryption-key-provider.js +82 -0
  71. package/encrypt/encryption-key-provider-interface.js +50 -0
  72. package/errors/fetch-error.js +1 -1
  73. package/generated/prisma-mongodb/edge.js +59 -4
  74. package/generated/prisma-mongodb/index-browser.js +49 -1
  75. package/generated/prisma-mongodb/index.d.ts +4023 -61
  76. package/generated/prisma-mongodb/index.js +59 -4
  77. package/generated/prisma-mongodb/package.json +1 -1
  78. package/generated/prisma-mongodb/schema.prisma +87 -0
  79. package/generated/prisma-mongodb/wasm.js +59 -4
  80. package/generated/prisma-postgresql/edge.js +58 -4
  81. package/generated/prisma-postgresql/index-browser.js +48 -1
  82. package/generated/prisma-postgresql/index.d.ts +4738 -54
  83. package/generated/prisma-postgresql/index.js +58 -4
  84. package/generated/prisma-postgresql/package.json +1 -1
  85. package/generated/prisma-postgresql/schema.prisma +85 -0
  86. package/generated/prisma-postgresql/wasm.js +58 -4
  87. package/handlers/WEBHOOKS.md +104 -81
  88. package/handlers/app-definition-loader.js +53 -10
  89. package/handlers/app-handler-helpers.js +14 -3
  90. package/handlers/backend-utils.js +0 -1
  91. package/handlers/database-migration-handler.js +64 -45
  92. package/handlers/middleware/admin-auth.js +53 -0
  93. package/handlers/routers/HEALTHCHECK.md +170 -135
  94. package/handlers/routers/admin.js +474 -0
  95. package/handlers/routers/auth.js +51 -9
  96. package/handlers/routers/db-migration.handler.js +2 -3
  97. package/handlers/routers/db-migration.js +102 -66
  98. package/handlers/routers/docs.js +180 -0
  99. package/handlers/routers/health.js +84 -283
  100. package/handlers/routers/integration-defined-routers.js +44 -33
  101. package/handlers/routers/integration-webhook-routers.js +73 -52
  102. package/handlers/routers/middleware/loadUser.js +39 -0
  103. package/handlers/routers/middleware/requireAdmin.js +43 -0
  104. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  105. package/handlers/routers/user.js +30 -14
  106. package/handlers/routers/websocket.js +18 -4
  107. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  108. package/handlers/workers/db-migration.js +74 -35
  109. package/handlers/workers/integration-defined-workers.js +31 -20
  110. package/index.js +50 -16
  111. package/infrastructure/scheduler/index.js +15 -11
  112. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  113. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  114. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  115. package/integrations/index.js +4 -0
  116. package/integrations/integration-base.js +9 -6
  117. package/integrations/integration-router.js +1828 -157
  118. package/integrations/options.js +47 -20
  119. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  120. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  121. package/integrations/repositories/integration-repository-factory.js +6 -6
  122. package/integrations/repositories/integration-repository-interface.js +27 -9
  123. package/integrations/repositories/integration-repository-postgres.js +3 -3
  124. package/integrations/repositories/process-repository-factory.js +4 -7
  125. package/integrations/repositories/process-repository-interface.js +9 -5
  126. package/integrations/repositories/process-repository-mongo.js +7 -3
  127. package/integrations/repositories/process-repository-postgres.js +2 -2
  128. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  129. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  130. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  131. package/integrations/use-cases/create-integration.js +36 -32
  132. package/integrations/use-cases/create-process.js +20 -9
  133. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  134. package/integrations/use-cases/execute-proxy-request.js +564 -0
  135. package/integrations/use-cases/get-integration-for-user.js +25 -10
  136. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  137. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  138. package/integrations/use-cases/get-possible-integrations.js +1 -1
  139. package/integrations/use-cases/get-process.js +5 -6
  140. package/integrations/use-cases/index.js +3 -1
  141. package/integrations/use-cases/load-integration-context.js +1 -5
  142. package/integrations/use-cases/update-process-state.js +10 -8
  143. package/integrations/utils/map-integration-dto.js +9 -3
  144. package/jest-setup.js +1 -1
  145. package/jest-teardown.js +1 -1
  146. package/lambda/TimeoutCatcher.js +32 -30
  147. package/lambda/index.js +1 -1
  148. package/logs/index.js +3 -3
  149. package/modules/domain/entities/AuthorizationSession.js +131 -0
  150. package/modules/domain/entities/index.js +10 -0
  151. package/modules/module.js +4 -9
  152. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  153. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  154. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  155. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  156. package/modules/repositories/module-repository-documentdb.js +89 -41
  157. package/modules/repositories/module-repository-factory.js +4 -6
  158. package/modules/repositories/module-repository-interface.js +13 -0
  159. package/modules/repositories/module-repository-mongo.js +40 -5
  160. package/modules/repositories/module-repository-postgres.js +42 -5
  161. package/modules/repositories/module-repository.js +28 -0
  162. package/modules/requester/api-key.js +0 -2
  163. package/modules/requester/basic.js +0 -1
  164. package/modules/requester/oauth-2.js +3 -14
  165. package/modules/test/mock-api/definition.js +15 -10
  166. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  167. package/modules/tests/doubles/test-module-factory.js +10 -4
  168. package/modules/tests/doubles/test-module-repository.js +1 -1
  169. package/modules/use-cases/delete-module-entity.js +23 -0
  170. package/modules/use-cases/get-authorization-requirements.js +110 -0
  171. package/modules/use-cases/get-entities-for-user.js +1 -1
  172. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  173. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  174. package/modules/use-cases/get-module.js +12 -9
  175. package/modules/use-cases/process-authorization-callback.js +12 -6
  176. package/modules/use-cases/process-authorization-step.js +160 -0
  177. package/modules/use-cases/refresh-entity-options.js +8 -6
  178. package/modules/use-cases/start-authorization-session.js +86 -0
  179. package/modules/use-cases/test-module-auth.js +8 -6
  180. package/modules/use-cases/update-module-entity.js +27 -0
  181. package/modules/utils/map-module-dto.js +2 -2
  182. package/openapi/openapi-spec-generator.js +370 -0
  183. package/openapi/openapi-v1.yaml +1360 -0
  184. package/openapi/openapi-v2.yaml +1535 -0
  185. package/package.json +7 -9
  186. package/prisma-mongodb/schema.prisma +87 -0
  187. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  188. package/prisma-postgresql/schema.prisma +85 -0
  189. package/providers/index.js +13 -0
  190. package/providers/resolve-provider.js +93 -0
  191. package/queues/index.js +19 -0
  192. package/queues/providers/index.js +7 -0
  193. package/queues/providers/netlify-background-provider.js +76 -0
  194. package/queues/providers/qstash-queue-provider.js +122 -0
  195. package/queues/queue-client-interface.js +55 -0
  196. package/queues/queue-provider-factory.js +93 -0
  197. package/queues/queue-provider.js +46 -0
  198. package/queues/queuer-util.js +33 -25
  199. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  200. package/syncs/repositories/sync-repository-factory.js +4 -4
  201. package/syncs/repositories/sync-repository-interface.js +6 -2
  202. package/syncs/repositories/sync-repository-postgres.js +19 -13
  203. package/syncs/sync.js +104 -100
  204. package/token/repositories/token-repository-factory.js +4 -6
  205. package/types/assertions/index.d.ts +72 -72
  206. package/types/core/index.d.ts +98 -53
  207. package/types/errors/index.d.ts +61 -54
  208. package/types/eslint-config/index.d.ts +40 -40
  209. package/types/lambda/index.d.ts +26 -26
  210. package/types/logs/index.d.ts +4 -4
  211. package/types/prettier-config/index.d.ts +5 -5
  212. package/types/test-environment/index.d.ts +14 -14
  213. package/types/tsconfig.json +95 -93
  214. package/user/repositories/user-repository-factory.js +4 -4
  215. package/user/tests/doubles/test-user-repository.js +5 -2
  216. package/user/use-cases/authenticate-user.js +2 -4
  217. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  218. package/user/use-cases/create-individual-user.js +8 -7
  219. package/user/use-cases/create-organization-user.js +1 -1
  220. package/user/use-cases/create-token-for-user-id.js +1 -1
  221. package/user/use-cases/delete-user.js +47 -0
  222. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  223. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  224. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  225. package/user/use-cases/login-user.js +8 -8
  226. package/user/user.js +18 -4
  227. package/utils/backend-path.js +2 -2
  228. package/utils/index.js +4 -1
  229. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  230. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  231. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  232. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  233. package/websocket/repositories/websocket-connection-repository.js +26 -20
  234. package/websocket/websocket-message-sender-interface.js +38 -0
  235. package/database/adapters/lambda-invoker.js +0 -97
  236. package/database/repositories/migration-status-repository-s3.js +0 -137
  237. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
@@ -3,18 +3,60 @@ const path = require('node:path');
3
3
  const fs = require('fs-extra');
4
4
 
5
5
  /**
6
- * Loads the App definition from the nearest backend package
6
+ * Cached app definition, set via setAppDefinition().
7
+ * When set, loadAppDefinition() returns this instead of discovering
8
+ * the backend via process.cwd().
9
+ */
10
+ let cachedAppDefinition = null;
11
+
12
+ /**
13
+ * Pre-set the app definition so loadAppDefinition() can return it
14
+ * without needing process.cwd()-based discovery.
15
+ *
16
+ * This is critical for platforms like Netlify where:
17
+ * - nft/esbuild traces static require() calls to determine bundle contents
18
+ * - process.cwd() at runtime points to /var/task/, not the backend directory
19
+ * - The caller can use a static require('../../backend/index.js') to make
20
+ * the backend traceable, then pass the definition here
21
+ *
22
+ * @param {Object} definition - The app definition object (backend's Definition export)
23
+ */
24
+ function setAppDefinition(definition) {
25
+ cachedAppDefinition = definition;
26
+ }
27
+
28
+ /**
29
+ * Loads the App definition from the nearest backend package.
30
+ *
31
+ * If setAppDefinition() was called, returns the cached definition
32
+ * immediately (no filesystem discovery needed).
33
+ *
34
+ * Otherwise, discovers the backend via process.cwd() traversal.
35
+ *
36
+ * Returns the full appDefinition object plus convenience destructured fields
37
+ * for backward compatibility (integrations, userConfig).
38
+ *
7
39
  * @function loadAppDefinition
8
- * @description Searches for the nearest backend package.json, loads the corresponding index.js file,
9
- * and extracts the application definition containing integrations and user configuration.
10
- * @returns {{integrations: Array<object>, userConfig: object | null}} An object containing the application definition.
11
- * @throws {Error} Throws error if backend package.json cannot be found.
12
- * @throws {Error} Throws error if index.js file cannot be found in the backend directory.
40
+ * @returns {{
41
+ * integrations: Array<object>,
42
+ * userConfig: object | null,
43
+ * appDefinition: object
44
+ * }}
45
+ * @throws {Error} If backend package.json or index.js cannot be found.
13
46
  * @example
14
- * const { integrations, userConfig } = loadAppDefinition();
15
- * console.log(`Found ${integrations.length} integrations`);
47
+ * // Existing callers still work:
48
+ * const { integrations, userConfig } = loadAppDefinition();
49
+ *
50
+ * // New callers can access the full definition:
51
+ * const { appDefinition } = loadAppDefinition();
52
+ * console.log(appDefinition.provider); // 'aws' | 'netlify'
16
53
  */
17
54
  function loadAppDefinition() {
55
+ if (cachedAppDefinition) {
56
+ const { integrations = [], user: userConfig = null } = cachedAppDefinition;
57
+ return { integrations, userConfig, appDefinition: cachedAppDefinition };
58
+ }
59
+
18
60
  const backendPath = findNearestBackendPackageJson();
19
61
  if (!backendPath) {
20
62
  throw new Error('Could not find backend package.json');
@@ -30,9 +72,10 @@ function loadAppDefinition() {
30
72
  const appDefinition = backendJsFile.Definition;
31
73
 
32
74
  const { integrations = [], user: userConfig = null } = appDefinition;
33
- return { integrations, userConfig };
75
+ return { integrations, userConfig, appDefinition };
34
76
  }
35
77
 
36
78
  module.exports = {
37
79
  loadAppDefinition,
38
- };
80
+ setAppDefinition,
81
+ };
@@ -3,6 +3,7 @@ const express = require('express');
3
3
  const bodyParser = require('body-parser');
4
4
  const cors = require('cors');
5
5
  const Boom = require('@hapi/boom');
6
+ const loadUserManager = require('./routers/middleware/loadUser');
6
7
  const serverlessHttp = require('serverless-http');
7
8
 
8
9
  const createApp = (applyMiddleware) => {
@@ -19,6 +20,8 @@ const createApp = (applyMiddleware) => {
19
20
  })
20
21
  );
21
22
 
23
+ app.use(loadUserManager);
24
+
22
25
  if (applyMiddleware) applyMiddleware(app);
23
26
 
24
27
  // Handle sending error response and logging server errors to console
@@ -32,7 +35,6 @@ const createApp = (applyMiddleware) => {
32
35
  flushDebugLog(boomError);
33
36
  res.status(statusCode).json({ error: 'Internal Server Error' });
34
37
  } else {
35
- console.warn(`[Frigg] ${req.method} ${req.path} -> ${statusCode}: ${err.message}`);
36
38
  res.status(statusCode).json({ error: err.message });
37
39
  }
38
40
  });
@@ -40,9 +42,18 @@ const createApp = (applyMiddleware) => {
40
42
  return app;
41
43
  };
42
44
 
43
- function createAppHandler(eventName, router, shouldUseDatabase = true) {
45
+ function createAppHandler(
46
+ eventName,
47
+ router,
48
+ shouldUseDatabase = true,
49
+ basePath = null
50
+ ) {
44
51
  const app = createApp((app) => {
45
- app.use(router);
52
+ if (basePath) {
53
+ app.use(basePath, router);
54
+ } else {
55
+ app.use(router);
56
+ }
46
57
  });
47
58
  return createHandler({
48
59
  eventName,
@@ -101,7 +101,6 @@ const loadIntegrationForWebhook = async (integrationId) => {
101
101
  };
102
102
 
103
103
  const loadIntegrationForProcess = async (processId, integrationClass) => {
104
-
105
104
  const { processRepository, integrationRepository, moduleRepository } =
106
105
  initializeRepositories();
107
106
 
@@ -1,24 +1,24 @@
1
1
  /**
2
2
  * Database Migration Handler for AWS Lambda
3
- *
3
+ *
4
4
  * Executes Prisma migrations in a Lambda environment.
5
5
  * Based on AWS best practices for running migrations in serverless environments.
6
- *
6
+ *
7
7
  * Supported Commands:
8
8
  * - deploy: Apply pending migrations to the database (production-safe)
9
9
  * - reset: Reset database and apply all migrations (DANGEROUS - dev only)
10
- *
10
+ *
11
11
  * Usage:
12
12
  * // Via Lambda invoke
13
13
  * {
14
14
  * "command": "deploy" // or "reset"
15
15
  * }
16
- *
16
+ *
17
17
  * Requirements:
18
18
  * - Prisma CLI must be included in deployment or Lambda layer
19
19
  * - DATABASE_URL environment variable must be set
20
20
  * - VPC configuration for Aurora access
21
- *
21
+ *
22
22
  * Reference: https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-aws-lambda
23
23
  */
24
24
 
@@ -27,7 +27,7 @@ const path = require('path');
27
27
 
28
28
  /**
29
29
  * Execute Prisma migration command
30
- *
30
+ *
31
31
  * @param {string} command - Migration command ('deploy' or 'reset')
32
32
  * @param {string} schemaPath - Path to Prisma schema file
33
33
  * @returns {Promise<number>} Exit code
@@ -35,16 +35,18 @@ const path = require('path');
35
35
  async function executePrismaMigration(command, schemaPath) {
36
36
  console.log(`Executing Prisma migration: ${command}`);
37
37
  console.log(`Schema path: ${schemaPath}`);
38
- console.log(`Database URL: ${process.env.DATABASE_URL ? '[SET]' : '[NOT SET]'}`);
38
+ console.log(
39
+ `Database URL: ${process.env.DATABASE_URL ? '[SET]' : '[NOT SET]'}`
40
+ );
39
41
 
40
42
  return new Promise((resolve, reject) => {
41
43
  // Build command arguments
42
44
  const args = ['migrate', command];
43
-
45
+
44
46
  // Add command-specific options
45
47
  if (command === 'reset') {
46
- args.push('--force'); // Skip confirmation prompt
47
- args.push('--skip-generate'); // Skip client generation (already done in layer)
48
+ args.push('--force'); // Skip confirmation prompt
49
+ args.push('--skip-generate'); // Skip client generation (already done in layer)
48
50
  }
49
51
 
50
52
  // Add schema path if provided
@@ -53,7 +55,7 @@ async function executePrismaMigration(command, schemaPath) {
53
55
  }
54
56
 
55
57
  console.log(`Running: prisma ${args.join(' ')}`);
56
-
58
+
57
59
  // Execute Prisma CLI
58
60
  execFile(
59
61
  path.resolve('./node_modules/prisma/build/index.js'),
@@ -63,7 +65,7 @@ async function executePrismaMigration(command, schemaPath) {
63
65
  ...process.env,
64
66
  // Ensure Prisma uses the correct binary target
65
67
  PRISMA_CLI_BINARY_TARGETS: 'rhel-openssl-3.0.x',
66
- }
68
+ },
67
69
  },
68
70
  (error, stdout, stderr) => {
69
71
  // Log all output
@@ -75,7 +77,10 @@ async function executePrismaMigration(command, schemaPath) {
75
77
  }
76
78
 
77
79
  if (error) {
78
- console.error(`Migration ${command} exited with error:`, error.message);
80
+ console.error(
81
+ `Migration ${command} exited with error:`,
82
+ error.message
83
+ );
79
84
  console.error(`Exit code: ${error.code || 1}`);
80
85
  resolve(error.code || 1);
81
86
  } else {
@@ -92,11 +97,11 @@ async function executePrismaMigration(command, schemaPath) {
92
97
  */
93
98
  function validateCommand(command) {
94
99
  const validCommands = ['deploy', 'reset'];
95
-
100
+
96
101
  if (!validCommands.includes(command)) {
97
102
  throw new Error(
98
103
  `Invalid migration command: "${command}". ` +
99
- `Valid commands are: ${validCommands.join(', ')}`
104
+ `Valid commands are: ${validCommands.join(', ')}`
100
105
  );
101
106
  }
102
107
 
@@ -106,10 +111,12 @@ function validateCommand(command) {
106
111
  if (stage === 'production' || stage === 'prod') {
107
112
  throw new Error(
108
113
  'BLOCKED: "reset" command is not allowed in production environment. ' +
109
- 'This command would delete all data. Use "deploy" instead.'
114
+ 'This command would delete all data. Use "deploy" instead.'
110
115
  );
111
116
  }
112
- console.warn('⚠️ WARNING: "reset" will DELETE all data and reset the database!');
117
+ console.warn(
118
+ '⚠️ WARNING: "reset" will DELETE all data and reset the database!'
119
+ );
113
120
  }
114
121
  }
115
122
 
@@ -119,14 +126,17 @@ function validateCommand(command) {
119
126
  function getSchemaPath() {
120
127
  // In Lambda, schemas are in @friggframework/core/generated/
121
128
  const baseSchemaPath = './node_modules/@friggframework/core/generated';
122
-
129
+
123
130
  // Check if Postgres is enabled
124
- if (process.env.DATABASE_URL?.includes('postgresql') || process.env.DATABASE_URL?.includes('postgres')) {
131
+ if (
132
+ process.env.DATABASE_URL?.includes('postgresql') ||
133
+ process.env.DATABASE_URL?.includes('postgres')
134
+ ) {
125
135
  const schemaPath = `${baseSchemaPath}/prisma-postgresql/schema.prisma`;
126
136
  console.log(`Using PostgreSQL schema: ${schemaPath}`);
127
137
  return schemaPath;
128
138
  }
129
-
139
+
130
140
  // Check if MongoDB is enabled
131
141
  if (process.env.DATABASE_URL?.includes('mongodb')) {
132
142
  const schemaPath = `${baseSchemaPath}/prisma-mongodb/schema.prisma`;
@@ -135,13 +145,15 @@ function getSchemaPath() {
135
145
  }
136
146
 
137
147
  // Default to PostgreSQL
138
- console.log('DATABASE_URL not set or database type unknown, defaulting to PostgreSQL');
148
+ console.log(
149
+ 'DATABASE_URL not set or database type unknown, defaulting to PostgreSQL'
150
+ );
139
151
  return `${baseSchemaPath}/prisma-postgresql/schema.prisma`;
140
152
  }
141
153
 
142
154
  /**
143
155
  * Lambda handler for database migrations
144
- *
156
+ *
145
157
  * @param {Object} event - Lambda event
146
158
  * @param {string} event.command - Migration command ('deploy' or 'reset')
147
159
  * @param {Object} context - Lambda context
@@ -149,41 +161,48 @@ function getSchemaPath() {
149
161
  */
150
162
  exports.handler = async (event, context) => {
151
163
  const startTime = Date.now();
152
-
164
+
153
165
  console.log('='.repeat(60));
154
166
  console.log('Database Migration Handler');
155
167
  console.log('='.repeat(60));
156
168
  console.log('Event:', JSON.stringify(event, null, 2));
157
- console.log('Context:', JSON.stringify({
158
- functionName: context.functionName,
159
- functionVersion: context.functionVersion,
160
- memoryLimitInMB: context.memoryLimitInMB,
161
- logGroupName: context.logGroupName,
162
- }, null, 2));
163
-
169
+ console.log(
170
+ 'Context:',
171
+ JSON.stringify(
172
+ {
173
+ functionName: context.functionName,
174
+ functionVersion: context.functionVersion,
175
+ memoryLimitInMB: context.memoryLimitInMB,
176
+ logGroupName: context.logGroupName,
177
+ },
178
+ null,
179
+ 2
180
+ )
181
+ );
182
+
164
183
  try {
165
184
  // Get migration command (default to 'deploy')
166
185
  const command = event.command || 'deploy';
167
-
186
+
168
187
  // Validate command
169
188
  validateCommand(command);
170
-
189
+
171
190
  // Check required environment variables
172
191
  if (!process.env.DATABASE_URL) {
173
192
  throw new Error(
174
193
  'DATABASE_URL environment variable is not set. ' +
175
- 'Cannot connect to database for migrations.'
194
+ 'Cannot connect to database for migrations.'
176
195
  );
177
196
  }
178
-
197
+
179
198
  // Determine schema path
180
199
  const schemaPath = getSchemaPath();
181
-
200
+
182
201
  // Execute migration
183
202
  const exitCode = await executePrismaMigration(command, schemaPath);
184
-
203
+
185
204
  const duration = Date.now() - startTime;
186
-
205
+
187
206
  if (exitCode === 0) {
188
207
  const result = {
189
208
  success: true,
@@ -192,26 +211,27 @@ exports.handler = async (event, context) => {
192
211
  duration: `${duration}ms`,
193
212
  timestamp: new Date().toISOString(),
194
213
  };
195
-
214
+
196
215
  console.log('='.repeat(60));
197
216
  console.log('Migration completed successfully');
198
217
  console.log(JSON.stringify(result, null, 2));
199
218
  console.log('='.repeat(60));
200
-
219
+
201
220
  return result;
202
221
  } else {
203
- throw new Error(`Migration ${command} failed with exit code ${exitCode}`);
222
+ throw new Error(
223
+ `Migration ${command} failed with exit code ${exitCode}`
224
+ );
204
225
  }
205
-
206
226
  } catch (error) {
207
227
  const duration = Date.now() - startTime;
208
-
228
+
209
229
  console.error('='.repeat(60));
210
230
  console.error('Migration failed');
211
231
  console.error('Error:', error.message);
212
232
  console.error('Stack:', error.stack);
213
233
  console.error('='.repeat(60));
214
-
234
+
215
235
  const errorResult = {
216
236
  success: false,
217
237
  command: event.command || 'unknown',
@@ -219,9 +239,8 @@ exports.handler = async (event, context) => {
219
239
  duration: `${duration}ms`,
220
240
  timestamp: new Date().toISOString(),
221
241
  };
222
-
242
+
223
243
  // Return error (don't throw) so Lambda doesn't retry
224
244
  return errorResult;
225
245
  }
226
246
  };
227
-
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Admin Auth Middleware
3
+ *
4
+ * Shared authentication middleware for all admin endpoints:
5
+ * - /admin/db-migrate/*
6
+ * - /admin/scripts/*
7
+ *
8
+ * Uses simple ENV-based API key validation.
9
+ * Expects: x-frigg-admin-api-key header
10
+ */
11
+
12
+ /**
13
+ * Validate admin API key from request header
14
+ * @param {import('express').Request} req
15
+ * @param {import('express').Response} res
16
+ * @param {import('express').NextFunction} next
17
+ */
18
+ function validateAdminApiKey(req, res, next) {
19
+ const expectedKey = process.env.ADMIN_API_KEY;
20
+
21
+ // Check if admin API key is configured
22
+ if (!expectedKey) {
23
+ console.error('ADMIN_API_KEY environment variable not configured');
24
+ return res.status(401).json({
25
+ error: 'Unauthorized',
26
+ message: 'Admin API key not configured'
27
+ });
28
+ }
29
+
30
+ const apiKey = req.headers['x-frigg-admin-api-key'];
31
+
32
+ // Check if header is present
33
+ if (!apiKey) {
34
+ console.error('Missing x-frigg-admin-api-key header');
35
+ return res.status(401).json({
36
+ error: 'Unauthorized',
37
+ message: 'x-frigg-admin-api-key header required'
38
+ });
39
+ }
40
+
41
+ // Validate key
42
+ if (apiKey !== expectedKey) {
43
+ console.error('Invalid admin API key provided');
44
+ return res.status(401).json({
45
+ error: 'Unauthorized',
46
+ message: 'Invalid admin API key'
47
+ });
48
+ }
49
+
50
+ next();
51
+ }
52
+
53
+ module.exports = { validateAdminApiKey };