@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.5
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 +20 -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 +236 -27
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +682 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +48 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +112 -9
- 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/apiRoot.d.ts +32 -0
- package/dist/apiRoot.d.ts.map +1 -0
- package/dist/apiRoot.js +67 -0
- package/dist/apiRoot.js.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +25 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/LoginScreenEngine.d.ts +22 -0
- package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
- package/dist/auth/LoginScreenEngine.js +57 -0
- package/dist/auth/LoginScreenEngine.js.map +1 -0
- 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 +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +4 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
- package/dist/auth/magicLink/MagicLinkRouter.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 +2514 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +166 -4
- 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 +14 -11
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +29 -0
- package/dist/coreRoutes.d.ts.map +1 -0
- package/dist/coreRoutes.js +142 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +2456 -7
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +59 -19
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PushStatusResolver.js +1 -1
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +123 -48
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +293 -305
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +16 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts +33 -0
- package/dist/globalCors.d.ts.map +1 -0
- package/dist/globalCors.js +58 -0
- package/dist/globalCors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -278
- 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/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- 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 +139 -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 +99 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.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 +19 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +19 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +94 -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 +14 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +21 -5
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.js +9 -7
- package/dist/resolvers/ArtifactFileResolver.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/ConversationRunEventsResolver.d.ts +8 -4
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +24 -3
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +57 -8
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +3 -2
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +82 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +279 -101
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +117 -38
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +6 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.js +7 -1
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- 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/QueryResolver.d.ts +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +13 -0
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
- 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 +100 -14
- 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/SearchKnowledgeStreamResolver.d.ts +12 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
- package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
- package/dist/resolvers/SqlLoggingConfigResolver.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/TaskGraphFrameResolver.d.ts +2 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +65 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +102 -24
- package/dist/resolvers/TransactionGroupResolver.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/agent-run-data-guard.d.ts +31 -0
- package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
- package/dist/resolvers/agent-run-data-guard.js +72 -0
- package/dist/resolvers/agent-run-data-guard.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/meetingMutations.d.ts +6 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +21 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
- package/dist/rest/RealtimeRecordingHandler.js +383 -0
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts +3 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +21 -12
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +6 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +6 -0
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +12 -10
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +14 -17
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/services/MJStorageBlobStore.d.ts +8 -1
- package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
- package/dist/services/MJStorageBlobStore.js +25 -19
- package/dist/services/MJStorageBlobStore.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +2 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +5 -3
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -5
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +157 -9
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +279 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -96
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- 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__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
- package/src/__tests__/LoginScreenEngine.test.ts +86 -0
- package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
- package/src/__tests__/MeetingResolver.test.ts +75 -1
- package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
- 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__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
- 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__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +1203 -21
- package/src/__tests__/SessionManager.test.ts +240 -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 +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/globalCors.test.ts +217 -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__/multiTenancy.postgresRewrite.test.ts +181 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- 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__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +652 -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__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
- package/src/__tests__/sharedCache.test.ts +312 -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 +821 -59
- package/src/agentSessions/SessionManager.ts +138 -26
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +26 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +4 -8
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +171 -5
- package/src/context.ts +15 -10
- package/src/coreRoutes.ts +166 -0
- package/src/generated/graphql-schemas/__mj.ts +1815 -57
- package/src/generic/CacheInvalidationResolver.ts +73 -18
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/PushStatusResolver.ts +1 -1
- package/src/generic/ResolverBase.ts +335 -326
- package/src/generic/RunViewResolver.ts +21 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +231 -300
- package/src/isolatedProvider.ts +22 -0
- package/src/multiTenancy/index.ts +24 -2
- 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 +168 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +18 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
- package/src/realtimeSessions/sessionAccess.ts +145 -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 +24 -6
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
- package/src/resolvers/FileResolver.ts +321 -106
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/HumanHandoffResolver.ts +8 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/QueryResolver.ts +11 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
- package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
- 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 +116 -14
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +68 -13
- package/src/resolvers/TransactionGroupResolver.ts +131 -27
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -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/meetingMutations.ts +21 -0
- package/src/rest/RealtimeRecordingHandler.ts +464 -0
- package/src/rest/ViewOperationsHandler.ts +29 -13
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/services/MJStorageBlobStore.ts +31 -30
- package/src/services/StartTaskGraphDispatcher.ts +2 -2
- package/src/services/TaskGraphActionRunner.ts +25 -5
- package/src/services/TaskGraphAgentRunner.ts +188 -11
- package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
- package/src/sharedCache.ts +305 -0
- package/src/types.ts +7 -0
|
@@ -12,16 +12,15 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
};
|
|
13
13
|
import { EntityPermissionType, FieldValueCollection, LogError } from '@memberjunction/core';
|
|
14
14
|
import { NormalizeUUID } from '@memberjunction/global';
|
|
15
|
-
import { readRealtimeRecordingFile } from '@memberjunction/ai-agents';
|
|
16
15
|
import { Arg, Ctx, DeleteOptionsInput, Field, FieldResolver, Float, InputType, Int, Mutation, ObjectType, PubSub, PubSubEngine, Query, Resolver, Root, } from '@memberjunction/server';
|
|
17
|
-
import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, } from '@memberjunction/storage';
|
|
16
|
+
import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, StorageAccessEvaluator, StorageAccountAccessDeniedError, StorageObjectKeyOf, } from '@memberjunction/storage';
|
|
18
17
|
import { CreateMJFileInput, MJFileResolver as FileResolverBase, MJFile_, UpdateMJFileInput } from '../generated/generated.js';
|
|
19
18
|
import { FieldMapper } from '@memberjunction/graphql-dataprovider';
|
|
20
19
|
import { GetReadOnlyProvider } from '../util.js';
|
|
21
|
-
import { configInfo } from '../config.js';
|
|
22
20
|
import { MediaAccessKeyManager } from '../rest/MediaAccessKeys.js';
|
|
23
21
|
import { UploadTokenManager } from '../rest/UploadTokenManager.js';
|
|
24
22
|
import { DeriveSidecarPath, ParsePeaksSidecar } from './peaksSidecar.js';
|
|
23
|
+
import { ApiRoute } from '../apiRoot.js';
|
|
25
24
|
let CreateUploadURLInput = class CreateUploadURLInput {
|
|
26
25
|
};
|
|
27
26
|
__decorate([
|
|
@@ -576,16 +575,9 @@ MediaAccessTokenResult = __decorate([
|
|
|
576
575
|
ObjectType()
|
|
577
576
|
], MediaAccessTokenResult);
|
|
578
577
|
export { MediaAccessTokenResult };
|
|
578
|
+
/** The failure reported for an account `SearchAcrossAccounts` refuses — the same whether it is restricted or does not exist. */
|
|
579
|
+
const SEARCH_ACCOUNT_REFUSED_MESSAGE = 'Access denied: you do not have access to this storage account or it does not exist.';
|
|
579
580
|
let FileResolver = class FileResolver extends FileResolverBase {
|
|
580
|
-
/**
|
|
581
|
-
* Resolves the server's public base URL the same way callbacks/magic-link do:
|
|
582
|
-
* `publicUrl` if configured (e.g. an ngrok URL), else `baseUrl:graphqlPort`. Trailing
|
|
583
|
-
* slash stripped so callers can append `/media/...`.
|
|
584
|
-
*/
|
|
585
|
-
resolvePublicBaseUrl() {
|
|
586
|
-
const base = configInfo.publicUrl || `${configInfo.baseUrl}:${configInfo.graphqlPort}${configInfo.graphqlRootPath || ''}`;
|
|
587
|
-
return base.replace(/\/+$/, '');
|
|
588
|
-
}
|
|
589
581
|
/**
|
|
590
582
|
* Mints a short-lived signed media-access token for an `MJ: Files` record and returns the
|
|
591
583
|
* authenticated streaming URL (`<publicBase>/media/<fileId>?token=<token>`). Permission-gated
|
|
@@ -607,14 +599,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
607
599
|
if (!loaded) {
|
|
608
600
|
return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
|
|
609
601
|
}
|
|
602
|
+
// Storage gate: the token is the capability the /media route honours, so it is minted only when the caller may read
|
|
603
|
+
// the account the row's provider resolves to and every row tracking the object (throws — caught below).
|
|
604
|
+
const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
|
|
610
605
|
// Access authorized — mint the capability token.
|
|
611
606
|
const { Token, ExpiresAt } = MediaAccessKeyManager.Instance.Sign(fileId, contextUser.ID);
|
|
612
607
|
const fileNameSuffix = fileEntity.Name ? `/${encodeURIComponent(fileEntity.Name)}` : '';
|
|
613
|
-
const url =
|
|
608
|
+
const url = ApiRoute(`/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`, { absolute: true });
|
|
614
609
|
// Best-effort: surface precomputed waveform peaks from a peaks.json sidecar beside the file, so
|
|
615
610
|
// the player renders the real waveform instantly without fetching/decoding the audio. Never
|
|
616
611
|
// blocks token minting — any failure just omits Peaks.
|
|
617
|
-
const peaks = await this.tryReadPeaksSidecar(fileEntity,
|
|
612
|
+
const peaks = await this.tryReadPeaksSidecar(fileEntity, resolved?.Driver);
|
|
618
613
|
return { Success: true, Token, Url: url, ExpiresAt, MimeType: fileEntity.ContentType ?? undefined, Peaks: peaks };
|
|
619
614
|
}
|
|
620
615
|
catch (error) {
|
|
@@ -627,33 +622,22 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
627
622
|
* Best-effort read of a `peaks.json` waveform sidecar that sits in the SAME storage folder as the
|
|
628
623
|
* given file (a JSON array of normalized `0..1` numbers, written at capture time). Derives the
|
|
629
624
|
* folder from the file's `ProviderKey` (strips the final path segment), reads `<folder>/peaks.json`
|
|
630
|
-
*
|
|
631
|
-
* `undefined` on ANY failure (no ProviderKey, no
|
|
632
|
-
* peaks as a pure optimization and must never let a
|
|
625
|
+
* through the file's own storage driver — the one {@link assertFileObjectAccess} resolved, so the account is already
|
|
626
|
+
* gated — parses + validates it, and returns sanitized peaks. Returns `undefined` on ANY failure (no ProviderKey, no
|
|
627
|
+
* driver, no sidecar, parse error, garbage) — the caller treats peaks as a pure optimization and must never let a
|
|
628
|
+
* sidecar problem affect token minting.
|
|
633
629
|
*
|
|
634
630
|
* @param file The already-loaded (under the user's context) `MJ: Files` row.
|
|
635
|
-
* @param
|
|
631
|
+
* @param driver The gated driver for the file's account, or undefined when the provider has none.
|
|
636
632
|
* @returns Sanitized `0..1` peaks (length-capped), or `undefined`.
|
|
637
633
|
*/
|
|
638
|
-
async tryReadPeaksSidecar(file,
|
|
634
|
+
async tryReadPeaksSidecar(file, driver) {
|
|
639
635
|
try {
|
|
640
636
|
// The sidecar lives next to the recording: replace the final path segment with peaks.json.
|
|
641
637
|
const sidecarPath = DeriveSidecarPath(file.ProviderKey);
|
|
642
|
-
if (!sidecarPath) {
|
|
643
|
-
return undefined;
|
|
644
|
-
}
|
|
645
|
-
// Resolve the file's storage account → driver (mirror of readRealtimeRecordingFile's pattern).
|
|
646
|
-
await FileStorageEngine.Instance.Config(false, contextUser);
|
|
647
|
-
let accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
|
|
648
|
-
if (accounts.length === 0) {
|
|
649
|
-
await FileStorageEngine.Instance.Config(true, contextUser);
|
|
650
|
-
accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
|
|
651
|
-
}
|
|
652
|
-
const account = accounts[0];
|
|
653
|
-
if (!account) {
|
|
638
|
+
if (!sidecarPath || !driver) {
|
|
654
639
|
return undefined;
|
|
655
640
|
}
|
|
656
|
-
const driver = await FileStorageEngine.Instance.GetDriver(account.ID, contextUser);
|
|
657
641
|
const bytes = await driver.GetObject({ fullPath: sidecarPath });
|
|
658
642
|
if (!bytes || bytes.length === 0) {
|
|
659
643
|
return undefined;
|
|
@@ -689,6 +673,65 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
689
673
|
accountEntity,
|
|
690
674
|
};
|
|
691
675
|
}
|
|
676
|
+
/**
|
|
677
|
+
* The storage-account gate. Every route keyed by a client-supplied account ID runs it FIRST — before the account is
|
|
678
|
+
* loaded and before any driver call. Throws `StorageAccountAccessDeniedError` ("You do not have access to this storage
|
|
679
|
+
* account or it does not exist.") unless the caller may use the account for `access`, evaluated per call against
|
|
680
|
+
* `MJ: File Storage Account Permissions` by `StorageAccessEvaluator` (`@memberjunction/storage`). An unknown account
|
|
681
|
+
* gets the same refusal, so the error never reveals whether an account ID is real.
|
|
682
|
+
*/
|
|
683
|
+
async assertAccountAccess(accountId, access, context) {
|
|
684
|
+
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
685
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
686
|
+
await StorageAccessEvaluator.Instance.AssertAccountAccess(accountId, user, access, md);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* The tracked-file rule for an account-keyed route: when any of `objectNames` in the account backs an `MJ: Files` row,
|
|
690
|
+
* the caller must be able to read that row — the rule {@link GetFileContents} applies to a file ID — else this throws
|
|
691
|
+
* `TrackedFileAccessDeniedError` ("You do not have access to this file or it does not exist."). Keys are compared the
|
|
692
|
+
* way the account's driver addresses them (`FileStorageBase.NormalizeObjectKey`) and case-insensitively; a key whose
|
|
693
|
+
* meaning depends on the provider (`..`, `\`, `%2F`, ...) is refused outright. Applied to every object a route reads
|
|
694
|
+
* or signs, and to every object it overwrites, moves or deletes — the minimum for a write is that the caller may read
|
|
695
|
+
* what it replaces. An object no row tracks is governed by the account gate, which the caller has already passed.
|
|
696
|
+
*/
|
|
697
|
+
async assertTrackedObjectReadable(accountId, objectNames, user, md) {
|
|
698
|
+
const providerID = await this.accountProviderID(accountId, user, md);
|
|
699
|
+
const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
|
|
700
|
+
await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(providerID, objectNames, user, md, driver);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* The storage gate for a route that goes from an `MJ: Files` row (already loaded as the caller) to its object: the
|
|
704
|
+
* account the row's provider resolves to must allow `access`, and every row tracking the object must be readable by
|
|
705
|
+
* the caller (`FileStorageEngine.ResolveFileObject`) — so a row whose client-written `ProviderID`/`ProviderKey`/`Name`
|
|
706
|
+
* aliases someone else's object is refused. `extraKeys` (a rename target) get the tracked-file rule too. When the
|
|
707
|
+
* provider has no storage account the legacy env-configured driver serves the row; there is no account to gate, so the
|
|
708
|
+
* tracked-file rule alone applies. Returns the resolved object, or `null` in that legacy case.
|
|
709
|
+
*/
|
|
710
|
+
async assertFileObjectAccess(file, access, user, md, extraKeys = []) {
|
|
711
|
+
const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name };
|
|
712
|
+
const resolved = await FileStorageEngine.Instance.ResolveFileObject(location, user, access, md);
|
|
713
|
+
const keys = resolved ? extraKeys : [StorageObjectKeyOf(location), ...extraKeys];
|
|
714
|
+
if (keys.length > 0) {
|
|
715
|
+
await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(file.ProviderID, keys, user, md, resolved?.Driver);
|
|
716
|
+
}
|
|
717
|
+
return resolved;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* The storage provider ID of an account the caller has already passed the gate for, from the engine's cache (refreshed
|
|
721
|
+
* once on a miss). Refuses like the gate when the account still cannot be found.
|
|
722
|
+
*/
|
|
723
|
+
async accountProviderID(accountId, user, md) {
|
|
724
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
725
|
+
let account = FileStorageEngine.Instance.GetAccountById(accountId);
|
|
726
|
+
if (!account) {
|
|
727
|
+
await FileStorageEngine.Instance.RefreshAfterMiss(md);
|
|
728
|
+
account = FileStorageEngine.Instance.GetAccountById(accountId);
|
|
729
|
+
}
|
|
730
|
+
if (!account) {
|
|
731
|
+
throw new StorageAccountAccessDeniedError();
|
|
732
|
+
}
|
|
733
|
+
return account.ProviderID;
|
|
734
|
+
}
|
|
692
735
|
/**
|
|
693
736
|
* Loads a FileStorageAccount and its associated FileStorageProvider.
|
|
694
737
|
* This is the standard way to get provider information in the enterprise model.
|
|
@@ -729,6 +772,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
729
772
|
const fileEntity = await provider.GetEntityObject('MJ: Files', user);
|
|
730
773
|
const providerEntity = await provider.GetEntityObject('MJ: File Storage Providers', user);
|
|
731
774
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
775
|
+
// The upload URL signs an overwrite of `Name` (and the row will point at `ProviderKey`): Write on the account the
|
|
776
|
+
// provider resolves to, and the tracked-file rule on both keys, as for every other route that writes an object.
|
|
777
|
+
const location = { ProviderID: input.ProviderID ?? '', ProviderKey: null, Name: input.Name ?? '' };
|
|
778
|
+
await this.assertFileObjectAccess(location, 'Write', user, provider, input.ProviderKey ? [input.ProviderKey] : []);
|
|
732
779
|
const [sameName] = await this.findBy(provider, 'MJ: Files', { Name: input.Name, ProviderID: input.ProviderID }, context.userPayload.userRecord);
|
|
733
780
|
const NameExists = Boolean(sameName);
|
|
734
781
|
const success = fileEntity.NewRecord(FieldValueCollection.FromObject({ ...input, Status: 'Pending' }));
|
|
@@ -761,6 +808,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
761
808
|
// Permission check on MJ: Files
|
|
762
809
|
const fileEntity = await provider.GetEntityObject('MJ: Files', user);
|
|
763
810
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
811
|
+
// Write gate on the account the upload lands in — before the staged upload is consumed or any driver is touched.
|
|
812
|
+
// With no account at all there is nothing to gate; UploadFile then refuses with its documented error.
|
|
813
|
+
const targetAccountId = await this.resolveUploadAccountId(input.AccountID, user, provider);
|
|
814
|
+
if (targetAccountId) {
|
|
815
|
+
await this.assertAccountAccess(targetAccountId, 'Write', context);
|
|
816
|
+
}
|
|
764
817
|
let buffer;
|
|
765
818
|
let fileName = input.FileName || 'file';
|
|
766
819
|
let mimeType = input.MimeType || 'application/octet-stream';
|
|
@@ -785,17 +838,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
785
838
|
ErrorMessage: 'Either UploadToken or Base64Data must be provided.',
|
|
786
839
|
};
|
|
787
840
|
}
|
|
788
|
-
// Ensure FileStorageEngine is configured under the user context
|
|
789
|
-
await FileStorageEngine.Instance.Config(false, user, provider);
|
|
790
|
-
if (input.AccountID && !FileStorageEngine.Instance.GetAccountById(input.AccountID)) {
|
|
791
|
-
await FileStorageEngine.Instance.Config(true, user, provider);
|
|
792
|
-
}
|
|
793
841
|
const uploadResult = await FileStorageEngine.Instance.UploadFile({
|
|
794
842
|
content: buffer,
|
|
795
843
|
fileName,
|
|
796
844
|
mimeType,
|
|
797
845
|
contextUser: user,
|
|
798
|
-
storageAccountId:
|
|
846
|
+
storageAccountId: targetAccountId,
|
|
799
847
|
categoryID: input.CategoryID,
|
|
800
848
|
description: input.Description,
|
|
801
849
|
pathPrefix: input.PathPrefix,
|
|
@@ -819,32 +867,42 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
819
867
|
};
|
|
820
868
|
}
|
|
821
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* The account an upload will land in: the requested one, else the engine's default (the first active account) —
|
|
872
|
+
* returned as an explicit ID so the account the gate checks is the account `UploadFile` uses. A requested ID is
|
|
873
|
+
* returned as-is: the gate refuses an unknown one without revealing that it does not exist, and `UploadFile` refreshes
|
|
874
|
+
* the engine's cache itself when a permitted account is missing from it.
|
|
875
|
+
*/
|
|
876
|
+
async resolveUploadAccountId(accountId, user, md) {
|
|
877
|
+
if (accountId) {
|
|
878
|
+
return accountId;
|
|
879
|
+
}
|
|
880
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
881
|
+
return FileStorageEngine.Instance.ResolveStorageAccount()?.account.ID;
|
|
882
|
+
}
|
|
822
883
|
async DownloadUrl(file, context) {
|
|
823
884
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
824
885
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
825
886
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
826
887
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
}
|
|
833
|
-
if (accounts.length > 0) {
|
|
834
|
-
const account = accounts[0];
|
|
835
|
-
const driver = await FileStorageEngine.Instance.GetDriver(account.ID, user);
|
|
836
|
-
return await driver.CreatePreAuthDownloadUrl(file.ProviderKey ?? file.Name);
|
|
888
|
+
// Storage gate: Read on the account the row's provider resolves to, and the tracked-file rule on its object
|
|
889
|
+
const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name ?? '' };
|
|
890
|
+
const resolved = await this.assertFileObjectAccess(location, 'Read', user, md);
|
|
891
|
+
if (resolved) {
|
|
892
|
+
return await resolved.Driver.CreatePreAuthDownloadUrl(resolved.ObjectKey);
|
|
837
893
|
}
|
|
838
894
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
|
|
839
895
|
await providerEntity.Load(file.ProviderID);
|
|
840
896
|
const userContext = this.buildUserContext(context);
|
|
841
|
-
const url = await createDownloadUrl(providerEntity, file
|
|
897
|
+
const url = await createDownloadUrl(providerEntity, StorageObjectKeyOf(file), userContext);
|
|
842
898
|
return url;
|
|
843
899
|
}
|
|
844
900
|
/**
|
|
845
901
|
* Returns an `MJ: Files` record's bytes as base64, read server-side through authenticated MJStorage
|
|
846
902
|
* (`GetObject`) — NOT a public pre-signed link. Permission-gated: the file is first loaded under the
|
|
847
|
-
* calling user's context, so MJ row-level security determines access
|
|
903
|
+
* calling user's context, so MJ row-level security determines access; then the storage gate
|
|
904
|
+
* ({@link assertFileObjectAccess}) requires Read on the account the row's provider resolves to and refuses an object
|
|
905
|
+
* another row tracks when the caller cannot read that row. Never throws to the client.
|
|
848
906
|
*
|
|
849
907
|
* @param fileId The `MJ: Files` id whose bytes to return.
|
|
850
908
|
* @returns `{ Success, Base64?, MimeType?, ErrorMessage? }`.
|
|
@@ -860,15 +918,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
860
918
|
if (!loaded) {
|
|
861
919
|
return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
|
|
862
920
|
}
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
921
|
+
// Storage gate, then read the bytes via authenticated MJStorage (server-side GetObject on the file's own account).
|
|
922
|
+
// A refusal throws and is reported below; a row whose provider has no account has no bytes to read.
|
|
923
|
+
const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
|
|
924
|
+
const bytes = resolved ? await resolved.Driver.GetObject({ fullPath: resolved.ObjectKey }) : null;
|
|
925
|
+
if (!bytes || bytes.length === 0) {
|
|
866
926
|
return { Success: false, ErrorMessage: 'The file could not be read from storage.' };
|
|
867
927
|
}
|
|
868
928
|
return {
|
|
869
929
|
Success: true,
|
|
870
|
-
Base64:
|
|
871
|
-
MimeType:
|
|
930
|
+
Base64: bytes.toString('base64'),
|
|
931
|
+
MimeType: fileEntity.ContentType ?? undefined,
|
|
872
932
|
};
|
|
873
933
|
}
|
|
874
934
|
catch (error) {
|
|
@@ -885,6 +945,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
885
945
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
886
946
|
await fileEntity.Load(input.ID);
|
|
887
947
|
if (fileEntity.Name !== input.Name) {
|
|
948
|
+
// Renaming moves the object: Write on its account, and the tracked-file rule on the object and the new name
|
|
949
|
+
await this.assertFileObjectAccess(fileEntity, 'Write', user, md, [fileEntity.Name, input.Name]);
|
|
888
950
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
|
|
889
951
|
await providerEntity.Load(fileEntity.ProviderID);
|
|
890
952
|
const userContext = this.buildUserContext(context);
|
|
@@ -905,12 +967,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
905
967
|
return null;
|
|
906
968
|
}
|
|
907
969
|
fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
|
|
908
|
-
// Only delete the object from the provider if it's actually been uploaded
|
|
970
|
+
// Only delete the object from the provider if it's actually been uploaded — after the storage gate: Write on its
|
|
971
|
+
// account, and no other row the caller cannot read may track the object (a row can alias another row's object)
|
|
909
972
|
if (fileEntity.Status === 'Uploaded') {
|
|
973
|
+
await this.assertFileObjectAccess(fileEntity, 'Write', userInfo, md);
|
|
910
974
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', userInfo);
|
|
911
975
|
await providerEntity.Load(fileEntity.ProviderID);
|
|
912
976
|
const userContext = this.buildUserContext(context);
|
|
913
|
-
await deleteObject(providerEntity, fileEntity
|
|
977
|
+
await deleteObject(providerEntity, StorageObjectKeyOf(fileEntity), userContext);
|
|
914
978
|
}
|
|
915
979
|
return super.DeleteMJFile(ID, options, context, pubSub);
|
|
916
980
|
}
|
|
@@ -922,12 +986,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
922
986
|
});
|
|
923
987
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
924
988
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
989
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
990
|
+
await this.assertAccountAccess(input.AccountID, 'Read', context);
|
|
925
991
|
// Load the account and its provider
|
|
926
992
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
927
993
|
console.log('[FileResolver] Provider loaded:', {
|
|
928
994
|
Name: providerEntity.Name,
|
|
929
995
|
ServerDriverKey: providerEntity.ServerDriverKey,
|
|
930
|
-
HasConfiguration: !!providerEntity.
|
|
996
|
+
HasConfiguration: !!providerEntity.Configuration,
|
|
931
997
|
});
|
|
932
998
|
// Check permissions - user must have read access to Files entity
|
|
933
999
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
@@ -941,8 +1007,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
941
1007
|
objects: result.objects.map((o) => ({ name: o.name, isDirectory: o.isDirectory })),
|
|
942
1008
|
prefixes: result.prefixes,
|
|
943
1009
|
});
|
|
944
|
-
//
|
|
945
|
-
|
|
1010
|
+
// Drop objects behind an MJ: Files row the caller cannot read — a listing never names a tracked object the caller
|
|
1011
|
+
// is refused — then convert Date objects to ISO strings for GraphQL
|
|
1012
|
+
const visible = await this.dropUnreadableListedObjects(input.AccountID, result.objects, user, md);
|
|
1013
|
+
const objects = visible.map((obj) => ({
|
|
946
1014
|
...obj,
|
|
947
1015
|
lastModified: obj.lastModified.toISOString(),
|
|
948
1016
|
}));
|
|
@@ -957,6 +1025,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
957
1025
|
// Check permissions
|
|
958
1026
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
959
1027
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1028
|
+
// Account gate, then — when the object backs an MJ: Files row — that row must be readable by the caller
|
|
1029
|
+
await this.assertAccountAccess(input.AccountID, 'Read', context);
|
|
1030
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
960
1031
|
await FileStorageEngine.Instance.Config(false, user, md);
|
|
961
1032
|
const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
|
|
962
1033
|
const downloadUrl = await driver.CreatePreAuthDownloadUrl(input.ObjectName);
|
|
@@ -982,6 +1053,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
982
1053
|
// Check permissions
|
|
983
1054
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
984
1055
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1056
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1057
|
+
// An upload URL can overwrite: the caller must be able to read any MJ: Files row that tracks the target
|
|
1058
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
985
1059
|
await FileStorageEngine.Instance.Config(false, user, md);
|
|
986
1060
|
const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
|
|
987
1061
|
if (!driver.SupportsPreAuthUpload) {
|
|
@@ -1008,7 +1082,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1008
1082
|
return {
|
|
1009
1083
|
Success: true,
|
|
1010
1084
|
Token: mint.Token,
|
|
1011
|
-
Url: '/media/upload-stage',
|
|
1085
|
+
Url: ApiRoute('/media/upload-stage'),
|
|
1012
1086
|
};
|
|
1013
1087
|
}
|
|
1014
1088
|
catch (err) {
|
|
@@ -1022,11 +1096,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1022
1096
|
async DeleteStorageObject(input, context) {
|
|
1023
1097
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1024
1098
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1099
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1100
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1025
1101
|
// Load the account and its provider
|
|
1026
1102
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1027
1103
|
// Check permissions
|
|
1028
1104
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1029
1105
|
fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
|
|
1106
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
1030
1107
|
// Delete the object with extended user context (includes account for credential lookup)
|
|
1031
1108
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1032
1109
|
const success = await deleteObject(providerEntity, input.ObjectName, userContext);
|
|
@@ -1035,11 +1112,15 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1035
1112
|
async MoveStorageObject(input, context) {
|
|
1036
1113
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1037
1114
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1115
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1116
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1038
1117
|
// Load the account and its provider
|
|
1039
1118
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1040
1119
|
// Check permissions
|
|
1041
1120
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1042
1121
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
1122
|
+
// A move reads and removes the source and may overwrite the destination: both get the tracked-file rule
|
|
1123
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.OldName, input.NewName], user, md);
|
|
1043
1124
|
// Move the object with extended user context (includes account for credential lookup)
|
|
1044
1125
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1045
1126
|
const success = await moveObject(providerEntity, input.OldName, input.NewName, userContext);
|
|
@@ -1048,12 +1129,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1048
1129
|
async CopyStorageObject(input, context) {
|
|
1049
1130
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1050
1131
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1132
|
+
// Account gate first — a copy within one account writes to it — before the account is loaded or any driver is touched
|
|
1133
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1051
1134
|
// Load the account and its provider
|
|
1052
1135
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1053
1136
|
// Check permissions - copying requires both read (source) and create (destination)
|
|
1054
1137
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1055
1138
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1056
1139
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1140
|
+
// A copy of a tracked object is an untracked one the caller could then download, and the destination may be
|
|
1141
|
+
// overwritten: both get the tracked-file rule
|
|
1142
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.SourceName, input.DestinationName], user, md);
|
|
1057
1143
|
// Copy the object with extended user context (includes account for credential lookup)
|
|
1058
1144
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1059
1145
|
const success = await copyObject(providerEntity, input.SourceName, input.DestinationName, userContext);
|
|
@@ -1062,6 +1148,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1062
1148
|
async CreateDirectory(input, context) {
|
|
1063
1149
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1064
1150
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1151
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1152
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1065
1153
|
// Load the account and its provider
|
|
1066
1154
|
const { account: accountEntity, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1067
1155
|
// Check permissions
|
|
@@ -1086,6 +1174,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1086
1174
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1087
1175
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1088
1176
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1177
|
+
// Account gates first: read the source, write the destination — before either account is loaded or touched
|
|
1178
|
+
await this.assertAccountAccess(input.SourceAccountID, 'Read', context);
|
|
1179
|
+
await this.assertAccountAccess(input.DestinationAccountID, 'Write', context);
|
|
1180
|
+
// Tracked-file rule on both ends: the source (its copy would be untracked), the destination (it may be overwritten)
|
|
1181
|
+
await this.assertTrackedObjectReadable(input.SourceAccountID, [input.SourcePath], user, md);
|
|
1182
|
+
await this.assertTrackedObjectReadable(input.DestinationAccountID, [input.DestinationPath], user, md);
|
|
1089
1183
|
// Load the source account and its provider
|
|
1090
1184
|
const { account: sourceAccount, provider: sourceProviderEntity } = await this.loadAccountAndProvider(input.SourceAccountID, context);
|
|
1091
1185
|
// Load the destination account and its provider
|
|
@@ -1121,47 +1215,131 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1121
1215
|
// Check permissions - searching requires read access
|
|
1122
1216
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1123
1217
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1124
|
-
//
|
|
1125
|
-
await
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const foundIDs = new Set(accountEntities.map((a) => NormalizeUUID(a.ID)));
|
|
1135
|
-
const missingIDs = input.AccountIDs.filter((id) => !foundIDs.has(NormalizeUUID(id)));
|
|
1136
|
-
console.warn(`[FileResolver] Accounts not found: ${missingIDs.join(', ')}`);
|
|
1137
|
-
}
|
|
1138
|
-
// Load providers from cached metadata
|
|
1139
|
-
const providerMap = new Map();
|
|
1140
|
-
for (const provider of FileStorageEngine.Instance.Providers) {
|
|
1141
|
-
providerMap.set(provider.ID, provider);
|
|
1142
|
-
}
|
|
1143
|
-
// Build account/provider pairs for the search
|
|
1144
|
-
const accountInputs = [];
|
|
1145
|
-
for (const account of accountEntities) {
|
|
1146
|
-
const provider = providerMap.get(account.ProviderID);
|
|
1147
|
-
if (provider) {
|
|
1148
|
-
accountInputs.push({ accountEntity: account, providerEntity: provider });
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
// Execute the search across all accounts with account-based credentials
|
|
1152
|
-
const result = await searchAcrossAccounts(accountInputs, input.Query, {
|
|
1153
|
-
maxResultsPerAccount: input.MaxResultsPerAccount,
|
|
1154
|
-
fileTypes: input.FileTypes,
|
|
1155
|
-
searchContent: input.SearchContent,
|
|
1156
|
-
contextUser: user,
|
|
1157
|
-
});
|
|
1218
|
+
// Account gate BEFORE any driver call: refused accounts are reported as failed and never searched
|
|
1219
|
+
const { searchable, refused } = await this.partitionSearchAccounts(input.AccountIDs, user, md);
|
|
1220
|
+
const result = searchable.length > 0
|
|
1221
|
+
? await searchAcrossAccounts(searchable, input.Query, {
|
|
1222
|
+
maxResultsPerAccount: input.MaxResultsPerAccount,
|
|
1223
|
+
fileTypes: input.FileTypes,
|
|
1224
|
+
searchContent: input.SearchContent,
|
|
1225
|
+
contextUser: user,
|
|
1226
|
+
})
|
|
1227
|
+
: { accountResults: [], totalResultsReturned: 0, successfulAccounts: 0, failedAccounts: 0 };
|
|
1158
1228
|
console.log('[FileResolver] SearchAcrossAccounts result:', {
|
|
1159
1229
|
totalResultsReturned: result.totalResultsReturned,
|
|
1160
1230
|
successfulAccounts: result.successfulAccounts,
|
|
1161
1231
|
failedAccounts: result.failedAccounts,
|
|
1232
|
+
refusedAccounts: refused.length,
|
|
1162
1233
|
});
|
|
1163
|
-
|
|
1164
|
-
|
|
1234
|
+
const kept = await this.dropUnreadableTrackedHits(result.accountResults, user, md);
|
|
1235
|
+
return this.toSearchAcrossAccountsPayload(this.inRequestOrder(input.AccountIDs, [...kept, ...refused]));
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Per-account results in the order the accounts were requested (each requested ID once), whether searched or refused —
|
|
1239
|
+
* so a refused account sits where the caller put it rather than after every searched one.
|
|
1240
|
+
*/
|
|
1241
|
+
inRequestOrder(requestedIDs, results) {
|
|
1242
|
+
const byID = new Map(results.map((r) => [NormalizeUUID(r.accountID), r]));
|
|
1243
|
+
const ordered = [];
|
|
1244
|
+
for (const id of requestedIDs) {
|
|
1245
|
+
const result = byID.get(NormalizeUUID(id));
|
|
1246
|
+
if (result) {
|
|
1247
|
+
ordered.push(result);
|
|
1248
|
+
byID.delete(NormalizeUUID(id));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return [...ordered, ...byID.values()];
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Splits the requested accounts into those to search and those refused, before any driver call. An account the
|
|
1255
|
+
* caller may not read is refused with {@link SEARCH_ACCOUNT_REFUSED_MESSAGE} — and so is one that does not exist,
|
|
1256
|
+
* identically, so the response never reveals which IDs are real. Readable accounts are paired with their provider
|
|
1257
|
+
* from the engine's cache (refreshed once if a readable account is missing from it). Duplicate IDs are searched once.
|
|
1258
|
+
*/
|
|
1259
|
+
async partitionSearchAccounts(accountIDs, user, md) {
|
|
1260
|
+
const readable = await StorageAccessEvaluator.Instance.AccessibleAccountIDs(accountIDs, user, 'Read', md);
|
|
1261
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
1262
|
+
if ([...readable].some((id) => !FileStorageEngine.Instance.GetAccountWithProvider(id))) {
|
|
1263
|
+
await FileStorageEngine.Instance.RefreshAfterMiss(md);
|
|
1264
|
+
}
|
|
1265
|
+
const searchable = [];
|
|
1266
|
+
const refused = [];
|
|
1267
|
+
const seen = new Set();
|
|
1268
|
+
for (const requestedID of accountIDs) {
|
|
1269
|
+
const key = NormalizeUUID(requestedID);
|
|
1270
|
+
if (seen.has(key))
|
|
1271
|
+
continue;
|
|
1272
|
+
seen.add(key);
|
|
1273
|
+
const pair = readable.has(key) ? FileStorageEngine.Instance.GetAccountWithProvider(requestedID) : null;
|
|
1274
|
+
if (pair) {
|
|
1275
|
+
searchable.push({ accountEntity: pair.account, providerEntity: pair.provider });
|
|
1276
|
+
}
|
|
1277
|
+
else {
|
|
1278
|
+
refused.push(this.refusedAccountResult(requestedID));
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return { searchable, refused };
|
|
1282
|
+
}
|
|
1283
|
+
/** A failed per-account entry for an account the caller may not search; it names nothing beyond the requested ID. */
|
|
1284
|
+
refusedAccountResult(accountID) {
|
|
1285
|
+
return {
|
|
1286
|
+
accountID,
|
|
1287
|
+
accountName: '',
|
|
1288
|
+
providerID: '',
|
|
1289
|
+
providerName: '',
|
|
1290
|
+
success: false,
|
|
1291
|
+
errorMessage: SEARCH_ACCOUNT_REFUSED_MESSAGE,
|
|
1292
|
+
results: [],
|
|
1293
|
+
hasMore: false,
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Drops search hits that map to an `MJ: Files` row the caller may not read — one batched lookup per account, keyed on
|
|
1298
|
+
* each hit's path and provider object ID (`StorageAccessEvaluator.UnreadableTrackedObjectKeys`, compared the way the
|
|
1299
|
+
* account's driver addresses keys). Hits no row tracks stay; the account gate governs them.
|
|
1300
|
+
*
|
|
1301
|
+
* When any hit is dropped, the provider's `totalMatches` (which counted it) and `nextPageToken` (this route takes no
|
|
1302
|
+
* page token back) are withheld; `hasMore` is kept. How reliable the mapping is depends on the provider: for
|
|
1303
|
+
* object-key stores (S3, Azure Blob, GCS) a hit's path IS the row's `ProviderKey`; for hierarchical providers (Box,
|
|
1304
|
+
* Google Drive, Dropbox, SharePoint) a search path may not match the key a row was written with, and such a miss falls
|
|
1305
|
+
* back to the account gate. `MJ: Files` rows are keyed by provider, not account, so the lookup errs toward dropping.
|
|
1306
|
+
*/
|
|
1307
|
+
async dropUnreadableTrackedHits(accountResults, user, md) {
|
|
1308
|
+
return Promise.all(accountResults.map((ar) => this.dropUnreadableAccountHits(ar, user, md)));
|
|
1309
|
+
}
|
|
1310
|
+
/** {@link dropUnreadableTrackedHits} for one account's results; if the check cannot run, the account's hits are dropped. */
|
|
1311
|
+
async dropUnreadableAccountHits(ar, user, md) {
|
|
1312
|
+
if (!ar.success || ar.results.length === 0)
|
|
1313
|
+
return ar;
|
|
1314
|
+
try {
|
|
1315
|
+
const driver = await FileStorageEngine.Instance.GetDriver(ar.accountID, user);
|
|
1316
|
+
const keys = ar.results.flatMap((r) => [r.path, r.objectId].filter((k) => !!k));
|
|
1317
|
+
const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(ar.providerID, keys, user, md, driver);
|
|
1318
|
+
const results = ar.results.filter((r) => !unreadable.has(r.path) && !(r.objectId && unreadable.has(r.objectId)));
|
|
1319
|
+
return results.length === ar.results.length ? ar : { ...ar, results, totalMatches: undefined, nextPageToken: undefined };
|
|
1320
|
+
}
|
|
1321
|
+
catch (error) {
|
|
1322
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1323
|
+
LogError(`SearchAcrossAccounts: tracked-file check failed for account ${ar.accountID} — its hits are withheld: ${reason}`);
|
|
1324
|
+
return { ...ar, results: [], totalMatches: undefined, nextPageToken: undefined };
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* The listed objects minus those behind an `MJ: Files` row the caller may not read (directories are prefixes, never
|
|
1329
|
+
* tracked objects, and are kept). Keys are compared the way the account's driver addresses them.
|
|
1330
|
+
*/
|
|
1331
|
+
async dropUnreadableListedObjects(accountId, objects, user, md) {
|
|
1332
|
+
const files = objects.filter((o) => !o.isDirectory);
|
|
1333
|
+
if (files.length === 0)
|
|
1334
|
+
return objects;
|
|
1335
|
+
const providerID = await this.accountProviderID(accountId, user, md);
|
|
1336
|
+
const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
|
|
1337
|
+
const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(providerID, files.map((o) => o.fullPath), user, md, driver);
|
|
1338
|
+
return objects.filter((o) => o.isDirectory || !unreadable.has(o.fullPath));
|
|
1339
|
+
}
|
|
1340
|
+
/** Converts per-account results to the GraphQL payload, recomputing the totals from what is actually returned. */
|
|
1341
|
+
toSearchAcrossAccountsPayload(accountResults) {
|
|
1342
|
+
const payloadResults = accountResults.map((ar) => ({
|
|
1165
1343
|
accountID: ar.accountID,
|
|
1166
1344
|
accountName: ar.accountName,
|
|
1167
1345
|
success: ar.success,
|
|
@@ -1182,10 +1360,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1182
1360
|
nextPageToken: ar.nextPageToken,
|
|
1183
1361
|
}));
|
|
1184
1362
|
return {
|
|
1185
|
-
accountResults,
|
|
1186
|
-
totalResultsReturned:
|
|
1187
|
-
successfulAccounts:
|
|
1188
|
-
failedAccounts:
|
|
1363
|
+
accountResults: payloadResults,
|
|
1364
|
+
totalResultsReturned: accountResults.reduce((sum, ar) => sum + (ar.success ? ar.results.length : 0), 0),
|
|
1365
|
+
successfulAccounts: accountResults.filter((ar) => ar.success).length,
|
|
1366
|
+
failedAccounts: accountResults.filter((ar) => !ar.success).length,
|
|
1189
1367
|
};
|
|
1190
1368
|
}
|
|
1191
1369
|
};
|