From d628131e640ba873204d560a420dda14018fca00 Mon Sep 17 00:00:00 2001 From: Joao Victor Date: Fri, 14 Oct 2022 10:27:23 -0300 Subject: [PATCH 1/2] improvement: error screen messages and logs --- .../imports/api/users/server/methods.js | 2 ++ .../api/users/server/methods/setExitReason.js | 21 ++++++++++++ .../api/users/server/methods/userLeaving.js | 15 ++++++++- .../server/modifiers/setUserExitReason.js | 25 ++++++++++++++ .../imports/startup/client/base.jsx | 33 +++++++++++++------ .../imports/ui/components/app/container.jsx | 11 ++++++- .../ui/components/error-screen/component.jsx | 18 ++++++++-- .../ui/components/meeting-ended/component.jsx | 5 ++- .../imports/ui/services/auth/index.js | 5 +++ bigbluebutton-html5/public/locales/en.json | 3 ++ 10 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 bigbluebutton-html5/imports/api/users/server/methods/setExitReason.js create mode 100644 bigbluebutton-html5/imports/api/users/server/modifiers/setUserExitReason.js diff --git a/bigbluebutton-html5/imports/api/users/server/methods.js b/bigbluebutton-html5/imports/api/users/server/methods.js index ef1604c994..7e6896113b 100644 --- a/bigbluebutton-html5/imports/api/users/server/methods.js +++ b/bigbluebutton-html5/imports/api/users/server/methods.js @@ -11,6 +11,7 @@ import userActivitySign from './methods/userActivitySign'; import userLeftMeeting from './methods/userLeftMeeting'; import changePin from './methods/changePin'; import setRandomUser from './methods/setRandomUser'; +import setExitReason from './methods/setExitReason'; Meteor.methods({ setEmojiStatus, @@ -25,4 +26,5 @@ Meteor.methods({ userLeftMeeting, changePin, setRandomUser, + setExitReason, }); diff --git a/bigbluebutton-html5/imports/api/users/server/methods/setExitReason.js b/bigbluebutton-html5/imports/api/users/server/methods/setExitReason.js new file mode 100644 index 0000000000..b43bbf0b30 --- /dev/null +++ b/bigbluebutton-html5/imports/api/users/server/methods/setExitReason.js @@ -0,0 +1,21 @@ +import { check } from 'meteor/check'; +import Logger from '/imports/startup/server/logger'; +import { extractCredentials } from '/imports/api/common/server/helpers'; +import setUserExitReason from '/imports/api/users/server/modifiers/setUserExitReason'; + +export default function setExitReason(reason) { + try { + const { meetingId, requesterUserId } = extractCredentials(this.userId); + + // Unauthenticated user, just ignore and go ahead. + if (!meetingId || !requesterUserId) return; + + check(meetingId, String); + check(requesterUserId, String); + check(reason, String); + + setUserExitReason(meetingId, requesterUserId, reason); + } catch (err) { + Logger.error(`Exception while invoking method setExitReason ${err.stack}`); + } +}; diff --git a/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js b/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js index c10163a2df..bcd6ba35c9 100755 --- a/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js +++ b/bigbluebutton-html5/imports/api/users/server/methods/userLeaving.js @@ -45,7 +45,20 @@ export default function userLeaving(meetingId, userId, connectionId) { ClientConnections.removeClientConnection(`${meetingId}--${userId}`, connectionId); - Logger.info(`User '${userId}' is leaving meeting '${meetingId}'`); + let reason; + + if (user.loggedOut) { + // User explicitly requested logout. + reason = 'logout'; + } else if (user.exitReason) { + // User didn't requested logout but exited graciously. + reason = user.exitReason; + } else { + // User didn't exit graciously (disconnection). + reason = 'disconnection'; + } + + Logger.info(`User '${userId}' is leaving meeting '${meetingId}' reason=${reason}`); RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, userId, payload); } catch (err) { Logger.error(`Exception while invoking method userLeaving ${err.stack}`); diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/setUserExitReason.js b/bigbluebutton-html5/imports/api/users/server/modifiers/setUserExitReason.js new file mode 100644 index 0000000000..c24f34002d --- /dev/null +++ b/bigbluebutton-html5/imports/api/users/server/modifiers/setUserExitReason.js @@ -0,0 +1,25 @@ +import Logger from '/imports/startup/server/logger'; +import Users from '/imports/api/users'; + +export default function setUserExitReason(meetingId, userId, reason) { + const selector = { + meetingId, + userId, + }; + + const modifier = { + $set: { + exitReason: reason, + }, + }; + + try { + const numberAffected = Users.update(selector, modifier); + + if (numberAffected) { + Logger.info(`Set exit reason userId=${userId} meeting=${meetingId}`); + } + } catch (err) { + Logger.error(`Setting user exit reason: ${err}`); + } +}; diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index a75e26c11c..9468cd8c23 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -26,6 +26,7 @@ import DebugWindow from '/imports/ui/components/debug-window/component'; import { ACTIONS, PANELS } from '../../ui/components/layout/enums'; import { isChatEnabled } from '/imports/ui/services/features'; import MediaService from '/imports/ui/components/media/service'; +import { makeCall } from '/imports/ui/services/api'; const CHAT_CONFIG = Meteor.settings.public.chat; const PUBLIC_CHAT_ID = CHAT_CONFIG.public_id; @@ -319,6 +320,10 @@ class Base extends Component { }); } + static async setExitReason(reason) { + return await makeCall('setExitReason', reason); + } + renderByState() { const { updateLoadingState } = this; const stateControls = { updateLoadingState }; @@ -332,7 +337,6 @@ class Base extends Component { meetingEndedReason, meetingIsBreakout, subscriptionsReady, - User, } = this.props; if ((loading || !subscriptionsReady) && !meetingHasEnded && meetingExist) { @@ -340,30 +344,39 @@ class Base extends Component { } if (ejected) { - return (); + return ( + Base.setExitReason('ejected')} + /> + ); } - if ((meetingHasEnded || User?.loggedOut) && meetingIsBreakout) { - window.close(); + if (meetingHasEnded && meetingIsBreakout) { + Base.setExitReason('breakoutEnded').finally(() => { + Meteor.disconnect(); + window.close(); + }); return null; } - if (((meetingHasEnded && !meetingIsBreakout)) || (codeError && User?.loggedOut)) { + if (meetingHasEnded && !meetingIsBreakout) { return ( Base.setExitReason('meetingEnded')} /> ); } if (codeError && !meetingHasEnded) { - // 680 is set for the codeError when the user requests a logout + // 680 is set for the codeError when the user requests a logout. if (codeError !== '680') { - return (); + return ( Base.setExitReason('error')} />); } - return (); + return ( Base.setExitReason('logout')} />); } return (); @@ -454,7 +467,7 @@ export default withTracker(() => { const connectionIdUpdateTime = User?.connectionIdUpdateTime; if (currentConnectionId && currentConnectionId !== connectionID && connectionIdUpdateTime > connectionAuthTime) { - Session.set('codeError', 403); + Session.set('codeError', '409'); Session.set('errorMessageDescription', 'joined_another_window_reason') } diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx index aef915fc3f..844d07e43f 100755 --- a/bigbluebutton-html5/imports/ui/components/app/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx @@ -150,7 +150,16 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) setTimeout(() => { const queryCurrentUser = Users.find({ userId: Auth.userID, meetingId: Auth.meetingID }); if (queryCurrentUser.count() === 0) { - endMeeting(403, userData.ejectedReason || null); + if (userData.ejected) { + endMeeting('403', userData.ejectedReason); + } else { + // Either authentication process hasn't finished yet or user did authenticate but Users + // collection is unsynchronized. In both cases user may be able to rejoin. + const description = Auth.isAuthenticating || Auth.loggedIn + ? 'able_to_rejoin_user_disconnected_reason' + : null; + endMeeting('503', description); + } } }, delayForReconnection); }, diff --git a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx index 4013fcbf58..e3714b96b0 100644 --- a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx @@ -8,6 +8,9 @@ import logger from '/imports/startup/client/logger'; import Styled from './styles'; const intlMessages = defineMessages({ + 503: { + id: 'app.error.503', + }, 500: { id: 'app.error.500', defaultMessage: 'Oops, something went wrong', @@ -15,6 +18,9 @@ const intlMessages = defineMessages({ 410: { id: 'app.error.410', }, + 409: { + id: 'app.error.409', + }, 408: { id: 'app.error.408', }, @@ -55,6 +61,9 @@ const intlMessages = defineMessages({ not_enough_permission_eject_reason: { id: 'app.meeting.logout.permissionEjectReason', }, + able_to_rejoin_user_disconnected_reason: { + id: 'app.error.disconnected.rejoin', + }, }); const propTypes = { @@ -66,14 +75,17 @@ const propTypes = { const defaultProps = { code: 500, + callback: async () => {}, }; class ErrorScreen extends PureComponent { componentDidMount() { - const { code } = this.props; + const { code, callback } = this.props; const log = code === 403 ? 'warn' : 'error'; AudioManager.exitAudio(); - Meteor.disconnect(); + callback().finally(() => { + Meteor.disconnect(); + }); logger[log]({ logCode: 'startup_client_usercouldnotlogin_error' }, `User could not log in HTML5, hit ${code}`); } @@ -92,7 +104,7 @@ class ErrorScreen extends PureComponent { let errorMessageDescription = Session.get('errorMessageDescription'); - if (code === 403 && errorMessageDescription in intlMessages) { + if (errorMessageDescription in intlMessages) { errorMessageDescription = intl.formatMessage(intlMessages[errorMessageDescription]); } diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx index 307e6a4944..0f7f94c94b 100755 --- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx @@ -116,6 +116,7 @@ const propTypes = { const defaultProps = { ejectedReason: null, endedReason: null, + callback: async () => {}, }; class MeetingEnded extends PureComponent { @@ -159,7 +160,9 @@ class MeetingEnded extends PureComponent { AudioManager.exitAudio(); Storage.removeItem('getEchoTest'); Storage.removeItem('isFirstJoin'); - Meteor.disconnect(); + this.props.callback().finally(() => { + Meteor.disconnect(); + }); } setSelectedStar(starNumber) { diff --git a/bigbluebutton-html5/imports/ui/services/auth/index.js b/bigbluebutton-html5/imports/ui/services/auth/index.js index 8eea3bfdf5..bfde0994ee 100755 --- a/bigbluebutton-html5/imports/ui/services/auth/index.js +++ b/bigbluebutton-html5/imports/ui/services/auth/index.js @@ -223,10 +223,15 @@ class Auth { } this.loggedIn = false; + this.isAuthenticating = true; + return this.validateAuthToken() .then(() => { this.loggedIn = true; this.uniqueClientSession = `${this.sessionToken}-${Math.random().toString(36).substring(6)}`; + }) + .finally(() => { + this.isAuthenticating = false; }); } diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json index 26ed05975d..715ec5f420 100755 --- a/bigbluebutton-html5/public/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -598,8 +598,11 @@ "app.error.403": "You have been removed from the meeting", "app.error.404": "Not found", "app.error.408": "Authentication failed", + "app.error.409": "Conflict", "app.error.410": "Meeting has ended", "app.error.500": "Ops, something went wrong", + "app.error.503": "You have been disconnected", + "app.error.disconnected.rejoin": "You are able to refresh the page to rejoin.", "app.error.userLoggedOut": "User has an invalid sessionToken due to log out", "app.error.ejectedUser": "User has an invalid sessionToken due to ejection", "app.error.joinedAnotherWindow": "This session seems to be opened in another browser window.", From 79587a393b87fd7387c649da2bb8a2e0da17af33 Mon Sep 17 00:00:00 2001 From: Joao Victor Date: Mon, 17 Oct 2022 13:35:23 -0300 Subject: [PATCH 2/2] Stringify error code --- .../imports/ui/components/error-screen/component.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx index e3714b96b0..9bebd9d118 100644 --- a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx @@ -74,14 +74,14 @@ const propTypes = { }; const defaultProps = { - code: 500, + code: '500', callback: async () => {}, }; class ErrorScreen extends PureComponent { componentDidMount() { const { code, callback } = this.props; - const log = code === 403 ? 'warn' : 'error'; + const log = code === '403' ? 'warn' : 'error'; AudioManager.exitAudio(); callback().finally(() => { Meteor.disconnect();