fix(core): 3.0 cluster setup (#20439)
* Add graphql API endpoint to BBB API This parameter will be required for the HTML5 client to discover the API endpoint of the GraphQL API so it can fetch its settings. * Fix: ensure API calls are headed towards the BBB Server In a cluster setup the URL that is visible in the browser is different from the URL of the BBB Server. The clients needs to contact the BBB server to retrieve information about the graphQL websocket and the graphql API. This patch adds the GraphQL API endpoint to the BBB API and changes the Client to use it. * Make GraphQL API endpoint configurable The default value should work for single node and cluster setups * Update docs to reflect changes required by cluster setup * fix eslint and typescript errors * Fix cluster setup docs - add missing parts for hasura - use consistent domain example.com * rename variable Co-authored-by: Gustavo Trott <gustavo@trott.com.br> * rename variable Co-authored-by: Gustavo Trott <gustavo@trott.com.br> * rename variable Co-authored-by: Gustavo Trott <gustavo@trott.com.br> * Update bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy Co-authored-by: Gustavo Trott <gustavo@trott.com.br> * Update bigbluebutton-web/grails-app/conf/bigbluebutton.properties Co-authored-by: Gustavo Trott <gustavo@trott.com.br> --------- Co-authored-by: Daniel Schreiber <daniel.schreiber@hrz.tu-chemnitz.de> Co-authored-by: Gustavo Trott <gustavo@trott.com.br>
This commit is contained in:
parent
52a548b93e
commit
64e43e4331
@ -77,6 +77,7 @@ public class ParamsProcessorUtil {
|
||||
private String defaultHTML5ClientUrl;
|
||||
|
||||
private String graphqlWebsocketUrl;
|
||||
private String graphqlApiUrl;
|
||||
private Boolean allowRequestsWithoutSession = false;
|
||||
private Integer defaultHttpSessionTimeout = 14400;
|
||||
private Boolean useDefaultAvatar = false;
|
||||
@ -886,6 +887,10 @@ public class ParamsProcessorUtil {
|
||||
return graphqlWebsocketUrl;
|
||||
}
|
||||
|
||||
public String getGraphqlApiUrl() {
|
||||
return graphqlApiUrl;
|
||||
}
|
||||
|
||||
public Boolean getUseDefaultLogo() {
|
||||
return useDefaultLogo;
|
||||
}
|
||||
@ -1249,6 +1254,10 @@ public class ParamsProcessorUtil {
|
||||
this.graphqlWebsocketUrl = graphqlWebsocketUrl.replace("https://","wss://");
|
||||
}
|
||||
|
||||
public void setGraphqlApiUrl(String graphqlApiUrl) {
|
||||
this.graphqlApiUrl = graphqlApiUrl;
|
||||
}
|
||||
|
||||
public void setUseDefaultLogo(Boolean value) {
|
||||
this.useDefaultLogo = value;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class ResponseBuilder {
|
||||
return new Date(timestamp).toString();
|
||||
}
|
||||
|
||||
public String buildMeetingVersion(String apiVersion, String bbbVersion, String graphqlWebsocketUrl, String returnCode) {
|
||||
public String buildMeetingVersion(String apiVersion, String bbbVersion, String graphqlWebsocketUrl, String graphqlApiUrl, String returnCode) {
|
||||
StringWriter xmlText = new StringWriter();
|
||||
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
@ -61,6 +61,7 @@ public class ResponseBuilder {
|
||||
data.put("apiVersion", apiVersion);
|
||||
data.put("bbbVersion", bbbVersion);
|
||||
data.put("graphqlWebsocketUrl", graphqlWebsocketUrl);
|
||||
data.put("graphqlApiUrl", graphqlApiUrl);
|
||||
|
||||
processData(getTemplate("api-version.ftlx"), data, xmlText);
|
||||
|
||||
|
@ -70,7 +70,12 @@ const ConnectionManager: React.FC<ConnectionManagerProps> = ({ children }): Reac
|
||||
const numberOfAttempts = useRef(20);
|
||||
const [errorCounts, setErrorCounts] = React.useState(0);
|
||||
useEffect(() => {
|
||||
fetch(`https://${window.location.hostname}/bigbluebutton/api`, {
|
||||
const pathMatch = window.location.pathname.match('^(.*)/html5client/join$');
|
||||
if (pathMatch == null) {
|
||||
throw new Error('Failed to match BBB client URI');
|
||||
}
|
||||
const serverPathPrefix = pathMatch[1];
|
||||
fetch(`https://${window.location.hostname}${serverPathPrefix}/bigbluebutton/api`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import Session from '/imports/ui/services/storage/in-memory';
|
||||
import { Session } from 'meteor/session';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { ErrorScreen } from '../../error-screen/component';
|
||||
import LoadingScreen from '../../common/loading-screen/component';
|
||||
@ -65,20 +65,34 @@ const StartupDataFetch: React.FC<StartupDataFetchProps> = ({
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const clientStartupSettings = `/api/rest/clientStartupSettings/?sessionToken=${sessionToken}`;
|
||||
const url = new URL(`${window.location.origin}${clientStartupSettings}`);
|
||||
fetch(url, { method: 'get' })
|
||||
.then((resp) => resp.json())
|
||||
.then((data: Response) => {
|
||||
const settings = data.meeting_clientSettings[0];
|
||||
sessionStorage.setItem('clientStartupSettings', JSON.stringify(settings || {}));
|
||||
setSettingsFetched(true);
|
||||
clearTimeout(timeoutRef.current);
|
||||
setLoading(false);
|
||||
}).catch(() => {
|
||||
Session.setItem('errorMessageDescription', 'meeting_ended');
|
||||
setError('Error fetching startup data');
|
||||
const pathMatch = window.location.pathname.match('^(.*)/html5client/join$');
|
||||
if (pathMatch == null) {
|
||||
throw new Error('Failed to match BBB client URI');
|
||||
}
|
||||
const serverPathPrefix = pathMatch[1];
|
||||
fetch(`https://${window.location.hostname}${serverPathPrefix}/bigbluebutton/api`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then((resp) => resp.json())
|
||||
.then((data) => {
|
||||
const url = `${data.response.graphqlApiUrl}/clientStartupSettings/?sessionToken=${sessionToken}`;
|
||||
fetch(url, { method: 'get', credentials: 'include' })
|
||||
.then((resp) => resp.json())
|
||||
.then((data: Response) => {
|
||||
const settings = data.meeting_clientSettings[0];
|
||||
sessionStorage.setItem('clientStartupSettings', JSON.stringify(settings || {}));
|
||||
setSettingsFetched(true);
|
||||
clearTimeout(timeoutRef.current);
|
||||
setLoading(false);
|
||||
}).catch(() => {
|
||||
Session.set('errorMessageDescription', 'meeting_ended');
|
||||
setError('Error fetching startup data');
|
||||
setLoading(false);
|
||||
});
|
||||
}).catch((error) => {
|
||||
setLoading(false);
|
||||
throw new Error('Error fetching GraphQL URL: '.concat(error.message || ''));
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
@ -27,25 +27,39 @@ const SettingsLoader: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const sessionToken = urlParams.get('sessionToken');
|
||||
const clientStartupSettings = `/api/rest/clientSettings/?sessionToken=${sessionToken}`;
|
||||
const url = new URL(`${window.location.origin}${clientStartupSettings}`);
|
||||
fetch(url, { method: 'get' })
|
||||
.then((resp) => resp.json())
|
||||
.then((data: Response) => {
|
||||
const settings = data?.meeting_clientSettings[0].clientSettingsJson;
|
||||
const pathMatch = window.location.pathname.match('^(.*)/html5client/join$');
|
||||
if (pathMatch == null) {
|
||||
throw new Error('Failed to match BBB client URI');
|
||||
}
|
||||
const serverPathPrefix = pathMatch[1];
|
||||
fetch(`https://${window.location.hostname}${serverPathPrefix}/bigbluebutton/api`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then((resp) => resp.json())
|
||||
.then((data) => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const sessionToken = urlParams.get('sessionToken');
|
||||
const clientStartupSettings = `/clientSettings/?sessionToken=${sessionToken}`;
|
||||
const url = new URL(`${data.response.graphqlApiUrl}${clientStartupSettings}`);
|
||||
fetch(url, { method: 'get', credentials: 'include' })
|
||||
.then((resp) => resp.json())
|
||||
.then((data: Response) => {
|
||||
const settings = data?.meeting_clientSettings[0].clientSettingsJson;
|
||||
|
||||
window.meetingClientSettings = JSON.parse(JSON.stringify(settings as unknown as MeetingClientSettings));
|
||||
const Meteor = { settings: {} };
|
||||
Meteor.settings = window.meetingClientSettings;
|
||||
setMeetingSettings(settings as unknown as MeetingClientSettings);
|
||||
setAllowToRender(true);
|
||||
}).catch(() => {
|
||||
window.meetingClientSettings = JSON.parse(JSON.stringify(settings as unknown as MeetingClientSettings));
|
||||
const Meteor = { settings: {} };
|
||||
Meteor.settings = window.meetingClientSettings;
|
||||
setMeetingSettings(settings as unknown as MeetingClientSettings);
|
||||
setAllowToRender(true);
|
||||
}).catch(() => {
|
||||
loadingContextInfo.setLoading(false, '');
|
||||
throw new Error('Error on requesting client settings data.');
|
||||
});
|
||||
}).catch((error) => {
|
||||
loadingContextInfo.setLoading(false, '');
|
||||
throw new Error('Error on requesting client settings data.');
|
||||
throw new Error('Error fetching GraphQL API URL: '.concat(error.message || ''));
|
||||
});
|
||||
// }
|
||||
}, []);
|
||||
return (
|
||||
(allowToRender)
|
||||
|
@ -308,6 +308,9 @@ defaultHTML5ClientUrl=${bigbluebutton.web.serverURL}/html5client/join
|
||||
# Using `serverURL` as default, so `https` will be automatically replaced by `wss`
|
||||
graphqlWebsocketUrl=${bigbluebutton.web.serverURL}/graphql
|
||||
|
||||
# Graphql API url (it's necessary to change for cluster setup)
|
||||
graphqlApiUrl=${bigbluebutton.web.serverURL}/api/rest
|
||||
|
||||
# This parameter defines the duration (in minutes) to wait before removing user sessions after a meeting has ended.
|
||||
# During this delay, users can still access information indicating that the "Meeting has ended".
|
||||
# Setting this value to 0 will result in the sessions being kept alive indefinitely (permanent availability).
|
||||
|
@ -142,6 +142,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="defaultNumDigitsForTelVoice" value="${defaultNumDigitsForTelVoice}"/>
|
||||
<property name="defaultHTML5ClientUrl" value="${defaultHTML5ClientUrl}"/>
|
||||
<property name="graphqlWebsocketUrl" value="${graphqlWebsocketUrl}"/>
|
||||
<property name="graphqlApiUrl" value="${graphqlApiUrl}"/>
|
||||
<property name="useDefaultLogo" value="${useDefaultLogo}"/>
|
||||
<property name="defaultLogoURL" value="${defaultLogoURL}"/>
|
||||
<property name="allowRequestsWithoutSession" value="${allowRequestsWithoutSession}"/>
|
||||
|
@ -97,6 +97,7 @@ class ApiController {
|
||||
apiVersion paramsProcessorUtil.getApiVersion()
|
||||
bbbVersion paramsProcessorUtil.getBbbVersion()
|
||||
graphqlWebsocketUrl paramsProcessorUtil.getGraphqlWebsocketUrl()
|
||||
graphqlApiUrl paramsProcessorUtil.getGraphqlApiUrl()
|
||||
}
|
||||
render(contentType: "application/json", text: builder.toPrettyString())
|
||||
}
|
||||
@ -108,6 +109,7 @@ class ApiController {
|
||||
paramsProcessorUtil.getApiVersion(),
|
||||
paramsProcessorUtil.getBbbVersion(),
|
||||
paramsProcessorUtil.getGraphqlWebsocketUrl(),
|
||||
paramsProcessorUtil.getGraphqlApiUrl(),
|
||||
RESP_CODE_SUCCESS),
|
||||
contentType: "text/xml")
|
||||
}
|
||||
|
@ -7,5 +7,6 @@
|
||||
<apiVersion>${apiVersion}</apiVersion>
|
||||
<bbbVersion>${bbbVersion}</bbbVersion>
|
||||
<graphqlWebsocketUrl>${graphqlWebsocketUrl}</graphqlWebsocketUrl>
|
||||
<graphqlApiUrl>${graphqlApiUrl}</graphqlApiUrl>
|
||||
</response>
|
||||
</#compress>
|
||||
|
@ -77,6 +77,12 @@ location /bbb-01/html5client/ {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /bbb-01/bigbluebutton/api {
|
||||
proxy_pass https://bbb-01.example.com/bigbluebutton/api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
```
|
||||
|
||||
Repeat this `location` directive for every BigBlueButton server.
|
||||
@ -123,9 +129,9 @@ public:
|
||||
url: 'https://bbb-01.example.com/pad'
|
||||
```
|
||||
|
||||
Create (or edit if it already exists) these unit file overrides:
|
||||
Create (or edit if it already exists) this unit override file:
|
||||
|
||||
* `/usr/lib/systemd/system/bbb-html5.service`
|
||||
* `/etc/systemd/system/bbb-html5.service.d/cluster.conf`
|
||||
|
||||
It should have the following content:
|
||||
|
||||
@ -165,7 +171,7 @@ Create the file `/etc/bigbluebutton/etherpad.json` with the following content:
|
||||
```json
|
||||
{
|
||||
"cluster_proxies": [
|
||||
"https://bbb-proxy.example.org"
|
||||
"https://bbb-proxy.example.com"
|
||||
]
|
||||
}
|
||||
```
|
||||
@ -173,7 +179,7 @@ Create the file `/etc/bigbluebutton/etherpad.json` with the following content:
|
||||
Adjust the CORS settings in `/etc/default/bbb-web`:
|
||||
|
||||
```shell
|
||||
JDK_JAVA_OPTIONS="-Dgrails.cors.enabled=true -Dgrails.cors.allowCredentials=true -Dgrails.cors.allowedOrigins=https://bbb-proxy.example.org,https://https://bbb-01.example.com"
|
||||
JDK_JAVA_OPTIONS="-Dgrails.cors.enabled=true -Dgrails.cors.allowCredentials=true -Dgrails.cors.allowedOrigins=https://bbb-proxy.example.com,https://https://bbb-01.example.com"
|
||||
```
|
||||
|
||||
Adjust the CORS setting in `/etc/default/bbb-graphql-middleware`:
|
||||
@ -182,16 +188,24 @@ Adjust the CORS setting in `/etc/default/bbb-graphql-middleware`:
|
||||
BBB_GRAPHQL_MIDDLEWARE_LISTEN_PORT=8378
|
||||
# If you are running a cluster proxy setup, you need to configure the Origin of
|
||||
# the frontend. See https://docs.bigbluebutton.org/administration/cluster-proxy
|
||||
BBB_GRAPHQL_MIDDLEWARE_ORIGIN=bbb-proxy.example.org
|
||||
BBB_GRAPHQL_MIDDLEWARE_ORIGIN=bbb-proxy.example.com
|
||||
```
|
||||
|
||||
Pay attention that this one is without protocol, just the hostname.
|
||||
|
||||
|
||||
Restart BigBlueButton:
|
||||
Adjust the CORS setting in `/etc/default/bbb-graphql-server`:
|
||||
|
||||
```shell
|
||||
$ bbb-conf --restart
|
||||
HASURA_GRAPHQL_CORS_DOMAIN="https://bbb-proxy.example.com"
|
||||
```
|
||||
|
||||
This one includes the protocol.
|
||||
|
||||
Reload systemd and restart BigBlueButton:
|
||||
|
||||
```shell
|
||||
# systemctl daemon-reload
|
||||
# bbb-conf --restart
|
||||
```
|
||||
|
||||
Now, opening a new session should show
|
||||
|
Loading…
Reference in New Issue
Block a user