diff --git a/app/api/server/v1/video-conference.js b/app/api/server/v1/video-conference.js index 51d0b8830499f..d261da4b8665f 100644 --- a/app/api/server/v1/video-conference.js +++ b/app/api/server/v1/video-conference.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Rooms } from '../../../models'; +import { Rooms } from '../../../models/server'; import { API } from '../api'; API.v1.addRoute('video-conference/jitsi.update-timeout', { authRequired: true }, { @@ -10,13 +10,13 @@ API.v1.addRoute('video-conference/jitsi.update-timeout', { authRequired: true }, return API.v1.failure('The "roomId" parameter is required!'); } - const room = Rooms.findOneById(roomId); + const room = Rooms.findOneById(roomId, { fields: { _id: 1 } }); if (!room) { return API.v1.failure('Room does not exist!'); } - Meteor.runAsUser(this.userId, () => Meteor.call('jitsi:updateTimeout', roomId)); + const jitsiTimeout = Meteor.runAsUser(this.userId, () => Meteor.call('jitsi:updateTimeout', roomId)); - return API.v1.success({ jitsiTimeout: Rooms.findOneById(roomId).jitsiTimeout }); + return API.v1.success({ jitsiTimeout }); }, }); diff --git a/app/videobridge/client/views/videoFlexTab.js b/app/videobridge/client/views/videoFlexTab.js index ebc32f1bace2d..803f4648a54cc 100644 --- a/app/videobridge/client/views/videoFlexTab.js +++ b/app/videobridge/client/views/videoFlexTab.js @@ -5,8 +5,8 @@ import { Template } from 'meteor/templating'; import { TimeSync } from 'meteor/mizzao:timesync'; import { settings } from '../../../settings'; -import { modal, TabBar } from '../../../ui-utils'; -import { t } from '../../../utils'; +import { modal, TabBar, call } from '../../../ui-utils/client'; +import { t } from '../../../utils/client'; import { Users, Rooms } from '../../../models'; import * as CONSTANTS from '../../constants'; @@ -57,22 +57,33 @@ Template.videoFlexTab.onRendered(function() { this.stop = stop; - const start = () => { - const update = () => { - const { jitsiTimeout } = Rooms.findOne({ _id: rid }, { fields: { jitsiTimeout: 1 } }); + const update = async () => { + const { jitsiTimeout } = Rooms.findOne({ _id: rid }, { fields: { jitsiTimeout: 1 } }); - if (jitsiTimeout && (TimeSync.serverTime() - new Date(jitsiTimeout) + CONSTANTS.TIMEOUT < CONSTANTS.DEBOUNCE)) { + if (jitsiTimeout && (TimeSync.serverTime() - new Date(jitsiTimeout) + CONSTANTS.TIMEOUT < CONSTANTS.DEBOUNCE)) { + return; + } + if (Meteor.status().connected) { + return call('jitsi:updateTimeout', rid); + } + closePanel(); + return this.stop(); + }; + + const start = async () => { + try { + const jitsiTimeout = await update(); + if (!jitsiTimeout) { return; } - if (Meteor.status().connected) { - return Meteor.call('jitsi:updateTimeout', rid); - } + this.intervalHandler = setInterval(update, CONSTANTS.HEARTBEAT); + TabBar.updateButton('video', { class: 'red' }); + return jitsiTimeout; + } catch (error) { + console.error(error); closePanel(); - return this.stop(); - }; - update(); - this.intervalHandler = setInterval(update, CONSTANTS.HEARTBEAT); - TabBar.updateButton('video', { class: 'red' }); + throw error; + } }; modal.open({ @@ -111,54 +122,47 @@ Template.videoFlexTab.onRendered(function() { return stop(); } - let accessToken = null; - if (isEnabledTokenAuth) { - accessToken = await new Promise((resolve, reject) => { - Meteor.call('jitsi:generateAccessToken', rid, (error, result) => { - if (error) { - return reject(error); - } - resolve(result); - }); - }); - } + const accessToken = isEnabledTokenAuth && await call('jitsi:generateAccessToken', rid); jitsiRoomActive = jitsiRoom; if (settings.get('Jitsi_Open_New_Window')) { - Tracker.nonreactive(() => start()); - let queryString = ''; - if (accessToken) { - queryString = `?jwt=${ accessToken }`; - } - - const newWindow = window.open(`${ (noSsl ? 'http://' : 'https://') + domain }/${ jitsiRoom }${ queryString }`, jitsiRoom); - if (newWindow) { - const closeInterval = setInterval(() => { - if (newWindow.closed === false) { - return; - } - closePanel(); - stop(); - clearInterval(closeInterval); - }, 300); - return newWindow.focus(); - } + Tracker.nonreactive(async () => { + await start(); + + const queryString = accessToken && `?jwt=${ accessToken }`; + + const newWindow = window.open(`${ (noSsl ? 'http://' : 'https://') + domain }/${ jitsiRoom }${ queryString }`, jitsiRoom); + if (newWindow) { + const closeInterval = setInterval(() => { + if (newWindow.closed === false) { + return; + } + closePanel(); + stop(); + clearInterval(closeInterval); + }, 300); + return newWindow.focus(); + } + }); } if (typeof JitsiMeetExternalAPI !== 'undefined') { // Keep it from showing duplicates when re-evaluated on variable change. const name = Users.findOne(Meteor.userId(), { fields: { name: 1 } }); if (!$('[id^=jitsiConference]').length) { - this.api = new JitsiMeetExternalAPI(domain, jitsiRoom, width, height, this.$('.video-container').get(0), configOverwrite, interfaceConfigOverwrite, noSsl, accessToken); - - /* - * Hack to send after frame is loaded. - * postMessage converts to events in the jitsi meet iframe. - * For some reason those aren't working right. - */ - Meteor.setTimeout(() => this.api.executeCommand('displayName', [name]), 5000); - return Tracker.nonreactive(() => start()); + Tracker.nonreactive(async () => { + await start(); + + this.api = new JitsiMeetExternalAPI(domain, jitsiRoom, width, height, this.$('.video-container').get(0), configOverwrite, interfaceConfigOverwrite, noSsl, accessToken); + + /* + * Hack to send after frame is loaded. + * postMessage converts to events in the jitsi meet iframe. + * For some reason those aren't working right. + */ + Meteor.setTimeout(() => this.api.executeCommand('displayName', [name]), 5000); + }); } // Execute any commands that might be reactive. Like name changing. diff --git a/app/videobridge/server/methods/jitsiSetTimeout.js b/app/videobridge/server/methods/jitsiSetTimeout.js index af0351a6d4b20..2a18ee53811ff 100644 --- a/app/videobridge/server/methods/jitsiSetTimeout.js +++ b/app/videobridge/server/methods/jitsiSetTimeout.js @@ -1,10 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { Rooms, Messages } from '../../../models'; -import { callbacks } from '../../../callbacks'; +import { Rooms, Messages, Users } from '../../../models/server'; +import { callbacks } from '../../../callbacks/server'; +import { metrics } from '../../../metrics/server'; import * as CONSTANTS from '../../constants'; -import { canAccessRoom } from '../../../authorization/server'; +import { canSendMessage } from '../../../authorization/server'; +import { SystemLogger } from '../../../logger/server'; Meteor.methods({ 'jitsi:updateTimeout': (rid) => { @@ -12,34 +14,51 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'jitsi:updateTimeout' }); } - const room = Rooms.findOneById(rid); + const uid = Meteor.userId(); - if (!canAccessRoom(room, Meteor.user())) { - throw new Meteor.Error('error-not-allowerd', 'not allowed', { method: 'jitsi:updateTimeout' }); - } + const user = Users.findOneById(uid, { + fields: { + username: 1, + type: 1, + }, + }); - const currentTime = new Date().getTime(); + try { + const room = canSendMessage(rid, { uid, username: user.username, type: user.type }); - const jitsiTimeout = room.jitsiTimeout && new Date(room.jitsiTimeout).getTime(); + const currentTime = new Date().getTime(); - if (!jitsiTimeout || currentTime > jitsiTimeout - CONSTANTS.TIMEOUT / 2) { - Rooms.setJitsiTimeout(rid, new Date(currentTime + CONSTANTS.TIMEOUT)); - } + const jitsiTimeout = room.jitsiTimeout && new Date(room.jitsiTimeout).getTime(); + + const nextTimeOut = new Date(currentTime + CONSTANTS.TIMEOUT); + + if (!jitsiTimeout || currentTime > jitsiTimeout - CONSTANTS.TIMEOUT / 2) { + Rooms.setJitsiTimeout(rid, nextTimeOut); + } + + if (!jitsiTimeout || currentTime > jitsiTimeout) { + metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736 + + const message = Messages.createWithTypeRoomIdMessageAndUser('jitsi_call_started', rid, '', Meteor.user(), { + actionLinks: [ + { icon: 'icon-videocam', label: TAPi18n.__('Click_to_join'), method_id: 'joinJitsiCall', params: '' }, + ], + }); + message.msg = TAPi18n.__('Started_a_video_call'); + message.mentions = [ + { + _id: 'here', + username: 'here', + }, + ]; + callbacks.run('afterSaveMessage', message, { ...room, jitsiTimeout: currentTime + CONSTANTS.TIMEOUT }); + } + + return jitsiTimeout || nextTimeOut; + } catch (error) { + SystemLogger.error('Error starting video call:', error); - if (!jitsiTimeout || currentTime > jitsiTimeout) { - const message = Messages.createWithTypeRoomIdMessageAndUser('jitsi_call_started', rid, '', Meteor.user(), { - actionLinks: [ - { icon: 'icon-videocam', label: TAPi18n.__('Click_to_join'), method_id: 'joinJitsiCall', params: '' }, - ], - }); - message.msg = TAPi18n.__('Started_a_video_call'); - message.mentions = [ - { - _id: 'here', - username: 'here', - }, - ]; - callbacks.run('afterSaveMessage', message, { ...room, jitsiTimeout: currentTime + CONSTANTS.TIMEOUT }); + throw new Meteor.Error('error-starting-video-call', error.message); } }, });