@friggframework/core 2.0.0-next.11 → 2.0.0-next.110
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/CLAUDE.md +702 -0
- package/README.md +999 -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 +336 -0
- package/application/commands/integration-commands.js +356 -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 +700 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +189 -5
- package/core/parameters-to-env.js +257 -0
- 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 +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +192 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +401 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +230 -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 +15 -7
- package/errors/index.js +2 -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 +653 -0
- package/handlers/app-definition-loader.js +67 -0
- package/handlers/app-handler-helpers.js +3 -3
- package/handlers/backend-utils.js +271 -42
- package/handlers/database-migration-handler.js +227 -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 +80 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +5 -3
- 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 +427 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +102 -46
- 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 +730 -74
- package/integrations/integration-router.js +398 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +303 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +120 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +183 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +255 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +322 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +189 -0
- package/integrations/repositories/integration-repository-mongo.js +450 -0
- package/integrations/repositories/integration-repository-postgres.js +492 -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/create-integration.js +215 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +119 -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/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/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +307 -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 +555 -0
- package/modules/requester/requester.js +542 -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 +243 -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 +18 -0
- package/package.json +92 -50
- 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/queuer-util.js +103 -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 +73 -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 +87 -0
- package/telemetry/no-op-telemetry.js +67 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +213 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +40 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +147 -0
- package/telemetry/telemetry-service.js +51 -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/integrations/index.d.ts +3 -8
- 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 +458 -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 +127 -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 +77 -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,51 @@
|
|
|
1
|
+
const { NoOpTelemetry } = require('./no-op-telemetry');
|
|
2
|
+
const { createTelemetryEventBus } = require('./telemetry-event-bus');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {object} TelemetryService The vendor-neutral telemetry port. Both
|
|
6
|
+
* NoOpTelemetry and OtelTelemetry implement it (duck-typed; the bound wrapper
|
|
7
|
+
* from bindTelemetryContext is a plain object that also satisfies it):
|
|
8
|
+
* count(name, value?, attributes?, context?) · event(name, attributes?, context?)
|
|
9
|
+
* span(name, fn) · startSpan(name?) · withContext(context, fn)
|
|
10
|
+
* on(eventType, cb) · forceFlush() · shutdown() · isEnabled()
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Determine whether an exporter descriptor means "emit nothing".
|
|
15
|
+
* Absent config or `{ type: 'none' }` (or `'noop'`) → no-op.
|
|
16
|
+
*/
|
|
17
|
+
function isNoOpExporter(exporter) {
|
|
18
|
+
if (!exporter) return true;
|
|
19
|
+
const type = exporter.type;
|
|
20
|
+
return !type || type === 'none' || type === 'noop';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create the vendor-neutral telemetry service — the adapter selector. Integration
|
|
25
|
+
* code uses the returned instance (`this.telemetry`) and never imports a backend
|
|
26
|
+
* SDK. With no exporter configured it returns `NoOpTelemetry` (loads zero OTel);
|
|
27
|
+
* a real exporter lazily constructs `OtelTelemetry`.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} [options]
|
|
30
|
+
* @param {object} [options.exporter] Exporter descriptor, e.g. `{ type: 'otlp', endpoint }`.
|
|
31
|
+
* @param {object} [options.resource] Resource attributes, e.g. `{ service, stage }`.
|
|
32
|
+
* @param {number} [options.sampleRatio] Parent-based sampling ratio (0..1).
|
|
33
|
+
* @param {object} [options.bus] Event bus to reuse (defaults to a fresh one).
|
|
34
|
+
*/
|
|
35
|
+
function createTelemetry(options = {}) {
|
|
36
|
+
// The internal event stream is always on and independent of OTel export:
|
|
37
|
+
// the usage rollup + plugin taps must work even when
|
|
38
|
+
// the OTel exporter is a no-op. The bus is pure JS, so this does not load
|
|
39
|
+
// any OTel module on the no-op path.
|
|
40
|
+
const bus = options.bus || createTelemetryEventBus();
|
|
41
|
+
|
|
42
|
+
if (isNoOpExporter(options.exporter)) {
|
|
43
|
+
return new NoOpTelemetry({ bus });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Lazy-required so the no-op path never loads the OTel SDK.
|
|
47
|
+
const { OtelTelemetry } = require('./otel-telemetry');
|
|
48
|
+
return new OtelTelemetry({ ...options, bus });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { createTelemetry, isNoOpExporter };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const { computeUsageWindows } = require('../usage/usage-windows');
|
|
2
|
+
|
|
3
|
+
const WEBHOOK_EVENT_NAMES = new Set(['ON_WEBHOOK']);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Framework auto-signal metric names → the canonical usage key they feed.
|
|
7
|
+
* Resolvers receive (attributes, context) and may return null to
|
|
8
|
+
* decline. Handler invocations map by event: USER_ACTION → user_actions; the
|
|
9
|
+
* DB-connected `ON_WEBHOOK` queue dispatch → webhooks.received (per-integration,
|
|
10
|
+
* and where a durable write is actually possible — the HTTP receipt handler is
|
|
11
|
+
* DB-free so its buffer is discarded).
|
|
12
|
+
*/
|
|
13
|
+
const METRIC_TO_CANONICAL = {
|
|
14
|
+
'frigg.apimodule.requests': () => 'api.requests',
|
|
15
|
+
'frigg.handler.invocations': (attrs, ctx) => {
|
|
16
|
+
if (attrs && attrs.event === 'USER_ACTION') return 'user_actions';
|
|
17
|
+
if (ctx && WEBHOOK_EVENT_NAMES.has(ctx.event_name)) {
|
|
18
|
+
return 'webhooks.received';
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The built-in usage-rollup subscriber. Subscribes to the
|
|
26
|
+
* telemetry event bus and folds *declared* counters (canonical or custom) into
|
|
27
|
+
* the durable usage store. It buffers within an invocation and writes on
|
|
28
|
+
* `flush()` (no timers — Lambda-safe), or drops the buffer on `discard()` for an
|
|
29
|
+
* SQS redelivery so at-least-once delivery does not double-count (approximate
|
|
30
|
+
* accuracy contract).
|
|
31
|
+
*
|
|
32
|
+
* Attribution: high-cardinality ids never ride metric labels, so integrationId
|
|
33
|
+
* comes from the bus-only `context` (populated from the ambient handler context)
|
|
34
|
+
* and otherwise falls back to the bounded integration_type.
|
|
35
|
+
*/
|
|
36
|
+
function createUsageRollupSubscriber({
|
|
37
|
+
telemetry,
|
|
38
|
+
usageRepository,
|
|
39
|
+
trackedMetrics = new Set(),
|
|
40
|
+
now = () => new Date(),
|
|
41
|
+
}) {
|
|
42
|
+
// Buffer key is a JSON tuple [integrationId, integrationType, metric, window]
|
|
43
|
+
// — collision-proof for developer-defined custom keys / names.
|
|
44
|
+
let buffer = new Map();
|
|
45
|
+
|
|
46
|
+
function resolveUsageKey(name, attributes, context) {
|
|
47
|
+
if (trackedMetrics.has(name)) return name;
|
|
48
|
+
const resolver = METRIC_TO_CANONICAL[name];
|
|
49
|
+
const canonical = resolver ? resolver(attributes || {}, context) : null;
|
|
50
|
+
return canonical && trackedMetrics.has(canonical) ? canonical : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function onMetric(payload) {
|
|
54
|
+
try {
|
|
55
|
+
const { name, value = 1, attributes = {}, context } = payload;
|
|
56
|
+
const metric = resolveUsageKey(name, attributes, context);
|
|
57
|
+
if (!metric) return;
|
|
58
|
+
|
|
59
|
+
const integrationType =
|
|
60
|
+
context?.integrationType || attributes.integration_type || null;
|
|
61
|
+
if (!integrationType) return; // cannot attribute — skip
|
|
62
|
+
|
|
63
|
+
const integrationId = context?.integrationId || integrationType;
|
|
64
|
+
|
|
65
|
+
for (const window of computeUsageWindows(now())) {
|
|
66
|
+
const key = JSON.stringify([
|
|
67
|
+
integrationId,
|
|
68
|
+
integrationType,
|
|
69
|
+
metric,
|
|
70
|
+
window,
|
|
71
|
+
]);
|
|
72
|
+
buffer.set(key, (buffer.get(key) || 0) + value);
|
|
73
|
+
}
|
|
74
|
+
} catch (_) {
|
|
75
|
+
// Rollup must never break emission.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const unsubscribe = telemetry.on('metric', onMetric);
|
|
80
|
+
|
|
81
|
+
async function flush() {
|
|
82
|
+
if (buffer.size === 0) return;
|
|
83
|
+
const pending = buffer;
|
|
84
|
+
buffer = new Map();
|
|
85
|
+
for (const [key, value] of pending) {
|
|
86
|
+
const [integrationId, integrationType, metric, window] =
|
|
87
|
+
JSON.parse(key);
|
|
88
|
+
try {
|
|
89
|
+
await usageRepository.increment({
|
|
90
|
+
integrationId,
|
|
91
|
+
integrationType,
|
|
92
|
+
metric,
|
|
93
|
+
window,
|
|
94
|
+
value,
|
|
95
|
+
});
|
|
96
|
+
} catch (_) {
|
|
97
|
+
// A single failed write must not abort the rest of the flush.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function discard() {
|
|
103
|
+
buffer = new Map();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
flush,
|
|
108
|
+
discard,
|
|
109
|
+
unsubscribe,
|
|
110
|
+
get bufferSize() {
|
|
111
|
+
return buffer.size;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { createUsageRollupSubscriber, METRIC_TO_CANONICAL };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const bcrypt = require('bcryptjs');
|
|
3
|
+
const {
|
|
4
|
+
toObjectId,
|
|
5
|
+
fromObjectId,
|
|
6
|
+
findMany,
|
|
7
|
+
findOne,
|
|
8
|
+
insertOne,
|
|
9
|
+
deleteOne,
|
|
10
|
+
deleteMany,
|
|
11
|
+
} = require('../../database/documentdb-utils');
|
|
12
|
+
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
13
|
+
const { ClientSafeError } = require('../../errors');
|
|
14
|
+
|
|
15
|
+
const BCRYPT_ROUNDS = 10;
|
|
16
|
+
|
|
17
|
+
class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
this.prisma = prisma;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async createTokenWithExpire(userId, rawToken, minutes) {
|
|
24
|
+
const tokenHash = await bcrypt.hash(rawToken, BCRYPT_ROUNDS);
|
|
25
|
+
const expires = new Date(Date.now() + minutes * 60000);
|
|
26
|
+
const now = new Date();
|
|
27
|
+
const document = {
|
|
28
|
+
token: tokenHash,
|
|
29
|
+
expires,
|
|
30
|
+
userId: toObjectId(userId),
|
|
31
|
+
created: now,
|
|
32
|
+
};
|
|
33
|
+
const insertedId = await insertOne(this.prisma, 'Token', document);
|
|
34
|
+
const created = await findOne(this.prisma, 'Token', {
|
|
35
|
+
_id: insertedId,
|
|
36
|
+
});
|
|
37
|
+
return this._mapToken(created);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async validateAndGetToken(tokenObj) {
|
|
41
|
+
const objectId = toObjectId(tokenObj.id);
|
|
42
|
+
if (!objectId) {
|
|
43
|
+
throw new ClientSafeError(
|
|
44
|
+
'Invalid Token: Token does not exist',
|
|
45
|
+
401
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const record = await findOne(this.prisma, 'Token', { _id: objectId });
|
|
49
|
+
if (!record) {
|
|
50
|
+
throw new ClientSafeError(
|
|
51
|
+
'Invalid Token: Token does not exist',
|
|
52
|
+
401
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const isValid = await bcrypt.compare(tokenObj.token, record.token);
|
|
56
|
+
if (!isValid) {
|
|
57
|
+
throw new ClientSafeError(
|
|
58
|
+
'Invalid Token: Token does not match',
|
|
59
|
+
401
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (record.expires && new Date(record.expires) < new Date()) {
|
|
63
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
64
|
+
}
|
|
65
|
+
return this._mapToken(record);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async findTokenById(tokenId) {
|
|
69
|
+
const objectId = toObjectId(tokenId);
|
|
70
|
+
if (!objectId) return null;
|
|
71
|
+
const record = await findOne(this.prisma, 'Token', { _id: objectId });
|
|
72
|
+
return record ? this._mapToken(record) : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async findTokensByUserId(userId) {
|
|
76
|
+
const objectId = toObjectId(userId);
|
|
77
|
+
const filter = objectId ? { userId: objectId } : {};
|
|
78
|
+
const records = await findMany(this.prisma, 'Token', filter);
|
|
79
|
+
return records.map((record) => this._mapToken(record));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async deleteToken(tokenId) {
|
|
83
|
+
const objectId = toObjectId(tokenId);
|
|
84
|
+
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
85
|
+
const result = await deleteOne(this.prisma, 'Token', { _id: objectId });
|
|
86
|
+
const deleted = result?.n ?? 0;
|
|
87
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async deleteExpiredTokens() {
|
|
91
|
+
const result = await deleteMany(this.prisma, 'Token', {
|
|
92
|
+
expires: { $lt: new Date() },
|
|
93
|
+
});
|
|
94
|
+
const deleted = result?.n ?? 0;
|
|
95
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async deleteTokensByUserId(userId) {
|
|
99
|
+
const objectId = toObjectId(userId);
|
|
100
|
+
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
101
|
+
const result = await deleteMany(this.prisma, 'Token', {
|
|
102
|
+
userId: objectId,
|
|
103
|
+
});
|
|
104
|
+
const deleted = result?.n ?? 0;
|
|
105
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
createJSONToken(token, rawToken) {
|
|
109
|
+
return JSON.stringify({
|
|
110
|
+
id: token.id,
|
|
111
|
+
token: rawToken,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
createBase64BufferToken(token, rawToken) {
|
|
116
|
+
const jsonVal = this.createJSONToken(token, rawToken);
|
|
117
|
+
return Buffer.from(jsonVal).toString('base64');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getJSONTokenFromBase64BufferToken(buffer) {
|
|
121
|
+
const tokenStr = Buffer.from(buffer.trim(), 'base64').toString('ascii');
|
|
122
|
+
return JSON.parse(tokenStr);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_mapToken(record) {
|
|
126
|
+
if (!record) return null;
|
|
127
|
+
return {
|
|
128
|
+
id: fromObjectId(record._id),
|
|
129
|
+
token: record.token,
|
|
130
|
+
expires: record.expires ? new Date(record.expires) : null,
|
|
131
|
+
userId: fromObjectId(record.userId),
|
|
132
|
+
created: record.created ? new Date(record.created) : null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { TokenRepositoryDocumentDB };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { TokenRepositoryMongo } = require('./token-repository-mongo');
|
|
2
|
+
const { TokenRepositoryPostgres } = require('./token-repository-postgres');
|
|
3
|
+
const {
|
|
4
|
+
TokenRepositoryDocumentDB,
|
|
5
|
+
} = require('./token-repository-documentdb');
|
|
6
|
+
const config = require('../../database/config');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Token Repository Factory
|
|
10
|
+
* Creates the appropriate repository adapter based on database type
|
|
11
|
+
*
|
|
12
|
+
* @returns {TokenRepositoryInterface} Configured repository adapter
|
|
13
|
+
*/
|
|
14
|
+
function createTokenRepository() {
|
|
15
|
+
const dbType = config.DB_TYPE;
|
|
16
|
+
|
|
17
|
+
switch (dbType) {
|
|
18
|
+
case 'mongodb':
|
|
19
|
+
return new TokenRepositoryMongo();
|
|
20
|
+
|
|
21
|
+
case 'postgresql':
|
|
22
|
+
return new TokenRepositoryPostgres();
|
|
23
|
+
|
|
24
|
+
case 'documentdb':
|
|
25
|
+
return new TokenRepositoryDocumentDB();
|
|
26
|
+
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
createTokenRepository,
|
|
36
|
+
// Export adapters for direct testing
|
|
37
|
+
TokenRepositoryMongo,
|
|
38
|
+
TokenRepositoryPostgres,
|
|
39
|
+
TokenRepositoryDocumentDB,
|
|
40
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for token persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Note: Currently, Token model has identical structure across MongoDB and PostgreSQL,
|
|
11
|
+
* so TokenRepository serves both. This interface exists for consistency and
|
|
12
|
+
* future-proofing if database-specific implementations become needed.
|
|
13
|
+
*
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
16
|
+
class TokenRepositoryInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Create token with expiration
|
|
19
|
+
*
|
|
20
|
+
* @param {string|number} userId - User ID
|
|
21
|
+
* @param {string} rawToken - Raw unhashed token
|
|
22
|
+
* @param {number} minutes - Minutes until expiration
|
|
23
|
+
* @returns {Promise<Object>} Created token object
|
|
24
|
+
* @abstract
|
|
25
|
+
*/
|
|
26
|
+
async createTokenWithExpire(userId, rawToken, minutes) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'Method createTokenWithExpire must be implemented by subclass'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validate and get token
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} tokenObj - Token object with id and token
|
|
36
|
+
* @returns {Promise<Object>} Validated token object
|
|
37
|
+
* @abstract
|
|
38
|
+
*/
|
|
39
|
+
async validateAndGetToken(tokenObj) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Method validateAndGetToken must be implemented by subclass'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Find token by ID
|
|
47
|
+
*
|
|
48
|
+
* @param {string|number} tokenId - Token ID
|
|
49
|
+
* @returns {Promise<Object|null>} Token object or null
|
|
50
|
+
* @abstract
|
|
51
|
+
*/
|
|
52
|
+
async findTokenById(tokenId) {
|
|
53
|
+
throw new Error('Method findTokenById must be implemented by subclass');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Find all tokens for a user
|
|
58
|
+
*
|
|
59
|
+
* @param {string|number} userId - User ID
|
|
60
|
+
* @returns {Promise<Array>} Array of token objects
|
|
61
|
+
* @abstract
|
|
62
|
+
*/
|
|
63
|
+
async findTokensByUserId(userId) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'Method findTokensByUserId must be implemented by subclass'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Delete token by ID
|
|
71
|
+
*
|
|
72
|
+
* @param {string|number} tokenId - Token ID
|
|
73
|
+
* @returns {Promise<boolean>} True if deleted
|
|
74
|
+
* @abstract
|
|
75
|
+
*/
|
|
76
|
+
async deleteToken(tokenId) {
|
|
77
|
+
throw new Error('Method deleteToken must be implemented by subclass');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Delete expired tokens
|
|
82
|
+
*
|
|
83
|
+
* @returns {Promise<number>} Number of deleted tokens
|
|
84
|
+
* @abstract
|
|
85
|
+
*/
|
|
86
|
+
async deleteExpiredTokens() {
|
|
87
|
+
throw new Error(
|
|
88
|
+
'Method deleteExpiredTokens must be implemented by subclass'
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Delete all tokens for a user
|
|
94
|
+
*
|
|
95
|
+
* @param {string|number} userId - User ID
|
|
96
|
+
* @returns {Promise<number>} Number of deleted tokens
|
|
97
|
+
* @abstract
|
|
98
|
+
*/
|
|
99
|
+
async deleteTokensByUserId(userId) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
'Method deleteTokensByUserId must be implemented by subclass'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create base64 buffer token
|
|
107
|
+
*
|
|
108
|
+
* @param {Object} token - Token object
|
|
109
|
+
* @param {string} rawToken - Raw token
|
|
110
|
+
* @returns {string} Base64 encoded token
|
|
111
|
+
*/
|
|
112
|
+
createBase64BufferToken(token, rawToken) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
'Method createBase64BufferToken must be implemented by subclass'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get JSON token from base64 buffer token
|
|
120
|
+
*
|
|
121
|
+
* @param {string} base64Token - Base64 encoded token
|
|
122
|
+
* @returns {Object} Decoded token object
|
|
123
|
+
*/
|
|
124
|
+
getJSONTokenFromBase64BufferToken(base64Token) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'Method getJSONTokenFromBase64BufferToken must be implemented by subclass'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { TokenRepositoryInterface };
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const bcrypt = require('bcryptjs');
|
|
3
|
+
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
+
const { ClientSafeError } = require('../../errors');
|
|
5
|
+
|
|
6
|
+
const BCRYPT_ROUNDS = 10;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* MongoDB Token Repository Adapter
|
|
10
|
+
* Handles persistence of authentication tokens with bcrypt hashing
|
|
11
|
+
*
|
|
12
|
+
* MongoDB-specific characteristics:
|
|
13
|
+
* - Uses String IDs (ObjectId)
|
|
14
|
+
* - No ID conversion needed (IDs are already strings)
|
|
15
|
+
* - Bcrypt hashing handled in repository layer
|
|
16
|
+
*/
|
|
17
|
+
class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
this.prisma = prisma;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a token with expiration
|
|
25
|
+
* Replaces: Token.createTokenWithExpire(userId, rawToken, minutes)
|
|
26
|
+
*
|
|
27
|
+
* @param {string} userId - The user ID
|
|
28
|
+
* @param {string} rawToken - The raw (unhashed) token string
|
|
29
|
+
* @param {number} minutes - Minutes until expiration
|
|
30
|
+
* @returns {Promise<Object>} The created token record with string IDs
|
|
31
|
+
*/
|
|
32
|
+
async createTokenWithExpire(userId, rawToken, minutes) {
|
|
33
|
+
// Hash the token with bcrypt
|
|
34
|
+
const tokenHash = await bcrypt.hash(rawToken, BCRYPT_ROUNDS);
|
|
35
|
+
|
|
36
|
+
// Calculate expiration time
|
|
37
|
+
const expires = new Date(Date.now() + minutes * 60000);
|
|
38
|
+
|
|
39
|
+
return await this.prisma.token.create({
|
|
40
|
+
data: {
|
|
41
|
+
token: tokenHash,
|
|
42
|
+
expires,
|
|
43
|
+
userId,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate and retrieve token from JSON token object
|
|
50
|
+
* Replaces: Token.validateAndGetTokenFromJSONToken(tokenObj)
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} tokenObj - Object with id and token properties
|
|
53
|
+
* @returns {Promise<Object>} The validated token record with string IDs
|
|
54
|
+
* @throws {Error} If token is invalid, expired, or doesn't exist
|
|
55
|
+
*/
|
|
56
|
+
async validateAndGetToken(tokenObj) {
|
|
57
|
+
const sessionToken = await this.prisma.token.findUnique({
|
|
58
|
+
where: { id: tokenObj.id },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!sessionToken) {
|
|
62
|
+
throw new ClientSafeError(
|
|
63
|
+
'Invalid Token: Token does not exist',
|
|
64
|
+
401
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Verify token hash matches
|
|
69
|
+
const isValid = await bcrypt.compare(
|
|
70
|
+
tokenObj.token,
|
|
71
|
+
sessionToken.token
|
|
72
|
+
);
|
|
73
|
+
if (!isValid) {
|
|
74
|
+
throw new ClientSafeError(
|
|
75
|
+
'Invalid Token: Token does not match',
|
|
76
|
+
401
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check if token is expired
|
|
81
|
+
if (
|
|
82
|
+
sessionToken.expires &&
|
|
83
|
+
new Date(sessionToken.expires) < new Date()
|
|
84
|
+
) {
|
|
85
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return sessionToken;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Find a token by ID
|
|
93
|
+
* Replaces: Token.findById(tokenId)
|
|
94
|
+
*
|
|
95
|
+
* @param {string} tokenId - The token ID
|
|
96
|
+
* @returns {Promise<Object|null>} The token record with string IDs or null
|
|
97
|
+
*/
|
|
98
|
+
async findTokenById(tokenId) {
|
|
99
|
+
return await this.prisma.token.findUnique({
|
|
100
|
+
where: { id: tokenId },
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Find tokens by user ID
|
|
106
|
+
* Replaces: Token.find({ user: userId })
|
|
107
|
+
*
|
|
108
|
+
* @param {string} userId - The user ID
|
|
109
|
+
* @returns {Promise<Array>} Array of token records with string IDs
|
|
110
|
+
*/
|
|
111
|
+
async findTokensByUserId(userId) {
|
|
112
|
+
return await this.prisma.token.findMany({
|
|
113
|
+
where: { userId },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Delete a token by ID
|
|
119
|
+
* Replaces: Token.deleteOne({ _id: tokenId })
|
|
120
|
+
*
|
|
121
|
+
* @param {string} tokenId - The token ID
|
|
122
|
+
* @returns {Promise<Object>} The deletion result
|
|
123
|
+
*/
|
|
124
|
+
async deleteToken(tokenId) {
|
|
125
|
+
try {
|
|
126
|
+
await this.prisma.token.delete({
|
|
127
|
+
where: { id: tokenId },
|
|
128
|
+
});
|
|
129
|
+
return { acknowledged: true, deletedCount: 1 };
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (error.code === 'P2025') {
|
|
132
|
+
// Record not found
|
|
133
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Delete expired tokens
|
|
141
|
+
* Replaces: Token.deleteMany({ expires: { $lt: new Date() } })
|
|
142
|
+
*
|
|
143
|
+
* @returns {Promise<Object>} The deletion result with count
|
|
144
|
+
*/
|
|
145
|
+
async deleteExpiredTokens() {
|
|
146
|
+
const result = await this.prisma.token.deleteMany({
|
|
147
|
+
where: {
|
|
148
|
+
expires: {
|
|
149
|
+
lt: new Date(),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
acknowledged: true,
|
|
156
|
+
deletedCount: result.count,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Delete all tokens for a user
|
|
162
|
+
* Replaces: Token.deleteMany({ user: userId })
|
|
163
|
+
*
|
|
164
|
+
* @param {string} userId - The user ID
|
|
165
|
+
* @returns {Promise<Object>} The deletion result
|
|
166
|
+
*/
|
|
167
|
+
async deleteTokensByUserId(userId) {
|
|
168
|
+
const result = await this.prisma.token.deleteMany({
|
|
169
|
+
where: { userId },
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
acknowledged: true,
|
|
174
|
+
deletedCount: result.count,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Create JSON token string from token object and raw token
|
|
180
|
+
* Replaces: Token.createJSONToken(token, rawToken)
|
|
181
|
+
*
|
|
182
|
+
* @param {Object} token - The token record
|
|
183
|
+
* @param {string} rawToken - The raw token string
|
|
184
|
+
* @returns {string} JSON string with id and token
|
|
185
|
+
*/
|
|
186
|
+
createJSONToken(token, rawToken) {
|
|
187
|
+
return JSON.stringify({
|
|
188
|
+
id: token.id,
|
|
189
|
+
token: rawToken,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create base64 encoded buffer token
|
|
195
|
+
* Replaces: Token.createBase64BufferToken(token, rawToken)
|
|
196
|
+
*
|
|
197
|
+
* @param {Object} token - The token record
|
|
198
|
+
* @param {string} rawToken - The raw token string
|
|
199
|
+
* @returns {string} Base64 encoded token
|
|
200
|
+
*/
|
|
201
|
+
createBase64BufferToken(token, rawToken) {
|
|
202
|
+
const jsonVal = this.createJSONToken(token, rawToken);
|
|
203
|
+
return Buffer.from(jsonVal).toString('base64');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Parse JSON token from base64 buffer
|
|
208
|
+
* Replaces: Token.getJSONTokenFromBase64BufferToken(buffer)
|
|
209
|
+
*
|
|
210
|
+
* @param {string} buffer - Base64 encoded token string
|
|
211
|
+
* @returns {Object} Parsed token object with id and token
|
|
212
|
+
*/
|
|
213
|
+
getJSONTokenFromBase64BufferToken(buffer) {
|
|
214
|
+
const tokenStr = Buffer.from(buffer.trim(), 'base64').toString('ascii');
|
|
215
|
+
return JSON.parse(tokenStr);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = { TokenRepositoryMongo };
|