Merge branch 'master' of https://github.com/bigbluebutton/bigbluebutton into 2.2-panel-manager

This commit is contained in:
KDSBrowne 2018-11-20 16:30:39 +00:00
commit 79c168d2a4
7 changed files with 278 additions and 174 deletions

View File

@ -2,9 +2,24 @@ dist: trusty
language: node_js
env:
- JOB_TYPE=linter
- JOB_TYPE=acceptance_tests
node_js:
- "8"
jobs:
allow_failures:
- env: JOB_TYPE=linter
include:
- stage: "Linter"
name: "ESLint"
env: JOB_TYPE=linter
- stage: "Tests"
name: "Acceptance Tests"
env: JOB_TYPE=acceptance_tests
if: type = pull_request
env:
@ -12,7 +27,7 @@ env:
- BBB_SERVER_URL=http://localhost/bigbluebutton/api
script:
- travis_wait bash ./build_script.sh
- travis_wait bash ./build_script.sh $JOB_TYPE
after_script:
- docker stop $docker

View File

@ -1,16 +1,13 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { render } from 'react-dom';
import logger from '/imports/startup/client/logger';
import { joinRouteHandler, authenticatedRouteHandler } from '/imports/startup/client/auth';
import Base from '/imports/startup/client/base';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
import JoinHandler from '/imports/ui/components/join-handler/component';
import AuthenticatedHandler from '/imports/ui/components/authenticated-handler/component';
Meteor.startup(() => {
render(<LoadingScreen />, document.getElementById('app'));
// Logs all uncaught exceptions to the client logger
window.addEventListener('error', (e) => {
const { stack } = e.error;
@ -26,18 +23,12 @@ Meteor.startup(() => {
});
// TODO make this a Promise
joinRouteHandler((value, error) => {
if (error) {
logger.error(`User faced [${value}] on main.joinRouteHandler. Error was:`, JSON.stringify(error));
} else {
logger.info(`User successfully went through main.joinRouteHandler with [${value}].`);
}
authenticatedRouteHandler(() => {
// set defaults
Session.set('openPanel', '');
Session.set('idChatOpen', '');
Session.set('isMeetingEnded', false);
render(<Base />, document.getElementById('app'));
});
});
render(
<JoinHandler >
<AuthenticatedHandler>
<Base />
</AuthenticatedHandler>
</JoinHandler>,
document.getElementById('app'),
);
});

View File

@ -1,134 +0,0 @@
import Auth from '/imports/ui/services/auth';
import { setCustomLogoUrl } from '/imports/ui/components/user-list/service';
import { log, makeCall } from '/imports/ui/services/api';
import deviceInfo from '/imports/utils/deviceInfo';
import logger from '/imports/startup/client/logger';
import { Session } from 'meteor/session';
// disconnected and trying to open a new connection
const STATUS_CONNECTING = 'connecting';
const setError = (errorCode) => {
Session.set('hasError', true);
Session.set('codeError', errorCode);
};
export function joinRouteHandler(callback) {
const urlParams = new URLSearchParams(window.location.search);
const sessionToken = urlParams.get('sessionToken');
if (!sessionToken) {
setError('404');
callback('failed - no sessionToken', urlParams);
}
// Old credentials stored in memory were being used when joining a new meeting
Auth.clearCredentials();
// use enter api to get params for the client
const url = `/bigbluebutton/api/enter?sessionToken=${sessionToken}`;
fetch(url, { credentials: 'same-origin' })
.then(response => response.json())
.then(({ response }) => {
const {
returncode, meetingID, internalUserID, authToken, logoutUrl, customLogoURL,
externUserID, fullname, confname, customdata,
} = response;
if (returncode === 'FAILED') {
setError('404');
callback('failed during enter API call', response);
} else {
setCustomLogoUrl(customLogoURL);
if (customdata.length) {
makeCall('addUserSettings', meetingID, internalUserID, customdata);
}
Auth.set(
meetingID, internalUserID, authToken, logoutUrl,
sessionToken, fullname, externUserID, confname,
);
Session.set('isUserListOpen', deviceInfo.type().isPhone);
const userInfo = window.navigator;
// Browser information is sent once on startup
// Sent here instead of Meteor.startup, as the
// user might not be validated by then, thus user's data
// would not be sent with this information
const clientInfo = {
language: userInfo.language,
userAgent: userInfo.userAgent,
screenSize: { width: window.screen.width, height: window.screen.height },
windowSize: { width: window.innerWidth, height: window.innerHeight },
bbbVersion: Meteor.settings.public.app.bbbServerVersion,
location: window.location.href,
};
logger.info(clientInfo);
callback('all is good', null);
}
});
}
export function logoutRouteHandler() {
Auth.logout()
.then((logoutURL = window.location.origin) => {
const protocolPattern = /^((http|https):\/\/)/;
window.location.href =
protocolPattern.test(logoutURL) ?
logoutURL :
`http://${logoutURL}`;
});
}
/**
* Check if should revalidate the auth
* @param {Object} status
* @param {String} lastStatus
*/
export function shouldAuthenticate(status, lastStatus) {
return lastStatus != null && lastStatus === STATUS_CONNECTING && status.connected;
}
/**
* Check if the isn't the first connection try, preventing to authenticate on login.
* @param {Object} status
* @param {string} lastStatus
*/
export function updateStatus(status, lastStatus) {
return status.retryCount > 0 && lastStatus !== STATUS_CONNECTING ? status.status : lastStatus;
}
function _addReconnectObservable() {
let lastStatus = null;
Tracker.autorun(() => {
lastStatus = updateStatus(Meteor.status(), lastStatus);
if (shouldAuthenticate(Meteor.status(), lastStatus)) {
Auth.authenticate(true);
lastStatus = Meteor.status().status;
}
});
}
export function authenticatedRouteHandler(callback) {
if (Auth.loggedIn) {
callback();
}
_addReconnectObservable();
Auth.authenticate()
.then(callback)
.catch((reason) => {
log('error', reason);
setError(reason.error);
callback();
});
}

View File

@ -0,0 +1,89 @@
import React, { Component } from 'react';
import { Session } from 'meteor/session';
import { log } from '/imports/ui/services/api';
import Auth from '/imports/ui/services/auth';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
const STATUS_CONNECTING = 'connecting';
class AuthenticatedHandler extends Component {
static setError(codeError) {
Session.set('hasError', true);
if (codeError) Session.set('codeError', codeError);
}
static shouldAuthenticate(status, lastStatus) {
return lastStatus != null && lastStatus === STATUS_CONNECTING && status.connected;
}
static updateStatus(status, lastStatus) {
return status.retryCount > 0 && lastStatus !== STATUS_CONNECTING ? status.status : lastStatus;
}
static addReconnectObservable() {
let lastStatus = null;
Tracker.autorun(() => {
lastStatus = AuthenticatedHandler.updateStatus(Meteor.status(), lastStatus);
if (AuthenticatedHandler.shouldAuthenticate(Meteor.status(), lastStatus)) {
Auth.authenticate(true);
lastStatus = Meteor.status().status;
}
});
}
static authenticatedRouteHandler(callback) {
if (Auth.loggedIn) {
callback();
}
AuthenticatedHandler.addReconnectObservable();
Auth.authenticate()
.then(callback)
.catch((reason) => {
log('error', reason);
AuthenticatedHandler.setError(reason.error);
callback();
});
}
constructor(props) {
super(props);
this.changeState = this.changeState.bind(this);
this.state = {
authenticated: false,
};
}
componentDidMount() {
AuthenticatedHandler.authenticatedRouteHandler((value, error) => {
if (error) AuthenticatedHandler.setError(error);
this.changeState(true);
});
}
changeState(state) {
this.setState({ authenticated: state });
}
render() {
const {
children,
} = this.props;
const {
authenticated,
} = this.state;
Session.set('isChatOpen', false);
Session.set('idChatOpen', '');
Session.set('isMeetingEnded', false);
Session.set('isPollOpen', false);
Session.set('breakoutRoomIsOpen', false);
return authenticated
? children
: (<LoadingScreen />);
}
}
export default AuthenticatedHandler;

View File

@ -0,0 +1,128 @@
import React, { Component } from 'react';
import { Session } from 'meteor/session';
import Auth from '/imports/ui/services/auth';
import { setCustomLogoUrl } from '/imports/ui/components/user-list/service';
import { makeCall } from '/imports/ui/services/api';
import deviceInfo from '/imports/utils/deviceInfo';
import logger from '/imports/startup/client/logger';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
class JoinHandler extends Component {
static setError(codeError) {
Session.set('hasError', true);
if (codeError) Session.set('codeError', codeError);
}
constructor(props) {
super(props);
this.fetchToken = this.fetchToken.bind(this);
this.changeToJoin = this.changeToJoin.bind(this);
this.state = {
joined: false,
};
}
componentDidMount() {
this.fetchToken();
}
changeToJoin(bool) {
this.setState({ joined: bool });
}
fetchToken() {
const urlParams = new URLSearchParams(window.location.search);
const sessionToken = urlParams.get('sessionToken');
if (!sessionToken) {
JoinHandler.setError('404');
}
// Old credentials stored in memory were being used when joining a new meeting
Auth.clearCredentials();
const logUserInfo = () => {
const userInfo = window.navigator;
// Browser information is sent once on startup
// Sent here instead of Meteor.startup, as the
// user might not be validated by then, thus user's data
// would not be sent with this information
const clientInfo = {
language: userInfo.language,
userAgent: userInfo.userAgent,
screenSize: { width: window.screen.width, height: window.screen.height },
windowSize: { width: window.innerWidth, height: window.innerHeight },
bbbVersion: Meteor.settings.public.app.bbbServerVersion,
location: window.location.href,
};
logger.info(clientInfo);
};
const setAuth = (resp) => {
const {
meetingID, internalUserID, authToken, logoutUrl,
fullname, externUserID, confname,
} = resp;
Auth.set(
meetingID, internalUserID, authToken, logoutUrl,
sessionToken, fullname, externUserID, confname,
);
return resp;
};
const setLogoURL = (resp) => {
setCustomLogoUrl(resp.customLogoURL);
return resp;
};
const setCustomData = (resp) => {
const {
meetingID, internalUserID, customdata,
} = resp;
if (customdata.length) {
makeCall('addUserSettings', meetingID, internalUserID, customdata);
}
return resp;
};
// use enter api to get params for the client
const url = `/bigbluebutton/api/enter?sessionToken=${sessionToken}`;
const validAuth = new Promise((resolve, reject) => {
fetch(url, { credentials: 'same-origin' })
.then(response => response.json())
.then(({ response }) => response)
.then((resp) => {
if (resp.returncode !== 'FAILED') {
logger.info(`User successfully went through main.joinRouteHandler with [${resp}].`);
return resolve(resp);
}
const e = new Error('Session not found');
logger.error(`User faced [${e}] on main.joinRouteHandler. Error was:`, JSON.stringify(resp));
return reject(e);
});
});
validAuth
.then(setCustomData)
.then(setAuth)
.then(setLogoURL)
.then(logUserInfo)
.then(() => {
Session.set('openPanel', deviceInfo.type().isPhone ? 'userlist' : '');
Session.set('idChatOpen', '');
})
.finally(() => this.changeToJoin(true));
}
render() {
const { children } = this.props;
const { joined } = this.state;
return joined ?
children :
(<LoadingScreen />);
}
}
export default JoinHandler;

View File

@ -5,7 +5,6 @@ import { Meteor } from 'meteor/meteor';
import Auth from '/imports/ui/services/auth';
import Button from '/imports/ui/components/button/component';
import getFromUserSettings from '/imports/ui/services/users-settings';
import { logoutRouteHandler } from '/imports/startup/client/auth';
import Rating from './rating/component';
import { styles } from './styles';
@ -79,6 +78,18 @@ class MeetingEnded extends React.PureComponent {
return comment;
}
static logoutRouteHandler() {
Auth.logout()
.then((logoutURL = window.location.origin) => {
const protocolPattern = /^((http|https):\/\/)/;
window.location.href =
protocolPattern.test(logoutURL) ?
logoutURL :
`http://${logoutURL}`;
});
}
constructor(props) {
super(props);
this.state = {
@ -105,7 +116,7 @@ class MeetingEnded extends React.PureComponent {
} = this.state;
if (selected <= 0) {
logoutRouteHandler();
MeetingEnded.logoutRouteHandler();
return;
}
@ -127,7 +138,7 @@ class MeetingEnded extends React.PureComponent {
fetch(url, options)
.finally(() => {
logoutRouteHandler();
MeetingEnded.logoutRouteHandler();
});
}

View File

@ -3,27 +3,31 @@ set -ev
files=`git diff --name-only HEAD..$TRAVIS_BRANCH`
if [[ $files = *"bigbluebutton-html5"* ]]; then
{
cd bigbluebutton-html5
git clone --single-branch -b update-html5 https://github.com/bigbluebutton/docker.git
cp -r docker/{mod,restart.sh,setup.sh,supervisord.conf} .
cp -r docker/Dockerfile Dockerfile.test
docker build -t b2 -f Dockerfile.test .
docker=$(docker run -d -p 80:80/tcp -p 443:443/tcp -p 1935:1935 -p 5066:5066 -p 3478:3478 -p 3478:3478/udp b2 -h localhost)
echo $docker
cd bigbluebutton-html5
curl https://install.meteor.com/ | sh
meteor npm install
if [ $1 = linter ]
then
cd ..
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $files
elif [ $1 = acceptance_tests ]
then
{
git clone --single-branch -b update-html5 https://github.com/bigbluebutton/docker.git
cp -r docker/{mod,restart.sh,setup.sh,supervisord.conf} .
cp -r docker/Dockerfile Dockerfile.test
docker build -t b2 -f Dockerfile.test .
docker=$(docker run -d -p 80:80/tcp -p 443:443/tcp -p 1935:1935 -p 5066:5066 -p 3478:3478 -p 3478:3478/udp b2 -h localhost)
echo $docker
} > /dev/null
cd tests/puppeteer
npm install
conf=$(docker exec $(docker ps -q) bbb-conf --secret | grep "Secret:")
secret=$(echo $conf | cut -d' ' -f2)
export BBB_SHARED_SECRET=$secret
node html5-check.js
cd ../..
curl https://install.meteor.com/ | sh
meteor npm install
cd tests/puppeteer
npm install
cd ../../..
} > /dev/null
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $files
node html5-check.js
npm test
fi
fi