2024-03-07 01:28:18 +08:00
|
|
|
import {
|
2024-04-03 00:31:43 +08:00
|
|
|
ApolloClient, ApolloProvider, InMemoryCache, NormalizedCacheObject, ApolloLink,
|
2024-03-07 01:28:18 +08:00
|
|
|
} from '@apollo/client';
|
|
|
|
import { WebSocketLink } from '@apollo/client/link/ws';
|
|
|
|
import { SubscriptionClient } from 'subscriptions-transport-ws';
|
|
|
|
import React, { useContext, useEffect } from 'react';
|
|
|
|
import { LoadingContext } from '/imports/ui/components/common/loading-screen/loading-screen-HOC/component';
|
2024-03-22 03:41:32 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
2024-04-24 03:51:08 +08:00
|
|
|
import apolloContextHolder from '../../core/graphql/apolloContextHolder/apolloContextHolder';
|
2024-03-07 01:28:18 +08:00
|
|
|
|
|
|
|
interface ConnectionManagerProps {
|
|
|
|
children: React.ReactNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Response {
|
|
|
|
response: {
|
|
|
|
returncode: string;
|
|
|
|
version: string;
|
|
|
|
apiVersion: string;
|
|
|
|
bbbVersion: string;
|
|
|
|
graphqlWebsocketUrl: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-03 00:31:43 +08:00
|
|
|
const DEFAULT_MAX_MUTATION_PAYLOAD_SIZE = 10485760; // 10MB
|
|
|
|
const getMaxMutationPayloadSize = () => window.meetingClientSettings?.public?.app?.maxMutationPayloadSize
|
|
|
|
?? DEFAULT_MAX_MUTATION_PAYLOAD_SIZE;
|
|
|
|
|
|
|
|
const estimatePayloadSize = (variables: Record<string, unknown>) => {
|
|
|
|
const variablesAsString = JSON.stringify(variables);
|
|
|
|
const variablesAsBlob = new Blob([variablesAsString]);
|
|
|
|
return variablesAsBlob.size;
|
|
|
|
};
|
|
|
|
|
|
|
|
const payloadSizeCheckLink = new ApolloLink((operation, forward) => {
|
|
|
|
if (operation.query.definitions.some((def) => 'operation' in def && def.operation === 'mutation')) {
|
|
|
|
const size = estimatePayloadSize(operation.variables);
|
|
|
|
const maxPayloadSize = getMaxMutationPayloadSize();
|
|
|
|
|
|
|
|
if (size > maxPayloadSize) {
|
|
|
|
const errorMsg = `Mutation payload is too large: ${size} bytes. ${maxPayloadSize} maximum allowed.`;
|
|
|
|
logger.warn(errorMsg);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 04:34:43 +08:00
|
|
|
// logger.debug(`Valid ${operation.operationName} payload. Following with the query.`);
|
2024-04-03 00:31:43 +08:00
|
|
|
return forward(operation);
|
|
|
|
});
|
|
|
|
|
2024-03-07 01:28:18 +08:00
|
|
|
const ConnectionManager: React.FC<ConnectionManagerProps> = ({ children }): React.ReactNode => {
|
|
|
|
const [graphqlUrlApolloClient, setApolloClient] = React.useState<ApolloClient<NormalizedCacheObject> | null>(null);
|
|
|
|
const [graphqlUrl, setGraphqlUrl] = React.useState<string>('');
|
|
|
|
const loadingContextInfo = useContext(LoadingContext);
|
|
|
|
useEffect(() => {
|
|
|
|
fetch(`https://${window.location.hostname}/bigbluebutton/api`, {
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
}).then(async (response) => {
|
|
|
|
const responseJson: Response = await response.json();
|
|
|
|
setGraphqlUrl(responseJson.response.graphqlWebsocketUrl);
|
|
|
|
}).catch((error) => {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Error fetching GraphQL URL: '.concat(error.message || ''));
|
|
|
|
});
|
2024-03-22 03:41:32 +08:00
|
|
|
logger.info('Fetching GraphQL URL');
|
|
|
|
loadingContextInfo.setLoading(true, '1/4');
|
2024-03-07 01:28:18 +08:00
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-03-22 03:41:32 +08:00
|
|
|
logger.info('Connecting to GraphQL server');
|
|
|
|
loadingContextInfo.setLoading(true, '2/4');
|
2024-03-07 01:28:18 +08:00
|
|
|
if (graphqlUrl) {
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
const sessionToken = urlParams.get('sessionToken');
|
|
|
|
if (!sessionToken) {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Missing session token');
|
|
|
|
}
|
|
|
|
sessionStorage.setItem('sessionToken', sessionToken);
|
|
|
|
|
|
|
|
let wsLink;
|
|
|
|
try {
|
|
|
|
const subscription = new SubscriptionClient(graphqlUrl, {
|
|
|
|
reconnect: true,
|
|
|
|
timeout: 30000,
|
2024-03-18 22:27:51 +08:00
|
|
|
minTimeout: 30000,
|
2024-03-07 01:28:18 +08:00
|
|
|
connectionParams: {
|
|
|
|
headers: {
|
|
|
|
'X-Session-Token': sessionToken,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
subscription.onError(() => {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Error: on subscription to server');
|
|
|
|
});
|
|
|
|
wsLink = new WebSocketLink(
|
|
|
|
subscription,
|
|
|
|
);
|
2024-04-03 00:31:43 +08:00
|
|
|
wsLink = ApolloLink.from([payloadSizeCheckLink, wsLink]);
|
2024-03-07 01:28:18 +08:00
|
|
|
wsLink.setOnError((error) => {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Error: on apollo connection'.concat(JSON.stringify(error) || ''));
|
|
|
|
});
|
2024-05-01 23:15:11 +08:00
|
|
|
apolloContextHolder.setLink(subscription);
|
2024-03-07 01:28:18 +08:00
|
|
|
} catch (error) {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Error creating WebSocketLink: '.concat(JSON.stringify(error) || ''));
|
|
|
|
}
|
|
|
|
let client;
|
|
|
|
try {
|
|
|
|
client = new ApolloClient({
|
|
|
|
link: wsLink,
|
|
|
|
cache: new InMemoryCache(),
|
2024-03-26 19:57:28 +08:00
|
|
|
connectToDevTools: true,
|
2024-03-07 01:28:18 +08:00
|
|
|
});
|
|
|
|
setApolloClient(client);
|
2024-04-24 03:51:08 +08:00
|
|
|
apolloContextHolder.setClient(client);
|
2024-03-07 01:28:18 +08:00
|
|
|
} catch (error) {
|
|
|
|
loadingContextInfo.setLoading(false, '');
|
|
|
|
throw new Error('Error creating Apollo Client: '.concat(JSON.stringify(error) || ''));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[graphqlUrl]);
|
|
|
|
return (
|
|
|
|
graphqlUrlApolloClient
|
|
|
|
? (
|
|
|
|
<ApolloProvider
|
|
|
|
client={graphqlUrlApolloClient}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</ApolloProvider>
|
|
|
|
) : null
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default ConnectionManager;
|