@memberjunction/server 6.2.0-edge.2 → 6.2.0-edge.4
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/README.md +30 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
- package/dist/agentSessions/BridgeHostInstance.js +30 -0
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
- package/dist/agentSessions/HostInstance.d.ts +75 -11
- package/dist/agentSessions/HostInstance.d.ts.map +1 -1
- package/dist/agentSessions/HostInstance.js +99 -28
- package/dist/agentSessions/HostInstance.js.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +173 -25
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +452 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +43 -2
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +99 -8
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
- package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
- package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +2 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +10 -3
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/auth/actionAuthorization.d.ts +40 -0
- package/dist/auth/actionAuthorization.d.ts.map +1 -0
- package/dist/auth/actionAuthorization.js +94 -0
- package/dist/auth/actionAuthorization.js.map +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +6 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.d.ts +10 -0
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +80 -11
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +4567 -71
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +246 -3
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +25 -34
- package/dist/context.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1073 -10
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +9188 -3649
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +62 -6
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +179 -35
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +37 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +214 -159
- package/dist/index.js.map +1 -1
- package/dist/isolatedProvider.d.ts +20 -0
- package/dist/isolatedProvider.d.ts.map +1 -0
- package/dist/isolatedProvider.js +22 -0
- package/dist/isolatedProvider.js.map +1 -0
- package/dist/postgresqlCompat.d.ts +8 -0
- package/dist/postgresqlCompat.d.ts.map +1 -0
- package/dist/postgresqlCompat.js +19 -0
- package/dist/postgresqlCompat.js.map +1 -0
- package/dist/postgresqlPoolSettings.d.ts +108 -0
- package/dist/postgresqlPoolSettings.d.ts.map +1 -0
- package/dist/postgresqlPoolSettings.js +134 -0
- package/dist/postgresqlPoolSettings.js.map +1 -0
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/realtimeSessions/index.d.ts +17 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +17 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +90 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
- package/dist/realtimeSessions/verificationCore.d.ts +243 -0
- package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationCore.js +356 -0
- package/dist/realtimeSessions/verificationCore.js.map +1 -0
- package/dist/realtimeSessions/verificationPages.d.ts +66 -0
- package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPages.js +110 -0
- package/dist/realtimeSessions/verificationPages.js.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.js +249 -0
- package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.js +435 -0
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +31 -5
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +75 -14
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +35 -17
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +92 -166
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
- package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
- package/dist/resolvers/CacheStatsResolver.js +113 -2
- package/dist/resolvers/CacheStatsResolver.js.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
- package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.js +28 -10
- package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +9 -47
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +9 -0
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
- package/dist/resolvers/HumanHandoffResolver.js +330 -0
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +9 -4
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/MeetingResolver.d.ts +69 -0
- package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
- package/dist/resolvers/MeetingResolver.js +381 -0
- package/dist/resolvers/MeetingResolver.js.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +88 -3
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +352 -12
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +156 -108
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +395 -293
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +68 -11
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +12 -11
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +6 -2
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
- package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
- package/dist/resolvers/UserAvatarResolver.js +124 -0
- package/dist/resolvers/UserAvatarResolver.js.map +1 -0
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +2 -17
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/adhocTimeout.d.ts +7 -0
- package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
- package/dist/resolvers/adhocTimeout.js +12 -0
- package/dist/resolvers/adhocTimeout.js.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.js +63 -0
- package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
- package/dist/resolvers/avatarInputValidation.d.ts +40 -0
- package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
- package/dist/resolvers/avatarInputValidation.js +97 -0
- package/dist/resolvers/avatarInputValidation.js.map +1 -0
- package/dist/resolvers/currentUserRoles.d.ts +29 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
- package/dist/resolvers/currentUserRoles.js +30 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -1
- package/dist/resolvers/meetings/index.d.ts +5 -0
- package/dist/resolvers/meetings/index.d.ts.map +1 -0
- package/dist/resolvers/meetings/index.js +5 -0
- package/dist/resolvers/meetings/index.js.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.js +212 -0
- package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts +18 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.js +439 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.js +113 -0
- package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.js +345 -0
- package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +8 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +8 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -1
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +36 -0
- package/dist/services/WorkQueueHostService.d.ts.map +1 -0
- package/dist/services/WorkQueueHostService.js +97 -0
- package/dist/services/WorkQueueHostService.js.map +1 -0
- package/dist/services/workQueueConfig.d.ts +103 -0
- package/dist/services/workQueueConfig.d.ts.map +1 -0
- package/dist/services/workQueueConfig.js +48 -0
- package/dist/services/workQueueConfig.js.map +1 -0
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +276 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +97 -94
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
- package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
- package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/BridgeHostInstance.test.ts +232 -0
- package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
- package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +250 -0
- package/src/__tests__/MeetingResolver.test.ts +448 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +651 -4
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
- package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
- package/src/__tests__/RealtimeRelay.test.ts +876 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
- package/src/__tests__/SessionJanitor.test.ts +845 -18
- package/src/__tests__/SessionManager.test.ts +213 -17
- package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
- package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +41 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/WorkQueueHostService.test.ts +133 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
- package/src/__tests__/magicLinkService.consume.test.ts +6 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
- package/src/__tests__/newUsers.template.test.ts +151 -0
- package/src/__tests__/newUsers.test.ts +74 -5
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
- package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
- package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
- package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
- package/src/__tests__/realtimeSessions/helpers.ts +4 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
- package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
- package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
- package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
- package/src/__tests__/resolverBase.fls.test.ts +87 -4
- package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
- package/src/__tests__/sharedCache.test.ts +310 -0
- package/src/__tests__/translateBracketsToPG.test.ts +20 -0
- package/src/__tests__/widgetGuestElevation.test.ts +44 -0
- package/src/agentSessions/BridgeHostInstance.ts +32 -0
- package/src/agentSessions/HostInstance.ts +124 -28
- package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
- package/src/agentSessions/SessionJanitor.ts +543 -57
- package/src/agentSessions/SessionManager.ts +125 -25
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +3 -8
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +255 -3
- package/src/context.ts +26 -34
- package/src/generated/graphql-schemas/__mj.ts +22701 -18838
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/ResolverBase.ts +210 -36
- package/src/generic/RunViewResolver.ts +37 -2
- package/src/index.ts +236 -170
- package/src/isolatedProvider.ts +22 -0
- package/src/postgresqlCompat.ts +21 -0
- package/src/postgresqlPoolSettings.ts +213 -0
- package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
- package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
- package/src/realtimeSessions/README.md +179 -0
- package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +16 -0
- package/src/realtimeSessions/sessionAccess.ts +141 -0
- package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
- package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
- package/src/realtimeSessions/verificationCore.ts +502 -0
- package/src/realtimeSessions/verificationPages.ts +149 -0
- package/src/realtimeSessions/verificationPolicy.ts +321 -0
- package/src/realtimeSessions/verificationWorkflow.ts +702 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
- package/src/resolvers/ActionResolver.ts +84 -21
- package/src/resolvers/AdhocQueryResolver.ts +105 -187
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/EntityRecordNameResolver.ts +9 -51
- package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
- package/src/resolvers/GetDataResolver.ts +10 -0
- package/src/resolvers/HumanHandoffResolver.ts +312 -0
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/MeetingResolver.ts +438 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +385 -19
- package/src/resolvers/RealtimeClientSessionResolver.ts +503 -344
- package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
- package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
- package/src/resolvers/RunAIAgentResolver.ts +78 -11
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/adhocTimeout.ts +12 -0
- package/src/resolvers/agentVisionConsentAudit.ts +82 -0
- package/src/resolvers/avatarInputValidation.ts +123 -0
- package/src/resolvers/currentUserRoles.ts +32 -1
- package/src/resolvers/meetings/index.ts +4 -0
- package/src/resolvers/meetings/meetingHelpers.ts +307 -0
- package/src/resolvers/meetings/meetingMutations.ts +556 -0
- package/src/resolvers/meetings/meetingQueries.ts +172 -0
- package/src/resolvers/meetings/meetingTypes.ts +238 -0
- package/src/rest/ViewOperationsHandler.ts +8 -5
- package/src/services/TaskGraphActionRunner.ts +29 -1
- package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
- package/src/services/WorkQueueHostService.ts +129 -0
- package/src/services/workQueueConfig.ts +52 -0
- package/src/sharedCache.ts +302 -0
- package/src/types.ts +12 -0
- package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
- package/dist/resolvers/adhoc-query-helpers.js +0 -50
- package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
- package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
- package/src/resolvers/adhoc-query-helpers.ts +0 -67
package/src/auth/newUsers.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ApplicationInfo,
|
|
1
|
+
import { ApplicationInfo, CompositeKey, EntitySaveOptions, IMetadataProvider, LogError, LogStatus, RunView, RunViewResult, UserInfo } from "@memberjunction/core";
|
|
2
|
+
import { RecordCloneEngine } from "@memberjunction/record-cloning";
|
|
2
3
|
import { RegisterClass } from "@memberjunction/global";
|
|
3
4
|
import { ResolveConfiguredPrincipal } from "./principals.js";
|
|
4
5
|
import { configInfo } from "../config.js";
|
|
6
|
+
import { CreateIsolatedProvider } from "../isolatedProvider.js";
|
|
5
7
|
import { MJUserEntity, MJUserRoleEntity, MJUserApplicationEntity, MJUserApplicationEntityEntity, MJApplicationEntityType, MJApplicationEntityEntityType, UserInfoEngine } from "@memberjunction/core-entities";
|
|
6
8
|
|
|
7
9
|
export class NewUserBase {
|
|
@@ -16,8 +18,21 @@ export class NewUserBase {
|
|
|
16
18
|
return null;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// Everything here, metadata lookups, entity objects and the transaction, goes through ONE
|
|
22
|
+
// isolated provider. This runs in the JWT auth flow, before AppContext.providers exists, and
|
|
23
|
+
// the global Metadata.Provider is shared by every concurrent request: a transaction opened
|
|
24
|
+
// there nests into anyone else's and swallows their writes (see CreateIsolatedProvider).
|
|
25
|
+
const provider = await CreateIsolatedProvider();
|
|
26
|
+
|
|
27
|
+
// A configured template user is copied whole; a plain user is created only if it can't be.
|
|
28
|
+
const template = configInfo?.userHandling?.newUserTemplate;
|
|
29
|
+
if (template?.email) {
|
|
30
|
+
const fromTemplate = await this.createFromTemplate(provider, contextUser, template.email, template.preset, firstName, lastName, email, linkedRecordType, linkedEntityId, linkedEntityRecordId);
|
|
31
|
+
if (fromTemplate) return fromTemplate;
|
|
32
|
+
LogError(`Could not create ${email} from template user ${template.email}; creating a plain user instead`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const user = await provider.GetEntityObject<MJUserEntity>('MJ: Users', contextUser) // To-Do - change this to be a different defined user for the user creation process
|
|
21
36
|
user.NewRecord();
|
|
22
37
|
user.Name = email;
|
|
23
38
|
user.IsActive = true;
|
|
@@ -38,7 +53,6 @@ export class NewUserBase {
|
|
|
38
53
|
// Create the user and all of its role/application/app-entity records atomically.
|
|
39
54
|
// If any Save fails partway through, the whole provisioning rolls back so we never
|
|
40
55
|
// leave a half-created user with partial roles/applications behind.
|
|
41
|
-
const provider = Metadata.Provider as DatabaseProviderBase; // global-provider-ok: new-user creation runs in the JWT auth flow, before AppContext.providers is built
|
|
42
56
|
await provider.BeginTransaction();
|
|
43
57
|
try {
|
|
44
58
|
if (!await user.Save()) {
|
|
@@ -48,13 +62,13 @@ export class NewUserBase {
|
|
|
48
62
|
if(configInfo.userHandling && configInfo.userHandling.newUserRoles){
|
|
49
63
|
LogStatus(`User ${user.Email} created, assigning roles`);
|
|
50
64
|
for (const role of configInfo.userHandling.newUserRoles) {
|
|
51
|
-
const userRole =
|
|
65
|
+
const userRole = provider.Roles.find(r => r.Name === role);
|
|
52
66
|
if (!userRole) {
|
|
53
67
|
LogError(`Role ${role} not found in the database, cannot assign to new user ${user.Name}`);
|
|
54
68
|
continue;
|
|
55
69
|
}
|
|
56
70
|
|
|
57
|
-
const userRoleEntity: MJUserRoleEntity = await
|
|
71
|
+
const userRoleEntity: MJUserRoleEntity = await provider.GetEntityObject<MJUserRoleEntity>('MJ: User Roles', contextUser);
|
|
58
72
|
userRoleEntity.NewRecord();
|
|
59
73
|
userRoleEntity.UserID = user.ID;
|
|
60
74
|
userRoleEntity.RoleID = userRole.ID;
|
|
@@ -73,7 +87,7 @@ export class NewUserBase {
|
|
|
73
87
|
if (configInfo.userHandling.UserApplications && configInfo.userHandling.UserApplications.length > 0) {
|
|
74
88
|
for (const appName of configInfo.userHandling.UserApplications) {
|
|
75
89
|
const toLowerCase: string = appName.trim().toLocaleLowerCase();
|
|
76
|
-
const application: ApplicationInfo | undefined =
|
|
90
|
+
const application: ApplicationInfo | undefined = provider.Applications.find(a => a.Name.trim().toLocaleLowerCase() === toLowerCase);
|
|
77
91
|
if (application) {
|
|
78
92
|
applicationsToCreate.push(application);
|
|
79
93
|
} else {
|
|
@@ -85,12 +99,12 @@ export class NewUserBase {
|
|
|
85
99
|
// Bug F2: use the shared selector, which filters to Active apps flagged
|
|
86
100
|
// DefaultForNewUser (in DefaultSequence order). This path previously omitted the
|
|
87
101
|
// Status = 'Active' check, so an inactive default app could be provisioned here.
|
|
88
|
-
applicationsToCreate = UserInfoEngine.GetDefaultApplicationsForNewUser(
|
|
102
|
+
applicationsToCreate = UserInfoEngine.GetDefaultApplicationsForNewUser(provider);
|
|
89
103
|
LogStatus(`Found ${applicationsToCreate.length} active applications with DefaultForNewUser=true`);
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
for (const [appIndex, application] of applicationsToCreate.entries()) {
|
|
93
|
-
const userApplication: MJUserApplicationEntity = await
|
|
107
|
+
const userApplication: MJUserApplicationEntity = await provider.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications', contextUser);
|
|
94
108
|
userApplication.NewRecord();
|
|
95
109
|
userApplication.UserID = user.ID;
|
|
96
110
|
userApplication.ApplicationID = application.ID;
|
|
@@ -102,7 +116,7 @@ export class NewUserBase {
|
|
|
102
116
|
}
|
|
103
117
|
LogStatus(`Created User Application ${application.Name} for new user ${user.Name}`);
|
|
104
118
|
|
|
105
|
-
const rv: RunView =
|
|
119
|
+
const rv: RunView = RunView.FromMetadataProvider(provider);
|
|
106
120
|
// Boolean literal goes through the active provider's dialect:
|
|
107
121
|
// SQL Server emits `= 1`, PostgreSQL emits `= TRUE`.
|
|
108
122
|
// This keeps the filter server-side (no client-side `.filter()`
|
|
@@ -125,7 +139,7 @@ export class NewUserBase {
|
|
|
125
139
|
LogStatus(`Creating ${defaultForNewUserEntities.length} User Application Entities for User Application ${application.Name} for new user ${user.Name}`);
|
|
126
140
|
|
|
127
141
|
for(const [index, appEntity] of defaultForNewUserEntities.entries()){
|
|
128
|
-
const userAppEntity: MJUserApplicationEntityEntity = await
|
|
142
|
+
const userAppEntity: MJUserApplicationEntityEntity = await provider.GetEntityObject<MJUserApplicationEntityEntity>('MJ: User Application Entities', contextUser);
|
|
129
143
|
userAppEntity.NewRecord();
|
|
130
144
|
userAppEntity.UserApplicationID = userApplication.ID;
|
|
131
145
|
userAppEntity.EntityID = appEntity.EntityID;
|
|
@@ -153,4 +167,78 @@ export class NewUserBase {
|
|
|
153
167
|
return undefined;
|
|
154
168
|
}
|
|
155
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Creates the new user as a copy of the template user `templateEmail`, through the `MJ: Users`
|
|
173
|
+
* clone `preset`: its roles, apps, views, settings, dashboards and conversations come with it, owned
|
|
174
|
+
* by the new user. The clone engine creates users inactive for review; a user signing in is active.
|
|
175
|
+
*
|
|
176
|
+
* @returns the new user, or null when the template is missing, isn't flagged IsTemplate, or the copy
|
|
177
|
+
* fails (the reason is logged and the caller falls back to a plain user).
|
|
178
|
+
*/
|
|
179
|
+
protected async createFromTemplate(
|
|
180
|
+
provider: IMetadataProvider,
|
|
181
|
+
contextUser: UserInfo,
|
|
182
|
+
templateEmail: string,
|
|
183
|
+
preset: string,
|
|
184
|
+
firstName: string,
|
|
185
|
+
lastName: string,
|
|
186
|
+
email: string,
|
|
187
|
+
linkedRecordType: string,
|
|
188
|
+
linkedEntityId?: string,
|
|
189
|
+
linkedEntityRecordId?: string
|
|
190
|
+
): Promise<MJUserEntity | null> {
|
|
191
|
+
const lookup = await RunView.FromMetadataProvider(provider).RunView<{ ID: string; IsTemplate: boolean }>(
|
|
192
|
+
{
|
|
193
|
+
EntityName: 'MJ: Users',
|
|
194
|
+
ExtraFilter: `Email = '${templateEmail.replace(/'/g, "''")}'`,
|
|
195
|
+
Fields: ['ID', 'IsTemplate'],
|
|
196
|
+
ResultType: 'simple',
|
|
197
|
+
MaxRows: 1,
|
|
198
|
+
},
|
|
199
|
+
contextUser
|
|
200
|
+
);
|
|
201
|
+
const templateUser = lookup.Success ? lookup.Results[0] : undefined;
|
|
202
|
+
if (!templateUser) {
|
|
203
|
+
LogError(`userHandling.newUserTemplate: no user has the email ${templateEmail}`);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
if (!templateUser.IsTemplate) {
|
|
207
|
+
LogError(`userHandling.newUserTemplate: ${templateEmail} is not marked as a template user (IsTemplate)`);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const result = await new RecordCloneEngine(provider).Clone(
|
|
212
|
+
{
|
|
213
|
+
EntityName: 'MJ: Users',
|
|
214
|
+
SourceRecordKey: CompositeKey.FromID(templateUser.ID),
|
|
215
|
+
Preset: preset,
|
|
216
|
+
PromptedValues: { Email: email, FirstName: firstName, LastName: lastName },
|
|
217
|
+
Options: { Reason: `New user ${email} created at first sign-in from template ${templateEmail}` },
|
|
218
|
+
},
|
|
219
|
+
contextUser
|
|
220
|
+
);
|
|
221
|
+
const newKey = result.Roots?.[0]?.TargetKey.KeyValuePairs.find((kvp) => kvp.FieldName === 'ID')?.Value;
|
|
222
|
+
if (!result.Success || !newKey) {
|
|
223
|
+
const reasons = result.Warnings.filter((w) => w.Severity === 'Error').map((w) => w.Message);
|
|
224
|
+
LogError(`userHandling.newUserTemplate: copying ${templateEmail} failed: ${result.ErrorMessage || reasons.join('; ') || result.ResultCode}`);
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const user = await provider.GetEntityObject<MJUserEntity>('MJ: Users', contextUser);
|
|
229
|
+
if (!(await user.Load(String(newKey)))) {
|
|
230
|
+
LogError(`userHandling.newUserTemplate: the copy of ${templateEmail} for ${email} could not be loaded`);
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
user.IsActive = true;
|
|
234
|
+
user.LinkedRecordType = linkedRecordType;
|
|
235
|
+
if (linkedEntityId) user.LinkedEntityID = linkedEntityId;
|
|
236
|
+
if (linkedEntityRecordId) user.LinkedEntityRecordID = linkedEntityRecordId;
|
|
237
|
+
if (!(await user.Save())) {
|
|
238
|
+
LogError(`userHandling.newUserTemplate: could not activate ${email}: ${user.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
LogStatus(`Created user ${email} from template ${templateEmail} (${result.RecordsCloned ?? result.Created?.length ?? 0} records copied)`);
|
|
242
|
+
return user;
|
|
243
|
+
}
|
|
156
244
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { DEFAULT_WORK_QUEUE_CONFIG, WorkQueueSchema } from './services/workQueueConfig.js';
|
|
2
3
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
4
|
import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
|
|
4
5
|
import { mergeConfigs, parseBooleanEnv } from '@memberjunction/config';
|
|
@@ -37,6 +38,19 @@ const userHandlingInfoSchema = z.object({
|
|
|
37
38
|
contextUserForNewUserCreation: z.string().optional().default(''),
|
|
38
39
|
CreateUserApplicationRecords: z.boolean().optional().default(false),
|
|
39
40
|
UserApplications: z.array(z.string()).optional().default([]),
|
|
41
|
+
/**
|
|
42
|
+
* Build each auto-created user as a copy of a template user (`User.IsTemplate`), so new people start
|
|
43
|
+
* with its pinned apps, views, settings, dashboards and conversations. `email` names the template;
|
|
44
|
+
* `preset` is the `MJ: Users` clone preset to apply (default `template`). The copy takes the roles
|
|
45
|
+
* and apps of the template, so `newUserRoles`, `CreateUserApplicationRecords` and `UserApplications`
|
|
46
|
+
* apply only when the template can't be used, in which case a plain user is created as before.
|
|
47
|
+
*/
|
|
48
|
+
newUserTemplate: z
|
|
49
|
+
.object({
|
|
50
|
+
email: z.string(),
|
|
51
|
+
preset: z.string().optional().default('template'),
|
|
52
|
+
})
|
|
53
|
+
.optional(),
|
|
40
54
|
});
|
|
41
55
|
|
|
42
56
|
const databaseSettingsInfoSchema = z.object({
|
|
@@ -253,6 +267,38 @@ const cacheSettingsSchema = z.object({
|
|
|
253
267
|
* every signed-in user may read every row of every entity.
|
|
254
268
|
*/
|
|
255
269
|
recordDataBroadcastEntities: z.array(z.string()).optional().default([]),
|
|
270
|
+
/**
|
|
271
|
+
* Expiry, in seconds, of entries in the shared Redis cache (used when REDIS_URL is set). The
|
|
272
|
+
* REDIS_TTL_SECONDS environment variable overrides it. 0 stores entries without expiry. When
|
|
273
|
+
* neither is set, the Redis provider's default applies (one hour).
|
|
274
|
+
*/
|
|
275
|
+
sharedCacheTTLSeconds: z.number().int().nonnegative().optional(),
|
|
276
|
+
/**
|
|
277
|
+
* How often, in seconds, loaded engines compare their rows with the database and reload what
|
|
278
|
+
* changed without an MJ event (direct SQL, other applications). With a shared cache one server
|
|
279
|
+
* does it per interval. 0 = disabled. Default: 300 (5 minutes).
|
|
280
|
+
*/
|
|
281
|
+
engineSweepIntervalSeconds: z.number().int().nonnegative().optional().default(300),
|
|
282
|
+
/**
|
|
283
|
+
* How often, in seconds, the user cache compares two row counts and the newest update timestamp
|
|
284
|
+
* with the database and reloads only when they differ. The cache already refreshes on MJ writes
|
|
285
|
+
* (locally and, with a shared cache, on other servers); this covers changes made outside MJ.
|
|
286
|
+
* 0 = disabled. Default: 300 (5 minutes).
|
|
287
|
+
*/
|
|
288
|
+
userCacheCheckIntervalSeconds: z.number().int().nonnegative().optional().default(300),
|
|
289
|
+
/**
|
|
290
|
+
* How often to compare this process's metadata with the database, in seconds (0 disables).
|
|
291
|
+
* Costs nothing unless an entity the metadata is built from declares
|
|
292
|
+
* `TrustServerCacheCompletely = false`; see `ProviderBase.SweepMetadataAgainstDatabase`.
|
|
293
|
+
*/
|
|
294
|
+
metadataSweepIntervalSeconds: z.number().int().nonnegative().optional().default(300),
|
|
295
|
+
/**
|
|
296
|
+
* How long, in seconds, a server holds the shared warm-up turn while it loads its engines, so
|
|
297
|
+
* servers starting together load one at a time and the rest find the cache warm. The lease is
|
|
298
|
+
* renewed while the load runs; this value is both its expiry and how long another server waits
|
|
299
|
+
* for it. 0 disables the turn-taking. Default: 30.
|
|
300
|
+
*/
|
|
301
|
+
startupWarmupLeaseSeconds: z.number().int().nonnegative().optional().default(30),
|
|
256
302
|
});
|
|
257
303
|
|
|
258
304
|
const loggingSettingsSchema = z.object({
|
|
@@ -470,10 +516,20 @@ const twilioTelephonySchema = z.object({
|
|
|
470
516
|
apiKeySecret: z.string().optional(),
|
|
471
517
|
/** The publicly reachable `wss://…/telephony/twilio/media` URL Twilio's <Connect><Stream> connects to. */
|
|
472
518
|
streamPublicUrl: z.string(),
|
|
473
|
-
/**
|
|
519
|
+
/**
|
|
520
|
+
* Reserved; currently unused. The Media-Streams websocket is authenticated by a per-call token MJ mints and
|
|
521
|
+
* embeds in the TwiML (`<Parameter name="mjToken">`), and webhooks by `X-Twilio-Signature`.
|
|
522
|
+
*/
|
|
474
523
|
webhookSigningSecret: z.string().optional(),
|
|
475
|
-
/**
|
|
524
|
+
/**
|
|
525
|
+
* URL Twilio posts outbound-call lifecycle events to. Defaults to `<public URL>/telephony/twilio/status`, the
|
|
526
|
+
* route that ends the bridge session when a call is busy / unanswered / failed / completed.
|
|
527
|
+
*/
|
|
476
528
|
statusCallbackUrl: z.string().optional(),
|
|
529
|
+
/** URL Twilio posts the async answering-machine verdict to. Defaults to `<public URL>/telephony/twilio/amd`. */
|
|
530
|
+
amdStatusCallbackUrl: z.string().optional(),
|
|
531
|
+
/** What to do when a machine or fax answers an outbound call: `hangup` (default) ends it, `continue` only logs the verdict. */
|
|
532
|
+
onMachine: z.enum(['hangup', 'continue']).optional(),
|
|
477
533
|
}).passthrough();
|
|
478
534
|
|
|
479
535
|
/**
|
|
@@ -495,8 +551,14 @@ const vonageTelephonySchema = z.object({
|
|
|
495
551
|
mediaPublicUrl: z.string(),
|
|
496
552
|
/** Vonage account signature secret — HMAC key for signed-request `sig` AND HS256 webhook-JWT verification. */
|
|
497
553
|
signatureSecret: z.string().optional(),
|
|
498
|
-
/**
|
|
554
|
+
/**
|
|
555
|
+
* Event-webhook URL Vonage posts call lifecycle events to (passed on outbound createCall). Defaults to
|
|
556
|
+
* `<public URL>/telephony/vonage/event`, the route that ends the bridge session when a call is busy /
|
|
557
|
+
* unanswered / failed / completed.
|
|
558
|
+
*/
|
|
499
559
|
eventUrl: z.string().optional(),
|
|
560
|
+
/** What to do when a machine answers an outbound call — Vonage's `machine_detection`: `hangup` (default) or `continue`. */
|
|
561
|
+
onMachine: z.enum(['hangup', 'continue']).optional(),
|
|
500
562
|
}).passthrough();
|
|
501
563
|
|
|
502
564
|
/**
|
|
@@ -550,22 +612,204 @@ const teamsMeetingsSchema = z.object({
|
|
|
550
612
|
modelSampleRate: z.coerce.number().optional().default(16000),
|
|
551
613
|
}).passthrough();
|
|
552
614
|
|
|
615
|
+
/**
|
|
616
|
+
* LiveKit SIP binding: phone calls carried by a SIP trunk and landed in a LiveKit room (every conversation lives in a room;
|
|
617
|
+
* the carrier is only the pipe). LiveKit credentials default to the `LIVEKIT_URL` / `LIVEKIT_API_KEY` /
|
|
618
|
+
* `LIVEKIT_API_SECRET` env vars the Meet room already uses. When `livekitSip` is omitted, the LiveKit SIP webhook is not mounted.
|
|
619
|
+
* Point a LiveKit project webhook at `<public URL>/telephony/livekit-sip/webhook` (the signature is verified with the API secret).
|
|
620
|
+
*/
|
|
621
|
+
const livekitSipTelephonySchema = z.object({
|
|
622
|
+
/** LiveKit server URL (`wss://…`). Defaults to `LIVEKIT_URL`. */
|
|
623
|
+
serverUrl: z.string().optional(),
|
|
624
|
+
/** LiveKit API key. Defaults to `LIVEKIT_API_KEY`. */
|
|
625
|
+
apiKey: z.string().optional(),
|
|
626
|
+
/** LiveKit API secret (resolved upstream — never inlined). Defaults to `LIVEKIT_API_SECRET`. */
|
|
627
|
+
apiSecret: z.string().optional(),
|
|
628
|
+
/** Inbound calls land in a room whose name starts with this. Defaults to `call-`. */
|
|
629
|
+
roomPrefix: z.string().optional(),
|
|
630
|
+
/** The numbers (E.164) this deployment answers. Each routes to the agent identity registered for it (on the LiveKit provider). */
|
|
631
|
+
numbers: z.array(z.string()).optional(),
|
|
632
|
+
/** The LiveKit inbound trunk id, when created by hand. */
|
|
633
|
+
inboundTrunkId: z.string().optional(),
|
|
634
|
+
/** The LiveKit OUTBOUND trunk used to dial out (outbound calls, fallback legs, transfers to a number). Dialing out is off without it. */
|
|
635
|
+
outboundTrunkId: z.string().optional(),
|
|
636
|
+
/** The caller ID presented on calls dialed out through the outbound trunk. */
|
|
637
|
+
outboundFromNumber: z.string().optional(),
|
|
638
|
+
/** Create the inbound trunk and dispatch rule at startup when missing (idempotent). Defaults to false. */
|
|
639
|
+
autoProvision: zodBooleanWithTransforms().optional().default(false),
|
|
640
|
+
/** Source addresses the inbound trunk accepts calls from (the carrier's SIP signalling addresses). Used when provisioning. */
|
|
641
|
+
allowedAddresses: z.array(z.string()).optional(),
|
|
642
|
+
/** The carrier behind the trunk, for configuration checks only (no carrier API is called). */
|
|
643
|
+
carrier: z
|
|
644
|
+
.object({
|
|
645
|
+
type: z.literal('twilio-elastic-sip'),
|
|
646
|
+
/** The LiveKit SIP URI the Twilio trunk's origination URI points at, e.g. `sip:<project>.sip.livekit.cloud`. */
|
|
647
|
+
originationUri: z.string().optional(),
|
|
648
|
+
/** The trunk's Termination SIP URI on Twilio (`<name>.pstn.twilio.com`). Needed only when dialing out. */
|
|
649
|
+
terminationUri: z.string().optional(),
|
|
650
|
+
/** How Twilio authenticates LiveKit's outbound requests. */
|
|
651
|
+
terminationAuth: z.enum(['credential-list', 'ip-acl']).optional(),
|
|
652
|
+
/** The Twilio numbers associated with the trunk (E.164). */
|
|
653
|
+
numbers: z.array(z.string()).optional(),
|
|
654
|
+
})
|
|
655
|
+
.optional(),
|
|
656
|
+
}).passthrough();
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Outbound-call policy applied to every `PlaceTwilioCall` / `PlaceVonageCall` / `PlaceRingCentralCall`
|
|
660
|
+
* mutation, on top of the caller's right to run the agent. Defaults are deliberately conservative.
|
|
661
|
+
* The rate limiter is in-memory and therefore PER PROCESS: with N MJAPI instances a user can place up to
|
|
662
|
+
* N × `maxCallsPerUserPerHour` calls.
|
|
663
|
+
*/
|
|
664
|
+
const outboundTelephonySchema = z.object({
|
|
665
|
+
/** Destination prefixes a call may go to (E.164, e.g. `+1`). An empty list refuses every destination. Defaults to `['+1']`. */
|
|
666
|
+
allowedPrefixes: z.array(z.string()).optional().default(['+1']),
|
|
667
|
+
/**
|
|
668
|
+
* Destination prefixes that are always refused, even when an allowed prefix matches. Deliberately has NO default
|
|
669
|
+
* here: when omitted, `@memberjunction/telephony-adapters` applies its own `DEFAULT_BLOCKED_PREFIXES` (NANP
|
|
670
|
+
* premium-rate plus the Caribbean +1 countries abused for toll fraud), so the list lives in exactly one place.
|
|
671
|
+
* Setting this REPLACES that list.
|
|
672
|
+
*/
|
|
673
|
+
blockedPrefixes: z.array(z.string()).optional(),
|
|
674
|
+
/** Max outbound calls one user may place per rolling hour (per process). Defaults to 20. */
|
|
675
|
+
maxCallsPerUserPerHour: z.coerce.number().int().positive().optional().default(20),
|
|
676
|
+
}).passthrough();
|
|
677
|
+
|
|
553
678
|
const telephonySchema = z.object({
|
|
554
679
|
/** Master switch. When false (or when no vendor block is present), telephony routes are not mounted. */
|
|
555
680
|
enabled: zodBooleanWithTransforms().default(false),
|
|
681
|
+
/**
|
|
682
|
+
* Email of the user INBOUND calls run as. A caller is an anonymous member of the public, so the call needs a
|
|
683
|
+
* principal to create its agent session and run the agent. Point this at a DEDICATED LEAST-PRIVILEGE user.
|
|
684
|
+
* There is deliberately no fallback: if unset, unknown, inactive, or the system user, inbound calls are
|
|
685
|
+
* rejected with a polite message and the rejection is logged. Applies to Twilio, Vonage and RingCentral.
|
|
686
|
+
*/
|
|
687
|
+
inboundRunAsUserEmail: z.string().optional(),
|
|
688
|
+
/** Maximum length of one phone call, in seconds. The bridge session is stopped (and the call hung up) at the cap. Defaults to 1800. */
|
|
689
|
+
maxCallSeconds: z.coerce.number().int().positive().optional().default(1800),
|
|
690
|
+
/**
|
|
691
|
+
* Most phone calls (every carrier together, both directions) the server carries at once. Past it an inbound
|
|
692
|
+
* caller hears "all agents are busy" and an outbound request is refused. Keep it at or below the realtime
|
|
693
|
+
* model plan's concurrent-session limit. Defaults to 25.
|
|
694
|
+
*/
|
|
695
|
+
maxConcurrentCalls: z.coerce.number().int().positive().optional().default(25),
|
|
696
|
+
/** Outbound destination policy + per-user rate limit (see {@link outboundTelephonySchema}). */
|
|
697
|
+
outbound: outboundTelephonySchema.optional().default({}),
|
|
698
|
+
/**
|
|
699
|
+
* The places the agent may hand a live call to, by name (the agent names a target, never a number, an email or an agent
|
|
700
|
+
* id). `kind` is `number` (the default; a phone number), `user` (a person at an Explorer console, who may accept or
|
|
701
|
+
* decline; `userEmail`, optional `fallbackNumber` the room dials if they do not take it) or `agent` (another MJ AI agent,
|
|
702
|
+
* by `agentName`). `user` and `agent` targets work on calls that live in a LiveKit room; a carrier call can only transfer to
|
|
703
|
+
* a `number`. Every number is validated at startup against the outbound allow/block lists; an invalid entry is dropped
|
|
704
|
+
* and logged. Empty (the default) means the agent cannot hand calls over at all.
|
|
705
|
+
*/
|
|
706
|
+
transferTargets: z
|
|
707
|
+
.array(
|
|
708
|
+
z.object({
|
|
709
|
+
name: z.string().min(1),
|
|
710
|
+
kind: z.enum(['number', 'user', 'agent']).optional(),
|
|
711
|
+
number: z.string().optional(),
|
|
712
|
+
userEmail: z.string().optional(),
|
|
713
|
+
fallbackNumber: z.string().optional(),
|
|
714
|
+
agentName: z.string().optional(),
|
|
715
|
+
description: z.string().optional(),
|
|
716
|
+
}),
|
|
717
|
+
)
|
|
718
|
+
.optional()
|
|
719
|
+
.default([]),
|
|
556
720
|
/** Twilio Programmable Voice + Media Streams binding. */
|
|
557
721
|
twilio: twilioTelephonySchema.optional(),
|
|
558
722
|
/** Vonage Voice + WebSocket-media binding. */
|
|
559
723
|
vonage: vonageTelephonySchema.optional(),
|
|
560
724
|
/** RingCentral Call Control + media-stream binding. */
|
|
561
725
|
ringcentral: ringcentralTelephonySchema.optional(),
|
|
726
|
+
/** LiveKit SIP binding: phone calls carried by a SIP trunk, landed in a LiveKit room. */
|
|
727
|
+
livekitSip: livekitSipTelephonySchema.optional(),
|
|
562
728
|
/** Microsoft Teams meetings (Graph cloud-communications + ACS application-hosted media) binding. */
|
|
563
729
|
teams: teamsMeetingsSchema.optional(),
|
|
564
730
|
}).passthrough();
|
|
565
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Mid-session identity verification for realtime sessions (see `src/realtimeSessions/README.md`).
|
|
734
|
+
*
|
|
735
|
+
* A person talking to an agent proves they control an email address WITHOUT leaving the conversation:
|
|
736
|
+
* the server emails a one-time link plus a short code, records the verification on the session, extends
|
|
737
|
+
* the session's limits, and tells the live session through a session event. The session's principal is
|
|
738
|
+
* never changed.
|
|
739
|
+
*
|
|
740
|
+
* Off by default: nothing is emailed and `/realtime/verify` is not mounted until `enabled` is true AND a
|
|
741
|
+
* `communicationProvider` is configured. Policy set here is the BASE layer; the config cascade
|
|
742
|
+
* (`channels.config.IdentityVerification`) snapshotted onto each session at mint overrides it per knob.
|
|
743
|
+
*/
|
|
744
|
+
const identityVerificationSchema = z.object({
|
|
745
|
+
/** Master switch. When false the verify routes are not mounted and the GraphQL operations refuse. */
|
|
746
|
+
enabled: zodBooleanWithTransforms().default(false),
|
|
747
|
+
/** CommunicationEngine provider that delivers the verification email (e.g. 'SendGrid', 'Microsoft Graph'). Required to send. */
|
|
748
|
+
communicationProvider: z.string().optional(),
|
|
749
|
+
/** From address of the verification email. */
|
|
750
|
+
fromAddress: z.string().optional(),
|
|
751
|
+
/**
|
|
752
|
+
* Server-only secret the typed verification code is HMAC'd under before it is stored on the session
|
|
753
|
+
* (at least 16 characters; 32+ random characters recommended). REQUIRED: without it verification is
|
|
754
|
+
* unavailable, because a session's owner can read their own session row and a bare hash of a six-digit
|
|
755
|
+
* code can be reversed offline. Never exposed to clients. Rotating it voids pending verifications only.
|
|
756
|
+
*/
|
|
757
|
+
hmacSecret: z.string().optional().default(process.env.MJ_REALTIME_VERIFICATION_SECRET || ''),
|
|
758
|
+
/**
|
|
759
|
+
* `send` (default) delivers the email. `dry-run` builds and logs the message through the communication
|
|
760
|
+
* provider WITHOUT delivering it — for rehearsal and integration environments, where a test reads the
|
|
761
|
+
* emailed link and code from the `MJ: Communication Logs` row. Never use `dry-run` in production: the
|
|
762
|
+
* Communication Log then holds live credentials and no one is emailed.
|
|
763
|
+
*/
|
|
764
|
+
deliveryMode: z.enum(['send', 'dry-run']).optional().default('send'),
|
|
765
|
+
/**
|
|
766
|
+
* Base URL the emailed link is built from (`<publicBaseUrl>/realtime/verify/<token>`). Defaults to the
|
|
767
|
+
* server's `publicUrl`, or `baseUrl:graphqlPort` when that is unset — the same derivation magic links use.
|
|
768
|
+
*/
|
|
769
|
+
publicBaseUrl: z.string().optional(),
|
|
770
|
+
/**
|
|
771
|
+
* The internal user the server acts as to read/write the session, send the email and write the audit
|
|
772
|
+
* row, matched against `User.Name` then `User.Email` (falls back to the system user, then the lowest-ID
|
|
773
|
+
* active Owner). The verify link is public, so it has no caller identity of its own.
|
|
774
|
+
*/
|
|
775
|
+
contextUserForVerification: z.string().optional(),
|
|
776
|
+
/** Base policy layer; each knob here is overridden by the per-session cascade layer. */
|
|
777
|
+
policy: z.object({
|
|
778
|
+
requireBusinessDomain: z.boolean().optional(),
|
|
779
|
+
blockedDomains: z.array(z.string()).optional(),
|
|
780
|
+
consumerDomains: z.array(z.string()).optional(),
|
|
781
|
+
linkTtlMinutes: z.coerce.number().optional(),
|
|
782
|
+
maxSendsPerSession: z.coerce.number().optional(),
|
|
783
|
+
maxCodeAttempts: z.coerce.number().optional(),
|
|
784
|
+
unverifiedMaxSeconds: z.coerce.number().optional(),
|
|
785
|
+
verifiedMaxSeconds: z.coerce.number().optional(),
|
|
786
|
+
}).optional().default({}),
|
|
787
|
+
/** Send-side and attempt-side limits (per instance, in memory; per-session limits are persisted and exact). */
|
|
788
|
+
rateLimits: z.object({
|
|
789
|
+
/** Window (ms) for the three send limits. Default 1 hour. */
|
|
790
|
+
sendWindowMs: z.coerce.number().optional().default(3_600_000),
|
|
791
|
+
/** Verification emails per client IP per window. Default 10. 0 disables. */
|
|
792
|
+
perIpSends: z.coerce.number().optional().default(10),
|
|
793
|
+
/** Verification emails per recipient domain per window. Default 30. 0 disables. */
|
|
794
|
+
perEmailDomainSends: z.coerce.number().optional().default(30),
|
|
795
|
+
/** Verification emails per recipient address per window — stops the feature being used to mail-bomb one inbox. Default 3. 0 disables. */
|
|
796
|
+
perEmailSends: z.coerce.number().optional().default(3),
|
|
797
|
+
/** Window (ms) for code attempts. Default 10 minutes. */
|
|
798
|
+
codeAttemptWindowMs: z.coerce.number().optional().default(600_000),
|
|
799
|
+
/** Code submissions per session per window. Default 10. 0 disables. */
|
|
800
|
+
perSessionCodeAttempts: z.coerce.number().optional().default(10),
|
|
801
|
+
/** Window (ms) of the public `/realtime/verify` route limiter. Default 60s. */
|
|
802
|
+
redeemWindowMs: z.coerce.number().optional().default(60_000),
|
|
803
|
+
/** `/realtime/verify` requests per IP per window. Default 30. */
|
|
804
|
+
redeemPerIp: z.coerce.number().optional().default(30),
|
|
805
|
+
}).optional().default({}),
|
|
806
|
+
}).passthrough();
|
|
807
|
+
|
|
566
808
|
const realtimeSchema = z.object({
|
|
567
809
|
/** Master switch. When false, the WebRTC SDP broker router is not mounted. Defaults to true. */
|
|
568
810
|
enabled: zodBooleanWithTransforms().default(true),
|
|
811
|
+
/** Mid-session identity verification (off by default). */
|
|
812
|
+
identityVerification: identityVerificationSchema.optional().default({}),
|
|
569
813
|
}).passthrough();
|
|
570
814
|
|
|
571
815
|
const configInfoSchema = z.object({
|
|
@@ -582,6 +826,7 @@ const configInfoSchema = z.object({
|
|
|
582
826
|
componentRegistries: z.array(componentRegistrySchema).optional(),
|
|
583
827
|
scheduledJobs: scheduledJobsSchema.optional().default({}),
|
|
584
828
|
integrationSyncWorker: integrationSyncWorkerSchema.optional().default({}),
|
|
829
|
+
workQueue: WorkQueueSchema.optional().default({}),
|
|
585
830
|
telemetry: telemetrySchema.optional().default({}),
|
|
586
831
|
queryDialects: queryDialectSchema.optional().default({}),
|
|
587
832
|
multiTenancy: multiTenancySchema.optional().default({}),
|
|
@@ -629,9 +874,11 @@ export type MagicLinkConfig = z.infer<typeof magicLinkSchema>;
|
|
|
629
874
|
export type WidgetConfig = z.infer<typeof widgetSchema>;
|
|
630
875
|
export type TelephonyConfig = z.infer<typeof telephonySchema>;
|
|
631
876
|
export type RealtimeConfig = z.infer<typeof realtimeSchema>;
|
|
877
|
+
export type IdentityVerificationConfig = z.infer<typeof identityVerificationSchema>;
|
|
632
878
|
export type TwilioTelephonyConfig = z.infer<typeof twilioTelephonySchema>;
|
|
633
879
|
export type VonageTelephonyConfig = z.infer<typeof vonageTelephonySchema>;
|
|
634
880
|
export type RingCentralTelephonyConfig = z.infer<typeof ringcentralTelephonySchema>;
|
|
881
|
+
export type LiveKitSipTelephonyConfig = z.infer<typeof livekitSipTelephonySchema>;
|
|
635
882
|
export type TeamsMeetingsConfig = z.infer<typeof teamsMeetingsSchema>;
|
|
636
883
|
export type DatabaseSettingsInfo = z.infer<typeof databaseSettingsInfoSchema>;
|
|
637
884
|
export type ViewingSystemSettingsInfo = z.infer<typeof viewingSystemInfoSchema>;
|
|
@@ -642,6 +889,7 @@ export type AuthProviderConfig = z.infer<typeof authProviderSchema>;
|
|
|
642
889
|
export type ComponentRegistryConfig = z.infer<typeof componentRegistrySchema>;
|
|
643
890
|
export type ScheduledJobsConfig = z.infer<typeof scheduledJobsSchema>;
|
|
644
891
|
export type IntegrationSyncWorkerConfig = z.infer<typeof integrationSyncWorkerSchema>;
|
|
892
|
+
export type { WorkQueueConfig } from './services/workQueueConfig.js';
|
|
645
893
|
export type TelemetryConfig = z.infer<typeof telemetrySchema>;
|
|
646
894
|
export type QueryDialectConfig = z.infer<typeof queryDialectSchema>;
|
|
647
895
|
export type MultiTenancyConfig = z.infer<typeof multiTenancySchema>;
|
|
@@ -775,6 +1023,9 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
775
1023
|
maxConcurrentRuns: 3
|
|
776
1024
|
},
|
|
777
1025
|
|
|
1026
|
+
// Work queue host defaults (off until an instance opts in)
|
|
1027
|
+
workQueue: DEFAULT_WORK_QUEUE_CONFIG,
|
|
1028
|
+
|
|
778
1029
|
// Realtime WebRTC SDP broker defaults (on by default; can be disabled via MJ_REALTIME_ENABLED=false)
|
|
779
1030
|
realtime: {
|
|
780
1031
|
enabled: RealtimeEnabledDefault(process.env.MJ_REALTIME_ENABLED),
|
|
@@ -799,6 +1050,7 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
799
1050
|
defaultTTLSeconds: 0,
|
|
800
1051
|
evictionSweepIntervalSeconds: 300,
|
|
801
1052
|
verboseLogging: false,
|
|
1053
|
+
engineSweepIntervalSeconds: 300,
|
|
802
1054
|
},
|
|
803
1055
|
|
|
804
1056
|
// Logging settings defaults — variables logging is always off unless the operator
|
package/src/context.ts
CHANGED
|
@@ -10,7 +10,8 @@ import { CloneUserForSessionContext } from './auth/sessionUserClone.js';
|
|
|
10
10
|
import { GetAPIKeyActingContextResolver } from './auth/actingContextResolver.js';
|
|
11
11
|
import { TokenExpiredError, AuthProviderFactory } from '@memberjunction/auth-providers';
|
|
12
12
|
import { AuthCache } from './cache.js';
|
|
13
|
-
import { userEmailMap, apiKey, mj_core_schema } from './config.js';
|
|
13
|
+
import { userEmailMap, apiKey, mj_core_schema, configInfo } from './config.js';
|
|
14
|
+
import { BuildPostgreSQLConnectionConfig, PostgreSQLReadOnlyPool, ResolvePostgreSQLEndpoint, ResolvePostgreSQLReadOnlyCredentials } from './postgresqlPoolSettings.js';
|
|
14
15
|
import { BuildBoundaryLogPayload } from './logging/boundaryLogPayload.js';
|
|
15
16
|
import { StartupLogger } from './logging/StartupLogger.js';
|
|
16
17
|
import { DataSourceInfo, UserPayload } from './types.js';
|
|
@@ -780,6 +781,7 @@ export const ContextFunction =
|
|
|
780
781
|
dataSources,
|
|
781
782
|
userPayload,
|
|
782
783
|
providers,
|
|
784
|
+
ClientIp: expressReq.ip,
|
|
783
785
|
};
|
|
784
786
|
};
|
|
785
787
|
|
|
@@ -810,36 +812,35 @@ async function createPerRequestProviders(
|
|
|
810
812
|
{ provider: p, type: 'Read-Write' }
|
|
811
813
|
];
|
|
812
814
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
providers.push({ provider: rp, type: 'Read-Only' });
|
|
817
|
-
}
|
|
818
|
-
} else {
|
|
819
|
-
const rp = await tryCreateReadOnlyProvider(dataSources);
|
|
820
|
-
if (rp) {
|
|
821
|
-
providers.push({ provider: rp, type: 'Read-Only' });
|
|
822
|
-
}
|
|
815
|
+
const rp = await CreateReadOnlyProvider(dataSources);
|
|
816
|
+
if (rp) {
|
|
817
|
+
providers.push({ provider: rp, type: 'Read-Only' });
|
|
823
818
|
}
|
|
824
819
|
|
|
825
820
|
return providers;
|
|
826
821
|
}
|
|
827
822
|
|
|
823
|
+
/**
|
|
824
|
+
* Opens a provider on the read-only database login, for the current platform. Resolves null when no
|
|
825
|
+
* read-only login is configured; it never falls back to the read-write pool.
|
|
826
|
+
* @param dataSources the data sources MJAPI opened at startup
|
|
827
|
+
*/
|
|
828
|
+
export async function CreateReadOnlyProvider(dataSources: DataSourceInfo[]): Promise<DatabaseProviderBase | null> {
|
|
829
|
+
return resolveDbPlatformFromEnv() === 'postgresql'
|
|
830
|
+
? tryCreateReadOnlyPostgresProvider()
|
|
831
|
+
: tryCreateReadOnlyProvider(dataSources);
|
|
832
|
+
}
|
|
833
|
+
|
|
828
834
|
/**
|
|
829
835
|
* Creates a PostgreSQL per-request provider, sharing the connection pool
|
|
830
836
|
* from the primary provider to avoid pool exhaustion.
|
|
831
837
|
*/
|
|
832
838
|
async function createPostgresProvider(): Promise<DatabaseProviderBase> {
|
|
833
839
|
const { PostgreSQLDataProvider, PostgreSQLProviderConfigData } = await import('@memberjunction/postgresql-dataprovider');
|
|
834
|
-
const pgHost = process.env.PG_HOST || process.env.DB_HOST || 'localhost';
|
|
835
|
-
const pgPort = parseInt(process.env.PG_PORT || process.env.DB_PORT || '5432', 10);
|
|
836
|
-
const pgUser = process.env.PG_USERNAME || process.env.DB_USERNAME || 'postgres';
|
|
837
|
-
const pgPass = process.env.PG_PASSWORD || process.env.DB_PASSWORD || '';
|
|
838
|
-
const pgDatabase = process.env.PG_DATABASE || process.env.DB_DATABASE || '';
|
|
839
840
|
|
|
840
841
|
const pgProvider = new PostgreSQLDataProvider();
|
|
841
842
|
const pgConfig = new PostgreSQLProviderConfigData(
|
|
842
|
-
|
|
843
|
+
BuildPostgreSQLConnectionConfig(ResolvePostgreSQLEndpoint(), configInfo.databaseSettings, 'api'),
|
|
843
844
|
mj_core_schema,
|
|
844
845
|
0,
|
|
845
846
|
undefined,
|
|
@@ -859,39 +860,30 @@ async function createPostgresProvider(): Promise<DatabaseProviderBase> {
|
|
|
859
860
|
}
|
|
860
861
|
|
|
861
862
|
/**
|
|
862
|
-
*
|
|
863
|
-
*
|
|
863
|
+
* Creates a read-only PostgreSQL per-request provider on the read-only pool MJAPI opened at
|
|
864
|
+
* startup with the read-only login. Returns null when no read-only login is configured, so the
|
|
865
|
+
* caller never falls back to the read-write pool.
|
|
864
866
|
*/
|
|
865
867
|
async function tryCreateReadOnlyPostgresProvider(): Promise<DatabaseProviderBase | null> {
|
|
866
|
-
const
|
|
867
|
-
const
|
|
868
|
-
if (!
|
|
868
|
+
const readOnlyPool = PostgreSQLReadOnlyPool.Instance.Pool;
|
|
869
|
+
const credentials = ResolvePostgreSQLReadOnlyCredentials(configInfo);
|
|
870
|
+
if (!readOnlyPool || !credentials) {
|
|
869
871
|
return null;
|
|
870
872
|
}
|
|
871
873
|
|
|
872
874
|
try {
|
|
873
875
|
const { PostgreSQLDataProvider, PostgreSQLProviderConfigData } = await import('@memberjunction/postgresql-dataprovider');
|
|
874
|
-
const pgHost = process.env.PG_HOST || process.env.DB_HOST || 'localhost';
|
|
875
|
-
const pgPort = parseInt(process.env.PG_PORT || process.env.DB_PORT || '5432', 10);
|
|
876
|
-
const pgDatabase = process.env.PG_DATABASE || process.env.DB_DATABASE || '';
|
|
877
876
|
|
|
878
877
|
const roProvider = new PostgreSQLDataProvider();
|
|
879
878
|
const roConfig = new PostgreSQLProviderConfigData(
|
|
880
|
-
{
|
|
879
|
+
BuildPostgreSQLConnectionConfig({ ...ResolvePostgreSQLEndpoint(), ...credentials }, configInfo.databaseSettings, 'read-only'),
|
|
881
880
|
mj_core_schema,
|
|
882
881
|
0,
|
|
883
882
|
undefined,
|
|
884
883
|
undefined,
|
|
885
884
|
false,
|
|
886
885
|
);
|
|
887
|
-
|
|
888
|
-
const primaryProvider = Metadata.Provider as unknown as { DatabaseConnection?: import('pg').Pool }; // global-provider-ok: bootstrap (share primary provider's PG pool with the read-only provider)
|
|
889
|
-
if (primaryProvider?.DatabaseConnection) {
|
|
890
|
-
await roProvider.ConfigWithSharedPool(roConfig, primaryProvider.DatabaseConnection);
|
|
891
|
-
} else {
|
|
892
|
-
await roProvider.Config(roConfig);
|
|
893
|
-
}
|
|
894
|
-
|
|
886
|
+
await roProvider.ConfigWithSharedPool(roConfig, readOnlyPool);
|
|
895
887
|
return roProvider;
|
|
896
888
|
} catch (_err) {
|
|
897
889
|
return null;
|