@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/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ dotenv.config({ quiet: true });
|
|
|
4
4
|
|
|
5
5
|
import { expressMiddleware } from '@as-integrations/express5';
|
|
6
6
|
import { mergeSchemas } from '@graphql-tools/schema';
|
|
7
|
-
import { Metadata, DatabasePlatform, SetProvider, StartupManager as StartupManagerImport, BaseEntity, BaseEntityEvent, RunView, DatabaseProviderBase, ResolveStartupMode } from '@memberjunction/core';
|
|
7
|
+
import { Metadata, DatabasePlatform, SetProvider, StartupManager as StartupManagerImport, BaseEntity, BaseEntityEvent, RunView, DatabaseProviderBase, ProviderBase, ResolveStartupMode } from '@memberjunction/core';
|
|
8
8
|
import { UserCache, resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
|
|
9
9
|
import { MJGlobal, MJEventType, UUIDsEqual, ShutdownRegistry } from '@memberjunction/global';
|
|
10
10
|
import { setupSQLServerClient, SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
@@ -19,9 +19,10 @@ import { default as fg } from 'fast-glob';
|
|
|
19
19
|
import { useServer } from 'graphql-ws/lib/use/ws';
|
|
20
20
|
import { createServer } from 'node:http';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
23
24
|
import { Session as InspectorSession } from 'node:inspector';
|
|
24
|
-
import { sep } from 'node:path';
|
|
25
|
+
import { join, sep } from 'node:path';
|
|
25
26
|
import 'reflect-metadata';
|
|
26
27
|
import { ReplaySubject } from 'rxjs';
|
|
27
28
|
import { BuildSchemaOptions, buildSchemaSync, getMetadataStorage, GraphQLTimestamp, PubSubEngine } from 'type-graphql';
|
|
@@ -31,8 +32,10 @@ import { WebSocketServer } from 'ws';
|
|
|
31
32
|
import { RealtimeProxyServer } from './realtimeProxy/RealtimeProxyServer.js';
|
|
32
33
|
import buildApolloServer from './apolloServer/index.js';
|
|
33
34
|
import { configInfo, configFilePath, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage, RESTApiOptions } from './config.js';
|
|
35
|
+
import { TranslateBracketsToPG } from './postgresqlCompat.js';
|
|
36
|
+
import { BuildPostgreSQLConnectionConfig, DescribeReadOnlyLoginOverreach, PostgreSQLReadOnlyPool, ResolvePostgreSQLEndpoint, ResolvePostgreSQLReadOnlyCredentials, ToPGPoolConfig } from './postgresqlPoolSettings.js';
|
|
34
37
|
import { default as jwt } from 'jsonwebtoken';
|
|
35
|
-
import { contextFunction, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
|
|
38
|
+
import { contextFunction, CreateReadOnlyProvider, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
|
|
36
39
|
import { UserPayload } from './types.js';
|
|
37
40
|
import { requireSystemUserDirective, publicDirective } from './directives/index.js';
|
|
38
41
|
import { variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
|
|
@@ -48,6 +51,14 @@ import { createRealtimeSdpBrokerRouter } from './rest/RealtimeSdpBrokerHandler.j
|
|
|
48
51
|
import { REALTIME_SDP_EXCHANGE_PATH } from '@memberjunction/ai';
|
|
49
52
|
import { createMagicLinkHandler, createMagicLinkJwksRouter, registerMagicLinkAuthProvider, MAGIC_LINK_MOUNT_PATH } from './auth/magicLink/index.js';
|
|
50
53
|
import { createWidgetHandler, WIDGET_MOUNT_PATH } from './realtimeWidget/index.js';
|
|
54
|
+
import {
|
|
55
|
+
CreateRealtimeVerifyRouter,
|
|
56
|
+
REALTIME_VERIFY_MOUNT_PATH,
|
|
57
|
+
RealtimeSessionEventService,
|
|
58
|
+
RealtimeSessionVerificationService,
|
|
59
|
+
ParseReplicatedRealtimeSessionEvent,
|
|
60
|
+
type RealtimeSessionEventTopicPayload,
|
|
61
|
+
} from './realtimeSessions/index.js';
|
|
51
62
|
import { resolve } from 'node:path';
|
|
52
63
|
import { DataSourceInfo, raiseEvent } from './types.js';
|
|
53
64
|
|
|
@@ -58,21 +69,28 @@ import { LocalCacheManager, StartupManager, TelemetryManager, TelemetryLevel, Lo
|
|
|
58
69
|
import { getSystemUser, validateAuthProvidersRegistered } from './auth/index.js';
|
|
59
70
|
import { createAuthProviderCatalogRouter, AUTH_CATALOG_MOUNT_PATH } from './auth/AuthProviderCatalogRouter.js';
|
|
60
71
|
import { GetAPIKeyEngine } from '@memberjunction/api-keys';
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
72
|
+
import { ActionEngineServer } from '@memberjunction/actions';
|
|
73
|
+
import { CreateAdhocSQLAuthorizer } from './resolvers/AdhocQueryResolver.js';
|
|
74
|
+
import { CacheManagerConfigFromSettings, CreateSharedCacheFromEnvironment, StartEngineSweeper, StartMetadataSweep, StartUserCacheChecks, WarmupLeaseMsFromSettings, WirePushStatusFanOut, WireSharedCacheEvents } from './sharedCache.js';
|
|
63
75
|
import { PubSubManager } from './generic/PubSubManager.js';
|
|
64
76
|
import { ReconcileOrphanedConversationDetails } from './generic/OrphanedConversationDetailReconciler.js';
|
|
65
77
|
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
HANDOFF_OFFER_FANOUT_CHANNEL,
|
|
79
|
+
HANDOFF_OFFER_TOPIC,
|
|
80
|
+
SetHandoffOfferPublishHook,
|
|
81
|
+
ParseReplicatedHandoffOfferUpdate,
|
|
82
|
+
} from './resolvers/HumanHandoffResolver.js';
|
|
83
|
+
import { RoomHandoffEngine } from '@memberjunction/livekit-room-server';
|
|
84
|
+
// Type-only: the provider is built in ./sharedCache.ts. Still needed here because the handoff-offer
|
|
85
|
+
// fan-out below stays in this file — see the note at its call site.
|
|
86
|
+
import type { RedisLocalStorageProvider } from '@memberjunction/redis-provider';
|
|
70
87
|
import { IntegrationProgressEmitter } from '@memberjunction/integration-progress-artifacts';
|
|
71
88
|
import { PublishIntegrationProgress } from './resolvers/IntegrationProgressResolver.js';
|
|
72
89
|
import { RegisterRSUProgressBridge } from './integration/RSUProgressBridge.js';
|
|
73
90
|
import { ClientToolRequestManager, AgentRunWatchdog } from '@memberjunction/ai-agents';
|
|
74
|
-
import { SessionJanitor } from './agentSessions/index.js';
|
|
91
|
+
import { BindBridgeEngineHostInstance, SessionJanitor, SetHostInstancePort, StartBridgeOrphanReconciliation } from './agentSessions/index.js';
|
|
75
92
|
import { StartTaskGraphDispatcher } from './services/StartTaskGraphDispatcher.js';
|
|
93
|
+
import { MJServerWorkQueueProviderSource, StartWorkQueueHost } from './services/WorkQueueHostService.js';
|
|
76
94
|
import { GetAttachmentService } from '@memberjunction/aiengine';
|
|
77
95
|
import { MJStorageBlobStore } from './services/MJStorageBlobStore.js';
|
|
78
96
|
import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData, ConfigureRecordDataBroadcast } from './generic/CacheInvalidationResolver.js';
|
|
@@ -122,6 +140,7 @@ export * from 'type-graphql';
|
|
|
122
140
|
// later importers, which makes schema build fail on ViewResult.RowCount.
|
|
123
141
|
export { Int, Float, ID } from 'type-graphql';
|
|
124
142
|
export { NewUserBase } from './auth/newUsers.js';
|
|
143
|
+
export { CreateIsolatedProvider } from './isolatedProvider.js';
|
|
125
144
|
export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
|
|
126
145
|
export { ServerExtensionLoader, BaseServerExtension, DefaultServerExtensionServiceRegistry } from '@memberjunction/server-extensions-core';
|
|
127
146
|
export type {
|
|
@@ -186,6 +205,8 @@ export * from './resolvers/GenerateSeedTaxonomyResolver.js';
|
|
|
186
205
|
export * from './resolvers/PipelineProgressResolver.js';
|
|
187
206
|
export * from './resolvers/IntegrationProgressResolver.js';
|
|
188
207
|
export * from './resolvers/IdentityClaimRedemptionResolver.js';
|
|
208
|
+
export * from './resolvers/UserAvatarResolver.js';
|
|
209
|
+
export * from './resolvers/avatarInputValidation.js';
|
|
189
210
|
export * from './resolvers/ClientToolRequestResolver.js';
|
|
190
211
|
export * from './resolvers/AutotagPipelineResolver.js';
|
|
191
212
|
export * from './resolvers/TagGovernanceResolver.js';
|
|
@@ -243,6 +264,10 @@ export * from './resolvers/CurrentUserContextResolver.js';
|
|
|
243
264
|
export * from './resolvers/RSUResolver.js';
|
|
244
265
|
export * from './resolvers/AgentSessionResolver.js';
|
|
245
266
|
export * from './resolvers/RealtimeClientSessionResolver.js';
|
|
267
|
+
export * from './resolvers/RealtimeSessionEventsResolver.js';
|
|
268
|
+
export * from './resolvers/RealtimeSessionVerificationResolver.js';
|
|
269
|
+
export * from './realtimeSessions/index.js';
|
|
270
|
+
export * from './resolvers/MeetingResolver.js';
|
|
246
271
|
export * from './resolvers/RemoteBrowserActionResolver.js';
|
|
247
272
|
export * from './agentSessions/index.js';
|
|
248
273
|
export { GetReadOnlyDataSource, GetReadWriteDataSource, GetReadWriteProvider, GetReadOnlyProvider } from './util.js';
|
|
@@ -296,7 +321,8 @@ function resolveServerVersion(): string | undefined {
|
|
|
296
321
|
const pkgPath = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
297
322
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version?: string };
|
|
298
323
|
return pkg.version;
|
|
299
|
-
} catch {
|
|
324
|
+
} catch (err) {
|
|
325
|
+
LogError('Failed to resolve server version from package.json', undefined, err);
|
|
300
326
|
return undefined;
|
|
301
327
|
}
|
|
302
328
|
}
|
|
@@ -304,55 +330,80 @@ function resolveServerVersion(): string | undefined {
|
|
|
304
330
|
/** How often to re-check for conversation details left behind by finished runs. */
|
|
305
331
|
const ORPHAN_DETAIL_SWEEP_INTERVAL_MS = 5 * 60_000;
|
|
306
332
|
|
|
307
|
-
/** Redis channel carrying replicated push-status updates between server instances. */
|
|
308
|
-
const PUSH_STATUS_FANOUT_CHANNEL = 'push-status-updates';
|
|
309
|
-
|
|
310
333
|
/**
|
|
311
|
-
* Replicate
|
|
312
|
-
*
|
|
313
|
-
* Outbound: every locally-published update is forwarded on a shared channel. Inbound: a message
|
|
314
|
-
* from another instance is republished onto THIS instance's local topic, where the normal
|
|
315
|
-
* subscription filter decides who receives it — so the identity gate (`ownerUserId` vs. the
|
|
316
|
-
* connection's authenticated user) still applies to a replicated message exactly as it does to a
|
|
317
|
-
* local one. The replica has no say in who sees what.
|
|
334
|
+
* Replicate handoff-offer updates across server instances over Redis.
|
|
318
335
|
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
336
|
+
* Outbound: every locally-published offer change is forwarded on a shared Redis channel.
|
|
337
|
+
* Inbound: a message from another instance is republished onto THIS instance's local GraphQL
|
|
338
|
+
* topic (where the subscription filter scopes to the target user) and delivered to RoomHandoffEngine
|
|
339
|
+
* so the call-hosting instance can transition its local flow (e.g. wait for the user to join the room).
|
|
322
340
|
*/
|
|
323
|
-
async function
|
|
341
|
+
async function wireHandoffOfferFanOut(redisProvider: RedisLocalStorageProvider, startupLog: StartupLogger): Promise<void> {
|
|
324
342
|
try {
|
|
325
|
-
await redisProvider.SubscribeToChannel(
|
|
343
|
+
await redisProvider.SubscribeToChannel(HANDOFF_OFFER_FANOUT_CHANNEL, (raw: string) => {
|
|
326
344
|
try {
|
|
327
|
-
const payload =
|
|
345
|
+
const payload = ParseReplicatedHandoffOfferUpdate(raw, MJGlobal.Instance.ProcessUUID);
|
|
328
346
|
if (!payload) {
|
|
329
347
|
return;
|
|
330
348
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
ownerUserId: payload.ownerUserId,
|
|
336
|
-
message: payload.message,
|
|
337
|
-
SourceServerId: payload.SourceServerId,
|
|
349
|
+
PubSubManager.Instance.Publish(HANDOFF_OFFER_TOPIC, {
|
|
350
|
+
UserID: payload.UserID,
|
|
351
|
+
Kind: payload.Kind,
|
|
352
|
+
Offer: payload.Offer,
|
|
338
353
|
});
|
|
339
|
-
|
|
354
|
+
RoomHandoffEngine.Instance.OnRemoteOfferChange(payload);
|
|
355
|
+
} catch (err) {
|
|
340
356
|
// A malformed message on a shared channel must not take down the subscriber.
|
|
357
|
+
LogError('Error processing handoff-offer fan-out message', undefined, err);
|
|
341
358
|
}
|
|
342
359
|
});
|
|
343
360
|
|
|
344
|
-
|
|
345
|
-
redisProvider.PublishMessage(
|
|
361
|
+
SetHandoffOfferPublishHook((payload) => {
|
|
362
|
+
redisProvider.PublishMessage(HANDOFF_OFFER_FANOUT_CHANNEL, JSON.stringify(payload));
|
|
346
363
|
});
|
|
347
364
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
console.
|
|
365
|
+
console.log('[MJAPI] Handoff-offer updates: cross-instance fan-out enabled via Redis');
|
|
366
|
+
startupLog.LogIf('verbose', 'Handoff-offer updates: cross-instance fan-out enabled via Redis');
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.warn(`Handoff-offer fan-out unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Redis channel carrying replicated realtime session events between server instances. */
|
|
373
|
+
const REALTIME_SESSION_EVENTS_FANOUT_CHANNEL = 'realtime-session-events';
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Replicate realtime session events across server instances over Redis.
|
|
377
|
+
*
|
|
378
|
+
* Behind a load balancer the browser's WebSocket (the `RealtimeSessionEvents` subscription) lives on one
|
|
379
|
+
* replica while the request that raises the event — typically the public verify-link redemption — can be
|
|
380
|
+
* handled by another. The session-events topic is an in-process PubSub, so without this an
|
|
381
|
+
* `identity.verified` published on replica B never reaches the subscriber on replica A.
|
|
382
|
+
*
|
|
383
|
+
* Inbound messages are republished onto THIS instance's local topic only (never back through the
|
|
384
|
+
* replication hook, so there is no loop) and are still judged by the subscription filter against the
|
|
385
|
+
* connection's identity — a replica has no say in who sees what. The durable
|
|
386
|
+
* `RealtimeSessionVerificationStatus` query remains the backstop if Redis is down.
|
|
387
|
+
*/
|
|
388
|
+
async function wireRealtimeSessionEventFanOut(redisProvider: RedisLocalStorageProvider): Promise<void> {
|
|
389
|
+
try {
|
|
390
|
+
await redisProvider.SubscribeToChannel(REALTIME_SESSION_EVENTS_FANOUT_CHANNEL, (raw: string) => {
|
|
391
|
+
try {
|
|
392
|
+
const payload = ParseReplicatedRealtimeSessionEvent(raw, MJGlobal.Instance.ProcessUUID);
|
|
393
|
+
if (payload) {
|
|
394
|
+
RealtimeSessionEventService.Instance.PublishReplicated(payload);
|
|
395
|
+
}
|
|
396
|
+
} catch (err) {
|
|
397
|
+
LogError(`Realtime session event fan-out: dropping a malformed replicated event: ${(err as Error).message}`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
RealtimeSessionEventService.Instance.SetReplicationHook((payload: RealtimeSessionEventTopicPayload) => {
|
|
401
|
+
redisProvider.PublishMessage(REALTIME_SESSION_EVENTS_FANOUT_CHANNEL, JSON.stringify(payload));
|
|
402
|
+
});
|
|
403
|
+
console.log('[MJAPI] Realtime session events: cross-instance fan-out enabled via Redis');
|
|
352
404
|
} catch (err) {
|
|
353
|
-
// Single-instance delivery still works
|
|
354
|
-
|
|
355
|
-
console.warn(`Push-status fan-out unavailable: ${(err as Error).message}`);
|
|
405
|
+
// Single-instance delivery still works and the status query covers the rest: degraded, not broken.
|
|
406
|
+
console.warn(`Realtime session event fan-out unavailable: ${(err as Error).message}`);
|
|
356
407
|
}
|
|
357
408
|
}
|
|
358
409
|
|
|
@@ -369,6 +420,12 @@ GetAttachmentService().BlobStore = new MJStorageBlobStore();
|
|
|
369
420
|
|
|
370
421
|
export const Serve = async (resolverPaths: Array<string>, app: Application = CreateApp(), options?: MJServerOptions): Promise<void> => {
|
|
371
422
|
const t0 = performance.now();
|
|
423
|
+
// Name this MJAPI instance by its port before anything stamps a session row with its identity, so the janitor's
|
|
424
|
+
// startup recovery closes only the sessions an earlier boot of this instance left, never those of another MJAPI on
|
|
425
|
+
// the same host (#5309).
|
|
426
|
+
SetHostInstancePort(graphqlPort);
|
|
427
|
+
// The realtime bridge engine stamps its bridge rows with the same identity and reconciles orphans by it (#5310).
|
|
428
|
+
BindBridgeEngineHostInstance();
|
|
372
429
|
// Level-gated startup logger. Resolves verbosity from telemetry.level (single
|
|
373
430
|
// operator knob). At `standard` (default), per-phase timings are collapsed into
|
|
374
431
|
// the one-line summary; at `verbose`+ each phase prints inline as before.
|
|
@@ -397,30 +454,37 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
397
454
|
const dbType = GetDbType();
|
|
398
455
|
const dataSources: DataSourceInfo[] = [];
|
|
399
456
|
|
|
457
|
+
// Shared cache first: the database provider loads metadata and startup engines through
|
|
458
|
+
// it, so the first server to start fills it and later servers read it instead of each querying
|
|
459
|
+
// the database and broadcasting what they loaded. Subscribed before engines load so they hear
|
|
460
|
+
// other servers while they start.
|
|
461
|
+
const sharedCache = CreateSharedCacheFromEnvironment(configInfo.cacheSettings);
|
|
462
|
+
if (sharedCache) {
|
|
463
|
+
await WireSharedCacheEvents(sharedCache, () => (Metadata.Provider instanceof ProviderBase ? Metadata.Provider : undefined)); // global-provider-ok: bootstrap
|
|
464
|
+
// The cross-instance fan-outs ride this connection. Push-status moved into sharedCache.ts
|
|
465
|
+
// with the rest of the Redis wiring; the handoff and realtime-session ones stay here deliberately —
|
|
466
|
+
// they reach into RoomHandoffEngine and RealtimeSessionEventService, and the cache module should not
|
|
467
|
+
// acquire a dependency on either.
|
|
468
|
+
await WirePushStatusFanOut(sharedCache);
|
|
469
|
+
await wireHandoffOfferFanOut(sharedCache, startupLog);
|
|
470
|
+
await wireRealtimeSessionEventFanOut(sharedCache);
|
|
471
|
+
startupLog.LogIf('verbose', `Redis cache provider connected: ${process.env.REDIS_URL}`);
|
|
472
|
+
}
|
|
473
|
+
const cacheManagerConfig = CacheManagerConfigFromSettings(configInfo.cacheSettings);
|
|
474
|
+
const warmupLeaseMs = WarmupLeaseMsFromSettings(configInfo.cacheSettings);
|
|
475
|
+
|
|
400
476
|
if (dbType === 'postgresql') {
|
|
401
477
|
// ─── PostgreSQL Path ───────────────────────────────────────────
|
|
402
478
|
startupLog.BeginPhase('Connecting to database');
|
|
403
479
|
startupLog.LogIf('verbose', 'Database type: PostgreSQL');
|
|
404
480
|
const pg = await import('pg');
|
|
405
|
-
const { PostgreSQLDataProvider, PostgreSQLProviderConfigData } = await import('@memberjunction/postgresql-dataprovider');
|
|
481
|
+
const { PostgreSQLDataProvider, PostgreSQLProviderConfigData, MJPostgresTypes } = await import('@memberjunction/postgresql-dataprovider');
|
|
406
482
|
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
const pgPool = new pg.default.Pool({
|
|
414
|
-
host: pgHost,
|
|
415
|
-
port: pgPort,
|
|
416
|
-
user: pgUser,
|
|
417
|
-
password: pgPass,
|
|
418
|
-
database: pgDatabase,
|
|
419
|
-
max: configInfo.databaseSettings.connectionPool?.max ?? 50,
|
|
420
|
-
min: configInfo.databaseSettings.connectionPool?.min ?? 5,
|
|
421
|
-
idleTimeoutMillis: configInfo.databaseSettings.connectionPool?.idleTimeoutMillis ?? 30000,
|
|
422
|
-
connectionTimeoutMillis: configInfo.databaseSettings.connectionPool?.acquireTimeoutMillis ?? 30000,
|
|
423
|
-
});
|
|
483
|
+
const pgEndpoint = ResolvePostgreSQLEndpoint();
|
|
484
|
+
const { Host: pgHost, Port: pgPort, User: pgUser, Database: pgDatabase } = pgEndpoint;
|
|
485
|
+
// Every API pool carries the statement and idle-in-transaction timeouts from connection #1.
|
|
486
|
+
const pgConnectionConfig = BuildPostgreSQLConnectionConfig(pgEndpoint, configInfo.databaseSettings, 'api');
|
|
487
|
+
const pgPool = new pg.default.Pool(ToPGPoolConfig(pgConnectionConfig));
|
|
424
488
|
|
|
425
489
|
// Verify connection
|
|
426
490
|
const testClient = await pgPool.connect();
|
|
@@ -428,6 +492,28 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
428
492
|
testClient.release();
|
|
429
493
|
startupLog.LogIf('verbose', `PostgreSQL pool connected to ${pgHost}:${pgPort}/${pgDatabase}`);
|
|
430
494
|
|
|
495
|
+
// A read-only pool opened with the read-only login, as SQL Server has. Read-only per-request
|
|
496
|
+
// providers (TestQuerySQL and other caller-supplied SQL) share it instead of the primary pool.
|
|
497
|
+
const pgReadOnlyCredentials = ResolvePostgreSQLReadOnlyCredentials(configInfo);
|
|
498
|
+
if (pgReadOnlyCredentials) {
|
|
499
|
+
// A pool a provider runs on must carry the provider's type parsers, so BIGINT and NUMERIC
|
|
500
|
+
// come back as numbers here as they do on the provider's own pool.
|
|
501
|
+
const readOnlyPgPool = new pg.default.Pool({
|
|
502
|
+
...ToPGPoolConfig(BuildPostgreSQLConnectionConfig({ ...pgEndpoint, ...pgReadOnlyCredentials }, configInfo.databaseSettings, 'read-only')),
|
|
503
|
+
types: MJPostgresTypes,
|
|
504
|
+
});
|
|
505
|
+
const readOnlyTestClient = await readOnlyPgPool.connect();
|
|
506
|
+
try {
|
|
507
|
+
for (const warning of await DescribeReadOnlyLoginOverreach(readOnlyTestClient, mj_core_schema)) {
|
|
508
|
+
LogStatus(`WARNING: ${warning}`);
|
|
509
|
+
}
|
|
510
|
+
} finally {
|
|
511
|
+
readOnlyTestClient.release();
|
|
512
|
+
}
|
|
513
|
+
PostgreSQLReadOnlyPool.Instance.Pool = readOnlyPgPool;
|
|
514
|
+
startupLog.LogIf('verbose', 'Read-only PostgreSQL pool has been initialized.');
|
|
515
|
+
}
|
|
516
|
+
|
|
431
517
|
// Create a DataSourceInfo with a MSSQL-compatible wrapper around pg.Pool
|
|
432
518
|
// This allows existing code (types, util, context) to work without changes
|
|
433
519
|
const mssqlCompatPool = createMSSQLCompatPool(pgPool);
|
|
@@ -441,20 +527,12 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
441
527
|
}));
|
|
442
528
|
|
|
443
529
|
// Set up the PostgreSQL provider
|
|
444
|
-
const pgConnectionConfig = {
|
|
445
|
-
Host: pgHost,
|
|
446
|
-
Port: pgPort,
|
|
447
|
-
Database: pgDatabase,
|
|
448
|
-
User: pgUser,
|
|
449
|
-
Password: pgPass,
|
|
450
|
-
MaxConnections: configInfo.databaseSettings.connectionPool?.max ?? 50,
|
|
451
|
-
MinConnections: configInfo.databaseSettings.connectionPool?.min ?? 5,
|
|
452
|
-
};
|
|
453
530
|
const pgConfigData = new PostgreSQLProviderConfigData(
|
|
454
531
|
pgConnectionConfig,
|
|
455
532
|
mj_core_schema,
|
|
456
533
|
cacheRefreshInterval / 1000, // convert ms to seconds
|
|
457
534
|
);
|
|
535
|
+
pgConfigData.LocalStorageProvider = sharedCache ?? undefined;
|
|
458
536
|
const provider = new PostgreSQLDataProvider();
|
|
459
537
|
await provider.Config(pgConfigData);
|
|
460
538
|
SetProvider(provider);
|
|
@@ -466,7 +544,7 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
466
544
|
const sysUser = UserCache.Instance.GetSystemUser();
|
|
467
545
|
const backupSysUser = UserCache.Instance.Users.find(u => u.IsActive && u.Type === 'Owner');
|
|
468
546
|
const pgStartupMode = ResolveStartupMode({ configValue: configInfo.startup?.mode, defaultMode: 'full' });
|
|
469
|
-
await StartupManagerImport.Instance.Startup(false, sysUser || backupSysUser, provider, { mode: pgStartupMode.mode });
|
|
547
|
+
await StartupManagerImport.Instance.Startup(false, sysUser || backupSysUser, provider, { mode: pgStartupMode.mode, cacheManagerConfig, warmupLeaseMs });
|
|
470
548
|
|
|
471
549
|
// Both provider sources have now had their turn — config/env at module load, metadata via
|
|
472
550
|
// AuthProviderEngine's startup hook — so "no providers at all" is finally a meaningful check.
|
|
@@ -483,7 +561,7 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
483
561
|
if (poolAny._pgPool) {
|
|
484
562
|
const thePgPool = poolAny._pgPool as import('pg').Pool;
|
|
485
563
|
// Translate SQL Server bracket syntax to PostgreSQL double-quote syntax
|
|
486
|
-
const pgQuery =
|
|
564
|
+
const pgQuery = TranslateBracketsToPG(query);
|
|
487
565
|
const result = await thePgPool.query(pgQuery);
|
|
488
566
|
return result.rows;
|
|
489
567
|
}
|
|
@@ -502,21 +580,21 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
502
580
|
const pgCodegenPass = process.env.CODEGEN_DB_PASSWORD;
|
|
503
581
|
if (pgCodegenUser && pgCodegenPass) {
|
|
504
582
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
583
|
+
// CodeGen and DDL work runs long statements across every entity, so this pool gets the
|
|
584
|
+
// long CodeGen timeout rather than the API one, as the SQL Server CodeGen pool does.
|
|
585
|
+
const codegenPgConnectionConfig = BuildPostgreSQLConnectionConfig(
|
|
586
|
+
{ ...pgEndpoint, User: pgCodegenUser, Password: pgCodegenPass },
|
|
587
|
+
configInfo.databaseSettings,
|
|
588
|
+
'codegen',
|
|
589
|
+
);
|
|
590
|
+
const codegenPgPool = new pg.default.Pool(ToPGPoolConfig(codegenPgConnectionConfig));
|
|
513
591
|
const codegenTestClient = await codegenPgPool.connect();
|
|
514
592
|
await codegenTestClient.query('SELECT 1');
|
|
515
593
|
codegenTestClient.release();
|
|
516
594
|
|
|
517
595
|
const { RuntimeSchemaManager } = await import('@memberjunction/schema-engine');
|
|
518
596
|
const codegenPgConfigData = new PostgreSQLProviderConfigData(
|
|
519
|
-
|
|
597
|
+
codegenPgConnectionConfig,
|
|
520
598
|
mj_core_schema,
|
|
521
599
|
cacheRefreshInterval / 1000, // ms → seconds
|
|
522
600
|
);
|
|
@@ -624,10 +702,11 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
624
702
|
|
|
625
703
|
// cacheRefreshInterval is configured in ms; checkRefreshIntervalSeconds declares seconds — see providerConfigUnits.ts
|
|
626
704
|
const config = new SQLServerProviderConfigData(pool, mj_core_schema, MetadataCacheRefreshIntervalSeconds(cacheRefreshInterval));
|
|
705
|
+
config.LocalStorageProvider = sharedCache ?? undefined;
|
|
627
706
|
// MJAPI is a long-running server, so entry-point default is 'full' engine pre-warm;
|
|
628
707
|
// MJ_STARTUP_MODE / mj.config.cjs startup.mode can override per the shared precedence chain
|
|
629
708
|
const startupMode = ResolveStartupMode({ configValue: configInfo.startup?.mode, defaultMode: 'full' });
|
|
630
|
-
await setupSQLServerClient(config, { mode: startupMode.mode });
|
|
709
|
+
await setupSQLServerClient(config, { mode: startupMode.mode, cacheManagerConfig, warmupLeaseMs });
|
|
631
710
|
|
|
632
711
|
// See the note on the PostgreSQL path above: this is the first point at which both the
|
|
633
712
|
// config/env providers and the metadata catalog have been registered.
|
|
@@ -752,83 +831,31 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
752
831
|
startupLog.LogIf('verbose', 'Server telemetry disabled');
|
|
753
832
|
}
|
|
754
833
|
|
|
755
|
-
//
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
});
|
|
763
|
-
(Metadata.Provider as GenericDatabaseProvider).SetLocalStorageProvider(redisProvider); // global-provider-ok: bootstrap (Redis cache wiring)
|
|
764
|
-
await redisProvider.StartListening();
|
|
765
|
-
|
|
766
|
-
// Connect Redis pub/sub events to LocalCacheManager callback dispatch
|
|
767
|
-
// so cross-server cache invalidation messages are routed to registered callbacks
|
|
768
|
-
redisProvider.OnCacheChanged((event) => {
|
|
769
|
-
const sourceShort = event.SourceServerId ? event.SourceServerId.substring(0, 8) : 'unknown';
|
|
770
|
-
console.log(`[MJAPI] Redis pub/sub → DispatchCacheChange: ${event.Action} for "${event.CacheKey}" from server ${sourceShort}`);
|
|
771
|
-
LocalCacheManager.Instance.DispatchCacheChange(event);
|
|
772
|
-
|
|
773
|
-
// Also broadcast to connected browser clients via GraphQL subscription
|
|
774
|
-
// Extract entity name from the cache key (format: EntityName|Filter|OrderBy|...)
|
|
775
|
-
const entityName = event.CacheKey ? event.CacheKey.split('|')[0] : '';
|
|
776
|
-
if (entityName) {
|
|
777
|
-
PubSubManager.Instance.Publish(CACHE_INVALIDATION_TOPIC, {
|
|
778
|
-
entityName,
|
|
779
|
-
primaryKeyValues: null, // entity-level invalidation
|
|
780
|
-
action: event.Action || 'save',
|
|
781
|
-
sourceServerId: event.SourceServerId || 'unknown',
|
|
782
|
-
timestamp: new Date(),
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
// Fan push-status updates across server instances (MJ #4222).
|
|
788
|
-
//
|
|
789
|
-
// Behind a load balancer the browser's WebSocket lives on one replica while the mutation that
|
|
790
|
-
// drives the agent can be handled by another. The push topic is an in-process PubSub, so a
|
|
791
|
-
// completion published on replica B never reaches a subscriber on replica A — the browser waits
|
|
792
|
-
// forever for an event that was delivered to nobody. Replicating progress and completion over
|
|
793
|
-
// Redis closes that, and the durable tail query remains the backstop if Redis is down.
|
|
794
|
-
await wirePushStatusFanOut(redisProvider, startupLog);
|
|
795
|
-
|
|
796
|
-
startupLog.LogIf('verbose', `Redis cache provider connected: ${process.env.REDIS_URL}`);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// If Redis is available, swap LocalCacheManager's storage provider to Redis.
|
|
800
|
-
// LocalCacheManager may have already been initialized (with in-memory provider)
|
|
801
|
-
// during engine loading. SetStorageProvider migrates cached data to Redis.
|
|
802
|
-
if (process.env.REDIS_URL) {
|
|
803
|
-
await LocalCacheManager.Instance.SetStorageProvider(Metadata.Provider.LocalStorageProvider); // global-provider-ok: bootstrap
|
|
804
|
-
startupLog.LogIf('verbose', 'LocalCacheManager: storage provider swapped to Redis');
|
|
805
|
-
}
|
|
806
|
-
// Ensure LocalCacheManager is initialized (no-op if already done during engine loading)
|
|
807
|
-
if (!LocalCacheManager.Instance.IsInitialized) {
|
|
808
|
-
// Build cache config from mj.config.cjs cacheSettings
|
|
809
|
-
const cs = configInfo.cacheSettings;
|
|
810
|
-
const cacheConfig = {
|
|
811
|
-
maxSizeBytes: (cs.maxMemoryMB ?? 150) * 1024 * 1024,
|
|
812
|
-
maxPercentOfCachePerEntity: cs.maxPercentOfCachePerEntity ?? 50,
|
|
813
|
-
defaultTTLMs: (cs.defaultTTLSeconds ?? 0) * 1000,
|
|
814
|
-
evictionSweepIntervalMs: (cs.evictionSweepIntervalSeconds ?? 300) * 1000,
|
|
815
|
-
verboseLogging: cs.verboseLogging ?? false,
|
|
816
|
-
};
|
|
817
|
-
await LocalCacheManager.Instance.Initialize(Metadata.Provider.LocalStorageProvider, cacheConfig); // global-provider-ok: bootstrap
|
|
818
|
-
if (startupLog.IsAtLeast('verbose')) {
|
|
819
|
-
// eslint-disable-next-line no-console
|
|
820
|
-
console.log('LocalCacheManager initialized with cache config:', JSON.stringify({
|
|
821
|
-
maxMemoryMB: cs.maxMemoryMB ?? 150,
|
|
822
|
-
maxPercentOfCachePerEntity: cs.maxPercentOfCachePerEntity ?? 50,
|
|
823
|
-
evictionSweepIntervalSeconds: cs.evictionSweepIntervalSeconds ?? 300,
|
|
824
|
-
}));
|
|
825
|
-
}
|
|
834
|
+
// The database provider already initialized LocalCacheManager from inside its own Config(), with
|
|
835
|
+
// no settings — this call is what makes `cacheSettings` take effect (Initialize merges a config
|
|
836
|
+
// handed to it later). Unconditional on purpose: the guard that used to sit here
|
|
837
|
+
// skipped exactly the case that needed it.
|
|
838
|
+
await LocalCacheManager.Instance.Initialize(Metadata.Provider.LocalStorageProvider, cacheManagerConfig); // global-provider-ok: bootstrap
|
|
839
|
+
if (sharedCache) {
|
|
840
|
+
await LocalCacheManager.Instance.SetStorageProvider(sharedCache);
|
|
826
841
|
}
|
|
842
|
+
const engineSweepMs = StartEngineSweeper(configInfo.cacheSettings);
|
|
843
|
+
startupLog.LogIf('verbose', engineSweepMs > 0 ? `Engine/database sweep every ${engineSweepMs / 1000}s` : 'Engine/database sweep disabled');
|
|
844
|
+
const userCacheCheckMs = StartUserCacheChecks(configInfo.cacheSettings);
|
|
845
|
+
startupLog.LogIf('verbose', userCacheCheckMs > 0 ? `User cache staleness check every ${userCacheCheckMs / 1000}s` : 'User cache staleness check disabled');
|
|
846
|
+
// Costs nothing unless a metadata entity declares TrustServerCacheCompletely = false.
|
|
847
|
+
const metadataSweepMs = StartMetadataSweep(configInfo.cacheSettings, () => (Metadata.Provider instanceof ProviderBase ? Metadata.Provider : undefined)); // global-provider-ok: bootstrap (this process's one provider)
|
|
848
|
+
startupLog.LogIf('verbose', metadataSweepMs > 0 ? `Metadata/database sweep every ${metadataSweepMs / 1000}s (only for entities declaring drift)` : 'Metadata/database sweep disabled');
|
|
827
849
|
|
|
828
850
|
// Initialize APIKeyEngine singleton — reads apiKeyGeneration from mj.config.cjs automatically
|
|
829
851
|
// This must happen before any request handler calls GetAPIKeyEngine()
|
|
830
852
|
GetAPIKeyEngine();
|
|
831
853
|
|
|
854
|
+
// Actions that run caller-supplied SQL (Run Ad-hoc Query) run only on the read-only login, with
|
|
855
|
+
// ExecuteAdhocQuery's table check and timeout limit.
|
|
856
|
+
ActionEngineServer.Instance.SetReadOnlyProviderFactory(() => CreateReadOnlyProvider(dataSources));
|
|
857
|
+
ActionEngineServer.Instance.SetAdhocSQLAuthorizer(CreateAdhocSQLAuthorizer(configInfo.databaseSettings.requestTimeout));
|
|
858
|
+
|
|
832
859
|
setupComplete$.next(true);
|
|
833
860
|
raiseEvent('setupComplete', dataSources, null, this);
|
|
834
861
|
|
|
@@ -1076,7 +1103,7 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1076
1103
|
const profile = await new Promise<unknown>((res, rej) =>
|
|
1077
1104
|
profSession!.post('Profiler.stop', (e, r) => (e ? rej(e) : res((r as { profile: unknown }).profile)))
|
|
1078
1105
|
);
|
|
1079
|
-
const outPath =
|
|
1106
|
+
const outPath = join(mkdtempSync(join(tmpdir(), 'mjapi-schema-')), `mjapi_schema_build_${process.pid}.cpuprofile`);
|
|
1080
1107
|
writeFileSync(outPath, JSON.stringify(profile));
|
|
1081
1108
|
profSession.disconnect();
|
|
1082
1109
|
// eslint-disable-next-line no-console
|
|
@@ -1359,6 +1386,19 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1359
1386
|
startupLog.LogIf('verbose', `[Widget] Public routes registered at ${WIDGET_MOUNT_PATH}/session and ${WIDGET_MOUNT_PATH}/session/refresh`);
|
|
1360
1387
|
}
|
|
1361
1388
|
|
|
1389
|
+
// ─── Realtime identity verification: PUBLIC verify-link routes (before auth mw) ───
|
|
1390
|
+
// The person opening the emailed link holds no MJ token — the single-use token is the capability.
|
|
1391
|
+
// GET only renders a confirm button; the POST redeems (mail scanners GET every link). Mounted only
|
|
1392
|
+
// when verification is enabled AND able to send mail AND able to protect its codes (see
|
|
1393
|
+
// realtime.identityVerification config: communicationProvider + hmacSecret).
|
|
1394
|
+
if (configInfo.realtime?.identityVerification?.enabled && !RealtimeSessionVerificationService.Instance.IsEnabled) {
|
|
1395
|
+
console.warn('[RealtimeVerify] realtime.identityVerification.enabled is true but verification is unavailable: it needs a communicationProvider and an hmacSecret of at least 16 characters.');
|
|
1396
|
+
}
|
|
1397
|
+
if (RealtimeSessionVerificationService.Instance.IsEnabled) {
|
|
1398
|
+
app.use(REALTIME_VERIFY_MOUNT_PATH, cors<cors.CorsRequest>(), CreateRealtimeVerifyRouter());
|
|
1399
|
+
startupLog.LogIf('verbose', `[RealtimeVerify] Public routes registered at ${REALTIME_VERIFY_MOUNT_PATH}/:token (GET) and ${REALTIME_VERIFY_MOUNT_PATH} (POST)`);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1362
1402
|
// ─── Server extensions loader & shared service registry ───────────────────
|
|
1363
1403
|
const extensionLoader = new ServerExtensionLoader();
|
|
1364
1404
|
_currentExtensionLoader = extensionLoader;
|
|
@@ -1367,13 +1407,22 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1367
1407
|
// Backwards-compatibility shim: synthesize ServerExtensionConfig entries from legacy configInfo.telephony
|
|
1368
1408
|
const telephonyExtensionConfigs: ServerExtensionConfig[] = [];
|
|
1369
1409
|
if (configInfo.telephony?.enabled) {
|
|
1410
|
+
// Settings every carrier shares (inbound run-as user, call cap, outbound policy). Each carrier's own block is
|
|
1411
|
+
// spread AFTER them, so a carrier can override one explicitly.
|
|
1412
|
+
const sharedTelephonySettings: Record<string, unknown> = {
|
|
1413
|
+
inboundRunAsUserEmail: configInfo.telephony.inboundRunAsUserEmail,
|
|
1414
|
+
maxCallSeconds: configInfo.telephony.maxCallSeconds,
|
|
1415
|
+
maxConcurrentCalls: configInfo.telephony.maxConcurrentCalls,
|
|
1416
|
+
transferTargets: configInfo.telephony.transferTargets,
|
|
1417
|
+
outbound: configInfo.telephony.outbound,
|
|
1418
|
+
};
|
|
1370
1419
|
if (configInfo.telephony.twilio) {
|
|
1371
1420
|
telephonyExtensionConfigs.push({
|
|
1372
1421
|
Enabled: true,
|
|
1373
1422
|
DriverClass: 'TwilioTelephonyExtension',
|
|
1374
1423
|
RootPath: '/telephony/twilio',
|
|
1375
1424
|
Phase: 'pre-auth',
|
|
1376
|
-
Settings: configInfo.telephony.twilio
|
|
1425
|
+
Settings: { ...sharedTelephonySettings, ...configInfo.telephony.twilio },
|
|
1377
1426
|
});
|
|
1378
1427
|
}
|
|
1379
1428
|
if (configInfo.telephony.vonage) {
|
|
@@ -1382,7 +1431,7 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1382
1431
|
DriverClass: 'VonageTelephonyExtension',
|
|
1383
1432
|
RootPath: '/telephony/vonage',
|
|
1384
1433
|
Phase: 'pre-auth',
|
|
1385
|
-
Settings: configInfo.telephony.vonage
|
|
1434
|
+
Settings: { ...sharedTelephonySettings, ...configInfo.telephony.vonage },
|
|
1386
1435
|
});
|
|
1387
1436
|
}
|
|
1388
1437
|
if (configInfo.telephony.ringcentral) {
|
|
@@ -1391,7 +1440,16 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1391
1440
|
DriverClass: 'RingCentralTelephonyExtension',
|
|
1392
1441
|
RootPath: '/telephony/ringcentral',
|
|
1393
1442
|
Phase: 'pre-auth',
|
|
1394
|
-
Settings: configInfo.telephony.ringcentral
|
|
1443
|
+
Settings: { ...sharedTelephonySettings, ...configInfo.telephony.ringcentral },
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
if (configInfo.telephony.livekitSip) {
|
|
1447
|
+
telephonyExtensionConfigs.push({
|
|
1448
|
+
Enabled: true,
|
|
1449
|
+
DriverClass: 'LiveKitSipExtension',
|
|
1450
|
+
RootPath: '/telephony/livekit-sip',
|
|
1451
|
+
Phase: 'pre-auth',
|
|
1452
|
+
Settings: { ...sharedTelephonySettings, ...configInfo.telephony.livekitSip },
|
|
1395
1453
|
});
|
|
1396
1454
|
}
|
|
1397
1455
|
if (configInfo.telephony.teams?.enabled) {
|
|
@@ -1684,12 +1742,16 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1684
1742
|
});
|
|
1685
1743
|
}
|
|
1686
1744
|
|
|
1687
|
-
// Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then
|
|
1688
|
-
//
|
|
1689
|
-
//
|
|
1745
|
+
// Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then a periodic tick
|
|
1746
|
+
// that closes stale and over-deadline sessions and rebuilds lost realtime recordings from their
|
|
1747
|
+
// crash-recovery shards (storage reads and writes). Self-registers with ShutdownRegistry, so its timer
|
|
1748
|
+
// is cleared by the gracefulShutdown drain below (no explicit Stop() wiring needed here).
|
|
1690
1749
|
if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — boot-time session janitor uses the server's own provider
|
|
1691
1750
|
SessionJanitor.Instance.Start(Metadata.Provider, resumeUser) // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
|
|
1692
1751
|
.catch(err => console.warn(`[SessionJanitor] Startup failed: ${err}`));
|
|
1752
|
+
// The bridge engine's orphan reconcile likewise starts only now that this process holds its port, so it can't
|
|
1753
|
+
// take the live bridges of another MJAPI still serving that port for its own orphans (#5310).
|
|
1754
|
+
StartBridgeOrphanReconciliation(Metadata.Provider, resumeUser); // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
|
|
1693
1755
|
}
|
|
1694
1756
|
|
|
1695
1757
|
// Launch the durable task-graph dispatcher: claim Pending tasks, execute them with a fresh
|
|
@@ -1723,6 +1785,18 @@ const setupComplete$ = new ReplaySubject(1);
|
|
|
1723
1785
|
.catch(err => console.warn(`[TaskGraphDispatcher] Startup failed: ${err}`));
|
|
1724
1786
|
}
|
|
1725
1787
|
|
|
1788
|
+
// Start the durable work-queue host where enabled. It plans which subscriptions this instance runs,
|
|
1789
|
+
// re-plans on a timer, and self-registers with ShutdownRegistry, so gracefulShutdown's awaited
|
|
1790
|
+
// ShutdownAll() drains it (up to 2 × shutdownDrainMs) before the HTTP server closes.
|
|
1791
|
+
// Not awaited: a slow engine load must not delay readiness, and a failure never stops the API.
|
|
1792
|
+
const workQueueProvider = Metadata.Provider; // global-provider-ok: server startup — the work-queue host runs on the server's own provider
|
|
1793
|
+
if (configInfo.workQueue?.enabled && workQueueProvider instanceof DatabaseProviderBase) {
|
|
1794
|
+
const workQueuePool = dataSources[0]?.dataSource;
|
|
1795
|
+
const providerSource = new MJServerWorkQueueProviderSource(workQueuePool instanceof sql.ConnectionPool ? workQueuePool : null, workQueueProvider);
|
|
1796
|
+
StartWorkQueueHost(configInfo.workQueue, workQueueProvider, providerSource)
|
|
1797
|
+
.catch(error => console.error('❌ Failed to start the work queue host:', error));
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1726
1800
|
|
|
1727
1801
|
// Set up graceful shutdown handlers
|
|
1728
1802
|
const gracefulShutdown = async (signal: string) => {
|
|
@@ -2206,11 +2280,3 @@ function createMSSQLCompatPool(pgPool: import('pg').Pool): sql.ConnectionPool {
|
|
|
2206
2280
|
return wrapper as unknown as sql.ConnectionPool;
|
|
2207
2281
|
}
|
|
2208
2282
|
|
|
2209
|
-
/**
|
|
2210
|
-
* Translates SQL Server bracket-quoted identifiers to PostgreSQL double-quoted identifiers.
|
|
2211
|
-
* Converts [schema].[table] to "schema"."table" and handles common T-SQL patterns.
|
|
2212
|
-
*/
|
|
2213
|
-
function translateBracketsToPG(sql: string): string {
|
|
2214
|
-
// Replace [identifier] with "identifier"
|
|
2215
|
-
return sql.replace(/\[([^\]]+)\]/g, '"$1"');
|
|
2216
|
-
}
|