@friggframework/core 2.0.0-next.12 → 2.0.0-next.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -1
- package/CLAUDE.md +830 -0
- package/README.md +996 -50
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/README.md +451 -0
- package/application/commands/admin-script-commands.js +359 -0
- package/application/commands/credential-commands.js +262 -0
- package/application/commands/entity-commands.js +294 -0
- package/application/commands/integration-commands.js +419 -0
- package/application/commands/integration-mapping-commands.js +25 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/report-commands.js +188 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/usage-commands.js +56 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +83 -0
- package/artifacts/repositories/artifact-repository-factory.js +19 -0
- package/artifacts/repositories/artifact-repository-interface.js +27 -0
- package/artifacts/repositories/artifact-repository-local.js +42 -0
- package/artifacts/repositories/artifact-repository-s3.js +61 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +715 -0
- package/core/Worker.js +55 -24
- package/core/create-handler.js +121 -38
- package/core/index.js +8 -1
- package/core/invocation-scope.js +225 -0
- package/core/parameters-to-env.js +257 -0
- package/core/secrets-to-env.js +7 -6
- package/core/ssm-preload.mjs +34 -0
- package/credential/repositories/credential-active-type.js +32 -0
- package/credential/repositories/credential-repository-documentdb.js +355 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +113 -0
- package/credential/repositories/credential-repository-mongo.js +294 -0
- package/credential/repositories/credential-repository-postgres.js +312 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +147 -0
- package/database/documentdb-encryption-service.js +334 -0
- package/database/documentdb-utils.js +196 -0
- package/database/encryption/README.md +863 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +404 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/integration-mapping-encryption.js +97 -0
- package/database/encryption/logger.js +82 -0
- package/database/encryption/prisma-encryption-extension.js +241 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +491 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +59 -57
- package/errors/index.js +6 -0
- package/errors/mapping-already-exists-error.js +14 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +5 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +5 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +386 -0
- package/generated/prisma-mongodb/index-browser.js +368 -0
- package/generated/prisma-mongodb/index.d.ts +27065 -0
- package/generated/prisma-mongodb/index.js +411 -0
- package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
- package/generated/prisma-mongodb/runtime/edge.js +35 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
- package/generated/prisma-mongodb/runtime/library.js +147 -0
- package/generated/prisma-mongodb/runtime/react-native.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-mongodb/schema.prisma +455 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +393 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +5 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +5 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +407 -0
- package/generated/prisma-postgresql/index-browser.js +389 -0
- package/generated/prisma-postgresql/index.d.ts +29904 -0
- package/generated/prisma-postgresql/index.js +432 -0
- package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
- package/generated/prisma-postgresql/runtime/edge.js +35 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
- package/generated/prisma-postgresql/runtime/library.js +147 -0
- package/generated/prisma-postgresql/runtime/react-native.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-postgresql/schema.prisma +436 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +414 -0
- package/handlers/WEBHOOKS.md +682 -0
- package/handlers/app-definition-loader.js +103 -0
- package/handlers/app-handler-helpers.js +20 -6
- package/handlers/backend-utils.js +272 -46
- package/handlers/database-migration-handler.js +236 -0
- package/handlers/integration-event-dispatcher.js +68 -0
- package/handlers/middleware/admin-auth.js +73 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +4 -15
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +329 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +87 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +31 -9
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +438 -0
- package/handlers/workers/dlq-processor.js +89 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +123 -47
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/EXTENSIONS.md +240 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/extension.js +254 -0
- package/integrations/index.js +20 -10
- package/integrations/integration-base.js +777 -74
- package/integrations/integration-router.js +399 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
- package/integrations/repositories/integration-mapping-query.js +193 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
- package/integrations/repositories/integration-mapping-repository.js +185 -0
- package/integrations/repositories/integration-repository-documentdb.js +354 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +219 -0
- package/integrations/repositories/integration-repository-mongo.js +502 -0
- package/integrations/repositories/integration-repository-postgres.js +550 -0
- package/integrations/repositories/process-repository-documentdb.js +311 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +136 -0
- package/integrations/repositories/process-repository-mongo.js +262 -0
- package/integrations/repositories/process-repository-postgres.js +380 -0
- package/integrations/repositories/process-update-ops-shared.js +112 -0
- package/integrations/repositories/report-id.js +13 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +113 -0
- package/integrations/tests/doubles/test-integration-repository.js +146 -0
- package/integrations/use-cases/add-entities-to-integration.js +92 -0
- package/integrations/use-cases/create-integration.js +253 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +130 -0
- package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +89 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/patch-integration-config.js +39 -0
- package/integrations/use-cases/process-errors.js +28 -0
- package/integrations/use-cases/remove-entities-from-integration.js +70 -0
- package/integrations/use-cases/update-integration-config.js +32 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +222 -0
- package/integrations/use-cases/update-process-state.js +163 -0
- package/integrations/utils/assert-entity-ids-input.js +42 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/jest.config.js +2 -0
- package/logs/.eslintrc.json +26 -1
- package/logs/__fixtures__/events.js +82 -0
- package/logs/__fixtures__/matchers.js +45 -0
- package/logs/__fixtures__/secrets.js +59 -0
- package/logs/__fixtures__/vectors.js +329 -0
- package/logs/__fixtures__/with-env.js +45 -0
- package/logs/context.js +109 -0
- package/logs/contract-keys.js +67 -0
- package/logs/debug-shims.js +46 -0
- package/logs/denied-keys.js +21 -0
- package/logs/index.js +14 -3
- package/logs/jest-logger-setup.js +25 -0
- package/logs/levels.js +71 -0
- package/logs/logger-runtime.js +137 -0
- package/logs/logger.js +134 -46
- package/logs/record.js +276 -0
- package/logs/redact.js +328 -0
- package/logs/serialize.js +329 -0
- package/logs/sinks.js +93 -0
- package/logs/summarize-event.js +163 -0
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +320 -0
- package/modules/repositories/module-repository-documentdb.js +350 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +145 -0
- package/modules/repositories/module-repository-mongo.js +436 -0
- package/modules/repositories/module-repository-postgres.js +481 -0
- package/modules/repositories/module-repository.js +369 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +561 -0
- package/modules/requester/requester.js +605 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +34 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +294 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +20 -0
- package/modules/utils/to-credential-id.js +14 -0
- package/package.json +91 -52
- package/prisma-mongodb/schema.prisma +455 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
- package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
- package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
- package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
- package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
- package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +436 -0
- package/queues/queue-delivery.js +41 -0
- package/queues/queuer-util.js +89 -21
- package/reporting/README.md +154 -0
- package/reporting/builtin-reports.js +6 -0
- package/reporting/index.js +13 -0
- package/reporting/report-base.js +49 -0
- package/reporting/reports/integrations-report.js +221 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/telemetry/README.md +331 -0
- package/telemetry/bind-telemetry-context.js +77 -0
- package/telemetry/canonical-counters.js +52 -0
- package/telemetry/exporters.js +85 -0
- package/telemetry/index.js +26 -0
- package/telemetry/instrument-handler.js +104 -0
- package/telemetry/no-op-telemetry.js +71 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +232 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +51 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +164 -0
- package/telemetry/telemetry-service.js +52 -0
- package/telemetry/usage-rollup-subscriber.js +116 -0
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/errors/index.d.ts +24 -10
- package/types/integrations/index.d.ts +3 -8
- package/types/logs/index.d.ts +61 -2
- package/types/module-plugin/index.d.ts +20 -69
- package/types/syncs/index.d.ts +0 -17
- package/usage/README.md +54 -0
- package/usage/index.js +17 -0
- package/usage/repositories/usage-repository-documentdb.js +194 -0
- package/usage/repositories/usage-repository-factory.js +25 -0
- package/usage/repositories/usage-repository-interface.js +37 -0
- package/usage/repositories/usage-repository-prisma.js +146 -0
- package/usage/tracked-metrics.js +38 -0
- package/usage/usage-windows.js +24 -0
- package/user/repositories/user-repository-documentdb.js +426 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +214 -0
- package/user/repositories/user-repository-mongo.js +323 -0
- package/user/repositories/user-repository-postgres.js +377 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +143 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +82 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.js +0 -165
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const { getEncryptionConfig } = require('../prisma');
|
|
2
|
+
const { Cryptor } = require('../../encrypt/Cryptor');
|
|
3
|
+
const {
|
|
4
|
+
getFieldsToDecryptOnRead,
|
|
5
|
+
getFieldsToEncryptOnWrite,
|
|
6
|
+
loadCustomEncryptionSchema,
|
|
7
|
+
} = require('./encryption-schema-registry');
|
|
8
|
+
const {
|
|
9
|
+
createFieldEncryptionService,
|
|
10
|
+
} = require('./prisma-encryption-extension');
|
|
11
|
+
|
|
12
|
+
let plainMappingEncryption = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The `IntegrationMapping` fields, `mapping` itself or a nested `mapping.*`
|
|
16
|
+
* path, that field-level encryption encrypts on write. An empty result is
|
|
17
|
+
* kept for the life of the process.
|
|
18
|
+
*
|
|
19
|
+
* @returns {string[]} Empty when every mapping path is written as plain JSON
|
|
20
|
+
*/
|
|
21
|
+
function getMappingFieldsEncryptedOnWrite() {
|
|
22
|
+
return mappingEncryption().encryptedOnWrite;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @throws {Error} When field-level encryption still encrypts `mapping`, or a
|
|
27
|
+
* nested `mapping.*` path, on write, naming the opt-out that lifts it
|
|
28
|
+
*/
|
|
29
|
+
function assertMappingWrittenUnencrypted() {
|
|
30
|
+
const fields = getMappingFieldsEncryptedOnWrite();
|
|
31
|
+
if (fields.length === 0) return;
|
|
32
|
+
|
|
33
|
+
const encrypted = fields
|
|
34
|
+
.map((field) => `IntegrationMapping.${field}`)
|
|
35
|
+
.join(', ');
|
|
36
|
+
const optOut = fields.map((field) => `'${field}'`).join(', ');
|
|
37
|
+
throw new Error(
|
|
38
|
+
`queryMappings: field-level encryption still encrypts ${encrypted} on write, so it cannot be queried. Opt out by adding ${optOut} to appDefinition.encryption.disable.IntegrationMapping.`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Decrypts rows that `queryMappings` read around the Prisma encryption
|
|
44
|
+
* extension, the way reads through the extension do: every
|
|
45
|
+
* `IntegrationMapping` field the schema lists, opted-out paths included, so a
|
|
46
|
+
* path written encrypted before its opt-out comes back plain.
|
|
47
|
+
*
|
|
48
|
+
* @param {Object[]} rows - Rows whose `mapping` is a JSON object
|
|
49
|
+
* @returns {Promise<Object[]>} `rows` itself when encryption is off or the
|
|
50
|
+
* schema lists no field besides `mapping`
|
|
51
|
+
*/
|
|
52
|
+
async function decryptQueriedMappings(rows) {
|
|
53
|
+
const { decryptor } = mappingEncryption();
|
|
54
|
+
if (!decryptor) return rows;
|
|
55
|
+
return decryptor.decryptFieldsInBulk('IntegrationMapping', rows);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function mappingEncryption() {
|
|
59
|
+
if (plainMappingEncryption) return plainMappingEncryption;
|
|
60
|
+
|
|
61
|
+
const encryption = currentMappingEncryption();
|
|
62
|
+
if (encryption.encryptedOnWrite.length === 0) {
|
|
63
|
+
plainMappingEncryption = encryption;
|
|
64
|
+
}
|
|
65
|
+
return encryption;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function currentMappingEncryption() {
|
|
69
|
+
const config = getEncryptionConfig();
|
|
70
|
+
if (!config.enabled) return { encryptedOnWrite: [], decryptor: null };
|
|
71
|
+
|
|
72
|
+
loadCustomEncryptionSchema();
|
|
73
|
+
const encryptedOnWrite = getFieldsToEncryptOnWrite(
|
|
74
|
+
'IntegrationMapping'
|
|
75
|
+
).filter((field) => field === 'mapping' || field.startsWith('mapping.'));
|
|
76
|
+
const decryptsBesidesMapping = getFieldsToDecryptOnRead(
|
|
77
|
+
'IntegrationMapping'
|
|
78
|
+
).some((field) => field !== 'mapping');
|
|
79
|
+
const decryptor = decryptsBesidesMapping
|
|
80
|
+
? createFieldEncryptionService(
|
|
81
|
+
new Cryptor({ shouldUseAws: config.method === 'kms' })
|
|
82
|
+
)
|
|
83
|
+
: null;
|
|
84
|
+
return { encryptedOnWrite, decryptor };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Test helper: forget a kept result. */
|
|
88
|
+
function resetMappingEncryptionCheck() {
|
|
89
|
+
plainMappingEncryption = null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
assertMappingWrittenUnencrypted,
|
|
94
|
+
decryptQueriedMappings,
|
|
95
|
+
getMappingFieldsEncryptedOnWrite,
|
|
96
|
+
resetMappingEncryptionCheck,
|
|
97
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encryption Logger
|
|
3
|
+
*
|
|
4
|
+
* Centralized logging for encryption operations.
|
|
5
|
+
* Prevents sensitive data leakage in production logs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const LOG_LEVELS = {
|
|
9
|
+
DEBUG: 0,
|
|
10
|
+
INFO: 1,
|
|
11
|
+
WARN: 2,
|
|
12
|
+
ERROR: 3,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class EncryptionLogger {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.minLevel = this._getMinLevel();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// FRIGG_LOG_LEVEL also takes TRACE and FATAL (ADR-048); this logger has
|
|
21
|
+
// no such levels, so they map to its nearest ones.
|
|
22
|
+
_getMinLevel() {
|
|
23
|
+
const level = (process.env.FRIGG_LOG_LEVEL || 'INFO').trim().toUpperCase();
|
|
24
|
+
const mapped = { TRACE: 'DEBUG', FATAL: 'ERROR' }[level] ?? level;
|
|
25
|
+
return LOG_LEVELS[mapped] ?? LOG_LEVELS.INFO;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_shouldLog(level) {
|
|
29
|
+
return LOG_LEVELS[level] >= this.minLevel;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_sanitize(message) {
|
|
33
|
+
// Remove potential key material or encrypted data from logs
|
|
34
|
+
if (typeof message === 'string') {
|
|
35
|
+
// Truncate long base64 strings that might be keys or encrypted data
|
|
36
|
+
return message.replace(/([A-Za-z0-9+/=]{50,})/g, (match) =>
|
|
37
|
+
`${match.substring(0, 10)}...[${match.length} chars]`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
debug(message, ...args) {
|
|
44
|
+
if (this._shouldLog('DEBUG')) {
|
|
45
|
+
console.log(`[Frigg Debug]`, this._sanitize(message), ...args);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
info(message, ...args) {
|
|
50
|
+
if (this._shouldLog('INFO')) {
|
|
51
|
+
console.log(`[Frigg]`, this._sanitize(message), ...args);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
warn(message, ...args) {
|
|
56
|
+
if (this._shouldLog('WARN')) {
|
|
57
|
+
console.warn(`[Frigg]`, this._sanitize(message), ...args);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
error(message, error) {
|
|
62
|
+
if (this._shouldLog('ERROR')) {
|
|
63
|
+
const sanitizedMessage = this._sanitize(message);
|
|
64
|
+
|
|
65
|
+
// In production, don't log stack traces with sensitive paths
|
|
66
|
+
const isProduction = process.env.STAGE === 'production';
|
|
67
|
+
|
|
68
|
+
if (error && !isProduction) {
|
|
69
|
+
console.error(`[Frigg]`, sanitizedMessage, error);
|
|
70
|
+
} else if (error) {
|
|
71
|
+
console.error(`[Frigg]`, sanitizedMessage, error.message);
|
|
72
|
+
} else {
|
|
73
|
+
console.error(`[Frigg]`, sanitizedMessage);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Singleton instance
|
|
80
|
+
const logger = new EncryptionLogger();
|
|
81
|
+
|
|
82
|
+
module.exports = { logger };
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma Client Extension for transparent field-level encryption.
|
|
3
|
+
* Intercepts Prisma queries to encrypt on write and decrypt on read.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
getEncryptedFields,
|
|
8
|
+
getFieldsToEncryptOnWrite,
|
|
9
|
+
getFieldsToDecryptOnRead,
|
|
10
|
+
} = require('./encryption-schema-registry');
|
|
11
|
+
const { FieldEncryptionService } = require('./field-encryption-service');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The FieldEncryptionService the extension runs, with field paths from the
|
|
15
|
+
* encryption schema registry.
|
|
16
|
+
*
|
|
17
|
+
* @param {import('../../encrypt/Cryptor').Cryptor} cryptor
|
|
18
|
+
* @returns {FieldEncryptionService}
|
|
19
|
+
*/
|
|
20
|
+
function createFieldEncryptionService(cryptor) {
|
|
21
|
+
return new FieldEncryptionService({
|
|
22
|
+
cryptor,
|
|
23
|
+
schema: {
|
|
24
|
+
getEncryptedFields,
|
|
25
|
+
getFieldsToEncryptOnWrite,
|
|
26
|
+
getFieldsToDecryptOnRead,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createEncryptionExtension({ cryptor, enabled = true }) {
|
|
32
|
+
if (!enabled) {
|
|
33
|
+
return (client) => client;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!cryptor) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'Cryptor instance required for encryption extension'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const encryptionService = createFieldEncryptionService(cryptor);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name: 'frigg-field-encryption',
|
|
46
|
+
query: {
|
|
47
|
+
$allModels: {
|
|
48
|
+
async create({ model, args, query }) {
|
|
49
|
+
if (args.data) {
|
|
50
|
+
args.data = await encryptionService.encryptFields(
|
|
51
|
+
model,
|
|
52
|
+
args.data
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const result = await query(args);
|
|
57
|
+
|
|
58
|
+
if (result) {
|
|
59
|
+
return await encryptionService.decryptFields(
|
|
60
|
+
model,
|
|
61
|
+
result
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
async createMany({ model, args, query }) {
|
|
69
|
+
if (args.data && Array.isArray(args.data)) {
|
|
70
|
+
args.data =
|
|
71
|
+
await encryptionService.encryptFieldsInBulk(
|
|
72
|
+
model,
|
|
73
|
+
args.data
|
|
74
|
+
);
|
|
75
|
+
} else if (args.data) {
|
|
76
|
+
args.data = await encryptionService.encryptFields(
|
|
77
|
+
model,
|
|
78
|
+
args.data
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return await query(args);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async update({ model, args, query }) {
|
|
86
|
+
if (args.data) {
|
|
87
|
+
args.data = await encryptionService.encryptFields(
|
|
88
|
+
model,
|
|
89
|
+
args.data
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = await query(args);
|
|
94
|
+
|
|
95
|
+
if (result) {
|
|
96
|
+
return await encryptionService.decryptFields(
|
|
97
|
+
model,
|
|
98
|
+
result
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return result;
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async updateMany({ model, args, query }) {
|
|
106
|
+
if (args.data) {
|
|
107
|
+
args.data = await encryptionService.encryptFields(
|
|
108
|
+
model,
|
|
109
|
+
args.data
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return await query(args);
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async upsert({ model, args, query }) {
|
|
117
|
+
if (args.create) {
|
|
118
|
+
args.create = await encryptionService.encryptFields(
|
|
119
|
+
model,
|
|
120
|
+
args.create
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (args.update) {
|
|
125
|
+
args.update = await encryptionService.encryptFields(
|
|
126
|
+
model,
|
|
127
|
+
args.update
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = await query(args);
|
|
132
|
+
|
|
133
|
+
if (result) {
|
|
134
|
+
return await encryptionService.decryptFields(
|
|
135
|
+
model,
|
|
136
|
+
result
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
async findUnique({ model, args, query }) {
|
|
144
|
+
const result = await query(args);
|
|
145
|
+
|
|
146
|
+
if (result) {
|
|
147
|
+
return await encryptionService.decryptFields(
|
|
148
|
+
model,
|
|
149
|
+
result
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return result;
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async findFirst({ model, args, query }) {
|
|
157
|
+
const result = await query(args);
|
|
158
|
+
|
|
159
|
+
if (result) {
|
|
160
|
+
return await encryptionService.decryptFields(
|
|
161
|
+
model,
|
|
162
|
+
result
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async findMany({ model, args, query }) {
|
|
170
|
+
const results = await query(args);
|
|
171
|
+
|
|
172
|
+
if (results && Array.isArray(results)) {
|
|
173
|
+
return await encryptionService.decryptFieldsInBulk(
|
|
174
|
+
model,
|
|
175
|
+
results
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return results;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
async delete({ model, args, query }) {
|
|
183
|
+
const result = await query(args);
|
|
184
|
+
|
|
185
|
+
if (result) {
|
|
186
|
+
return await encryptionService.decryptFields(
|
|
187
|
+
model,
|
|
188
|
+
result
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return result;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async deleteMany({ model, args, query }) {
|
|
196
|
+
return await query(args);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
async count({ model, args, query }) {
|
|
200
|
+
return await query(args);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
async aggregate({ model, args, query }) {
|
|
204
|
+
return await query(args);
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
async groupBy({ model, args, query }) {
|
|
208
|
+
return await query(args);
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async findFirstOrThrow({ model, args, query }) {
|
|
212
|
+
const result = await query(args);
|
|
213
|
+
|
|
214
|
+
if (result) {
|
|
215
|
+
return await encryptionService.decryptFields(
|
|
216
|
+
model,
|
|
217
|
+
result
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return result;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
async findUniqueOrThrow({ model, args, query }) {
|
|
225
|
+
const result = await query(args);
|
|
226
|
+
|
|
227
|
+
if (result) {
|
|
228
|
+
return await encryptionService.decryptFields(
|
|
229
|
+
model,
|
|
230
|
+
result
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result;
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = { createEncryptionExtension, createFieldEncryptionService };
|
package/database/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Database Module Index
|
|
3
|
+
* Exports Prisma client, connection utilities, and repositories
|
|
4
|
+
*
|
|
5
|
+
* Note: Frigg uses the Repository pattern for data access.
|
|
6
|
+
* Use repositories for data operations:
|
|
7
|
+
* - SyncRepository (syncs/sync-repository.js)
|
|
8
|
+
* - IntegrationRepository (integrations/integration-repository.js)
|
|
9
|
+
* - CredentialRepository (credential/credential-repository.js)
|
|
10
|
+
* etc.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { prisma, connectPrisma, disconnectPrisma } = require('./prisma');
|
|
14
|
+
const { TokenRepository } = require('../token/repositories/token-repository');
|
|
2
15
|
const {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
createObjectId,
|
|
6
|
-
} = require('./mongo');
|
|
7
|
-
const { IndividualUser } = require('./models/IndividualUser');
|
|
8
|
-
const { OrganizationUser } = require('./models/OrganizationUser');
|
|
9
|
-
const { State } = require('./models/State');
|
|
10
|
-
const { Token } = require('./models/Token');
|
|
11
|
-
const { UserModel } = require('./models/UserModel');
|
|
12
|
-
const { WebsocketConnection } = require('./models/WebsocketConnection');
|
|
16
|
+
WebsocketConnectionRepository,
|
|
17
|
+
} = require('../websocket/repositories/websocket-connection-repository');
|
|
13
18
|
|
|
14
19
|
module.exports = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
OrganizationUser,
|
|
21
|
-
State,
|
|
22
|
-
Token,
|
|
23
|
-
UserModel,
|
|
24
|
-
WebsocketConnection,
|
|
20
|
+
prisma,
|
|
21
|
+
connectPrisma,
|
|
22
|
+
disconnectPrisma,
|
|
23
|
+
TokenRepository,
|
|
24
|
+
WebsocketConnectionRepository,
|
|
25
25
|
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createEncryptionExtension,
|
|
3
|
+
} = require('./encryption/prisma-encryption-extension');
|
|
4
|
+
const { loadCustomEncryptionSchema } = require('./encryption/encryption-schema-registry');
|
|
5
|
+
const { logger } = require('./encryption/logger');
|
|
6
|
+
const { Cryptor } = require('../encrypt/Cryptor');
|
|
7
|
+
const config = require('./config');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ensures DATABASE_URL is set for MongoDB connections
|
|
11
|
+
* Falls back to MONGO_URI if DATABASE_URL is not set
|
|
12
|
+
* Infrastructure layer concern - maps legacy MONGO_URI to Prisma's expected DATABASE_URL
|
|
13
|
+
*
|
|
14
|
+
* Note: This should only be called when DB_TYPE is 'mongodb' or 'documentdb'
|
|
15
|
+
*/
|
|
16
|
+
function ensureMongoDbUrl() {
|
|
17
|
+
// If DATABASE_URL is already set, use it
|
|
18
|
+
if (process.env.DATABASE_URL && process.env.DATABASE_URL.trim()) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fallback to MONGO_URI for backwards compatibility with DocumentDB deployments
|
|
23
|
+
if (process.env.MONGO_URI && process.env.MONGO_URI.trim()) {
|
|
24
|
+
process.env.DATABASE_URL = process.env.MONGO_URI;
|
|
25
|
+
logger.debug('Using MONGO_URI as DATABASE_URL for Mongo-compatible connection');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Neither is set - error
|
|
30
|
+
throw new Error(
|
|
31
|
+
'DATABASE_URL or MONGO_URI environment variable must be set for MongoDB/DocumentDB'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getEncryptionConfig() {
|
|
36
|
+
const STAGE = process.env.STAGE || process.env.NODE_ENV || 'development';
|
|
37
|
+
const shouldBypassEncryption = ['dev', 'test', 'local'].includes(STAGE);
|
|
38
|
+
|
|
39
|
+
if (shouldBypassEncryption) {
|
|
40
|
+
return { enabled: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const hasKMS =
|
|
44
|
+
process.env.KMS_KEY_ARN && process.env.KMS_KEY_ARN.trim() !== '';
|
|
45
|
+
const hasAES =
|
|
46
|
+
process.env.AES_KEY_ID && process.env.AES_KEY_ID.trim() !== '';
|
|
47
|
+
|
|
48
|
+
if (!hasKMS && !hasAES) {
|
|
49
|
+
logger.warn(
|
|
50
|
+
'No encryption keys configured (KMS_KEY_ARN or AES_KEY_ID). ' +
|
|
51
|
+
'Field-level encryption disabled. Set STAGE=production and configure keys to enable.'
|
|
52
|
+
);
|
|
53
|
+
return { enabled: false };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
enabled: true,
|
|
58
|
+
method: hasKMS ? 'kms' : 'aes',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const prismaClientSingleton = () => {
|
|
63
|
+
let PrismaClient;
|
|
64
|
+
|
|
65
|
+
// Helper to try loading Prisma client from multiple locations
|
|
66
|
+
const loadPrismaClient = (dbType) => {
|
|
67
|
+
const paths = [
|
|
68
|
+
// Lambda layer location (when using Prisma Lambda layer)
|
|
69
|
+
`/opt/nodejs/node_modules/generated/prisma-${dbType}`,
|
|
70
|
+
// Local development location (relative to core package)
|
|
71
|
+
`../generated/prisma-${dbType}`,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const path of paths) {
|
|
75
|
+
try {
|
|
76
|
+
return require(path).PrismaClient;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// Continue to next path
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Cannot find Prisma client for ${dbType}. Tried paths: ${paths.join(', ')}`
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (config.DB_TYPE === 'mongodb' || config.DB_TYPE === 'documentdb') {
|
|
88
|
+
// Ensure DATABASE_URL is set (fallback to MONGO_URI if needed)
|
|
89
|
+
ensureMongoDbUrl();
|
|
90
|
+
PrismaClient = loadPrismaClient('mongodb');
|
|
91
|
+
} else if (config.DB_TYPE === 'postgresql') {
|
|
92
|
+
PrismaClient = loadPrismaClient('postgresql');
|
|
93
|
+
} else {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Unsupported database type: ${config.DB_TYPE}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let client = new PrismaClient({
|
|
100
|
+
log: process.env.PRISMA_LOG_LEVEL
|
|
101
|
+
? process.env.PRISMA_LOG_LEVEL.split(',')
|
|
102
|
+
: ['error', 'warn'],
|
|
103
|
+
errorFormat: 'pretty',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const encryptionConfig = getEncryptionConfig();
|
|
107
|
+
|
|
108
|
+
if (encryptionConfig.enabled) {
|
|
109
|
+
try {
|
|
110
|
+
// Load custom encryption schema from appDefinition before creating extension
|
|
111
|
+
loadCustomEncryptionSchema();
|
|
112
|
+
|
|
113
|
+
const cryptor = new Cryptor({
|
|
114
|
+
shouldUseAws: encryptionConfig.method === 'kms',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
client = client.$extends(
|
|
118
|
+
createEncryptionExtension({
|
|
119
|
+
cryptor,
|
|
120
|
+
enabled: true,
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
logger.info(
|
|
125
|
+
`Field-level encryption enabled using ${encryptionConfig.method.toUpperCase()}`
|
|
126
|
+
);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
logger.error(
|
|
129
|
+
'Failed to initialize encryption extension:',
|
|
130
|
+
error
|
|
131
|
+
);
|
|
132
|
+
logger.warn('Continuing without encryption...');
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
logger.info('Field-level encryption disabled');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return client;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const globalForPrisma = global;
|
|
142
|
+
|
|
143
|
+
// Lazy initialization - only create singleton when first accessed
|
|
144
|
+
function getPrismaClient() {
|
|
145
|
+
if (!globalForPrisma._prismaInstance) {
|
|
146
|
+
globalForPrisma._prismaInstance = prismaClientSingleton();
|
|
147
|
+
}
|
|
148
|
+
return globalForPrisma._prismaInstance;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Export a getter for lazy initialization
|
|
152
|
+
const prisma = new Proxy({}, {
|
|
153
|
+
get(target, prop) {
|
|
154
|
+
return getPrismaClient()[prop];
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
async function disconnectPrisma() {
|
|
159
|
+
await getPrismaClient().$disconnect();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function connectPrisma() {
|
|
163
|
+
await getPrismaClient().$connect();
|
|
164
|
+
|
|
165
|
+
// Initialize MongoDB schema - ensure all collections exist
|
|
166
|
+
// Only run for MongoDB/DocumentDB (not PostgreSQL)
|
|
167
|
+
// This prevents "Cannot create namespace in multi-document transaction" errors
|
|
168
|
+
if (config.DB_TYPE === 'mongodb' || config.DB_TYPE === 'documentdb') {
|
|
169
|
+
const { initializeMongoDBSchema } = require('./utils/mongodb-schema-init');
|
|
170
|
+
await initializeMongoDBSchema();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return getPrismaClient();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
prisma,
|
|
178
|
+
connectPrisma,
|
|
179
|
+
disconnectPrisma,
|
|
180
|
+
getEncryptionConfig,
|
|
181
|
+
ensureMongoDbUrl, // Exported for testing
|
|
182
|
+
};
|