Merge pull request #19104 from gustavotrott/graphql-user-join-action
graphql: New User Flow for Joining Meetings via GraphQL
This commit is contained in:
commit
6dc3dbb1fe
@ -2,15 +2,14 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
|
import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg
|
||||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
||||||
import org.bigbluebutton.core.db.{ UserStateDAO }
|
import org.bigbluebutton.core.db.{ UserDAO, UserStateDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models.{ RegisteredUser, RegisteredUsers, Users2x, VoiceUsers }
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running._
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders._
|
||||||
|
|
||||||
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||||
this: MeetingActor =>
|
this: MeetingActor =>
|
||||||
|
|
||||||
val liveMeeting: LiveMeeting
|
val liveMeeting: LiveMeeting
|
||||||
val outGW: OutMsgRouter
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
@ -18,41 +17,116 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
log.info("Received user joined meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
log.info("Received user joined meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
||||||
|
|
||||||
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
||||||
case Some(reconnectingUser) =>
|
case Some(user) => handleUserReconnecting(user, msg, state)
|
||||||
if (reconnectingUser.userLeftFlag.left) {
|
case None => handleUserJoining(msg, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserJoining(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
|
||||||
|
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
|
||||||
|
log.info(s"Number of registered users [${RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers)}]")
|
||||||
|
|
||||||
|
regUser.fold {
|
||||||
|
handleFailedUserJoin(msg, "Invalid auth token.", EjectReasonCode.VALIDATE_TOKEN)
|
||||||
|
state
|
||||||
|
} { user =>
|
||||||
|
val validationResult = for {
|
||||||
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
|
_ <- checkIfUserIsBanned(user)
|
||||||
|
_ <- checkIfUserLoggedOut(user)
|
||||||
|
_ <- validateMaxParticipants(user)
|
||||||
|
} yield user
|
||||||
|
|
||||||
|
validationResult.fold(
|
||||||
|
reason => handleFailedUserJoin(msg, reason._1, reason._2),
|
||||||
|
validUser => handleSuccessfulUserJoin(msg, validUser)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleSuccessfulUserJoin(msg: UserJoinMeetingReqMsg, regUser: RegisteredUser) = {
|
||||||
|
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
|
||||||
|
updateParentMeetingWithNewListOfUsers()
|
||||||
|
notifyPreviousUsersWithSameExtId(regUser)
|
||||||
|
clearCachedVoiceUser(regUser)
|
||||||
|
clearExpiredUserState(regUser)
|
||||||
|
invalidateUserGraphqlConnection(regUser)
|
||||||
|
|
||||||
|
newState
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleFailedUserJoin(msg: UserJoinMeetingReqMsg, failReason: String, failReasonCode: String) = {
|
||||||
|
log.info("Ignoring user {} attempt to join in meeting {}. Reason Code: {}, Reason Message: {}", msg.body.userId, msg.header.meetingId, failReasonCode, failReason)
|
||||||
|
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
private def handleUserReconnecting(user: UserState, msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
|
if (user.userLeftFlag.left) {
|
||||||
|
resetUserLeftFlag(msg)
|
||||||
|
}
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
private def resetUserLeftFlag(msg: UserJoinMeetingReqMsg) = {
|
||||||
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
log.info("Resetting flag that user left meeting. user {}", msg.body.userId)
|
||||||
// User has reconnected. Just reset it's flag. ralam Oct 23, 2018
|
|
||||||
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
||||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
private def validateMaxParticipants(regUser: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
case None =>
|
val userHasJoinedAlready = RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
||||||
// Check if maxParticipants has been reached
|
val maxParticipants = liveMeeting.props.usersProp.maxUsers - 1
|
||||||
// User are able to reenter if he already joined previously with the same extId
|
|
||||||
val userHasJoinedAlready = RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers) match {
|
if (maxParticipants > 0 && //0 = no limit
|
||||||
case Some(regUser: RegisteredUser) => RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= maxParticipants &&
|
||||||
case None => false
|
!userHasJoinedAlready) {
|
||||||
|
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
|
||||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
|
||||||
userHasJoinedAlready == false
|
|
||||||
|
|
||||||
if (!hasReachedMaxParticipants) {
|
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
|
if (user.guestStatus != GuestStatus.ALLOW) {
|
||||||
|
Left(("User is not allowed to join", EjectReasonCode.PERMISSION_FAILED))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserIsBanned(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.banned) {
|
||||||
|
Left(("Banned user rejoining", EjectReasonCode.BANNED_USER_REJOINING))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.loggedOut) {
|
||||||
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def updateParentMeetingWithNewListOfUsers() = {
|
||||||
if (liveMeeting.props.meetingProp.isBreakout) {
|
if (liveMeeting.props.meetingProp.isBreakout) {
|
||||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Warn previous users that someone connected with same Id
|
private def notifyPreviousUsersWithSameExtId(regUser: RegisteredUser) = {
|
||||||
for {
|
|
||||||
regUser <- RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
|
||||||
liveMeeting.registeredUsers)
|
|
||||||
} yield {
|
|
||||||
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
||||||
.filter(u => u.id != regUser.id)
|
.filter(_.id != regUser.id)
|
||||||
.foreach { previousUser =>
|
.foreach { previousUser =>
|
||||||
|
sendUserConnectedNotification(previousUser, regUser, liveMeeting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendUserConnectedNotification(previousUser: RegisteredUser, newUser: RegisteredUser, liveMeeting: LiveMeeting) = {
|
||||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||||
previousUser.id,
|
previousUser.id,
|
||||||
liveMeeting.props.meetingProp.intId,
|
liveMeeting.props.meetingProp.intId,
|
||||||
@ -60,22 +134,19 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
"promote",
|
"promote",
|
||||||
"app.mobileAppModal.userConnectedWithSameId",
|
"app.mobileAppModal.userConnectedWithSameId",
|
||||||
"Notification to warn that user connect again from other browser/device",
|
"Notification to warn that user connect again from other browser/device",
|
||||||
Vector(regUser.name)
|
Vector(newUser.name)
|
||||||
)
|
)
|
||||||
outGW.send(notifyUserEvent)
|
outGW.send(notifyUserEvent)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private def clearCachedVoiceUser(regUser: RegisteredUser) =
|
||||||
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
||||||
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, msg.body.userId)
|
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
|
||||||
UserStateDAO.updateExpired(msg.body.userId, false)
|
|
||||||
|
|
||||||
newState
|
private def clearExpiredUserState(regUser: RegisteredUser) =
|
||||||
} else {
|
UserStateDAO.updateExpired(regUser.id, false)
|
||||||
log.info("Ignoring user {} attempt to join, once the meeting {} has reached max participants: {}", msg.body.userId, msg.header.meetingId, liveMeeting.props.usersProp.maxUsers)
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private def invalidateUserGraphqlConnection(regUser: RegisteredUser) =
|
||||||
|
Sender.sendInvalidateUserGraphqlConnectionSysMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.sessionToken, "user_joined", outGW)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.bus.InternalEventBus
|
import org.bigbluebutton.core.bus.InternalEventBus
|
||||||
|
import org.bigbluebutton.core.db.UserDAO
|
||||||
import org.bigbluebutton.core.domain.MeetingState2x
|
import org.bigbluebutton.core.domain.MeetingState2x
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||||
@ -15,103 +16,79 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
|||||||
val eventBus: InternalEventBus
|
val eventBus: InternalEventBus
|
||||||
|
|
||||||
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = {
|
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||||
log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
|
log.debug(s"Received ValidateAuthTokenReqMsg msg $msg")
|
||||||
|
|
||||||
var failReason = "Invalid auth token."
|
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
|
||||||
var failReasonCode = EjectReasonCode.VALIDATE_TOKEN
|
log.info(s"Number of registered users [${RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers)}]")
|
||||||
|
|
||||||
log.info("Number of registered users [{}]", RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers))
|
regUser.fold {
|
||||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
sendFailedValidateAuthTokenRespMsg(msg, "Invalid auth token.", EjectReasonCode.VALIDATE_TOKEN)
|
||||||
liveMeeting.registeredUsers)
|
} { user =>
|
||||||
regUser match {
|
val validationResult = for {
|
||||||
case Some(u) =>
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
// Check if maxParticipants has been reached
|
_ <- checkIfUserIsBanned(user)
|
||||||
// User are able to reenter if he already joined previously with the same extId
|
_ <- checkIfUserLoggedOut(user)
|
||||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
_ <- validateMaxParticipants(user)
|
||||||
|
} yield user
|
||||||
|
|
||||||
|
validationResult.fold(
|
||||||
|
reason => sendFailedValidateAuthTokenRespMsg(msg, reason._1, reason._2),
|
||||||
|
validUser => sendSuccessfulValidateAuthTokenRespMsg(validUser)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
private def validateMaxParticipants(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (liveMeeting.props.usersProp.maxUsers > 0 &&
|
||||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
||||||
RegisteredUsers.checkUserExtIdHasJoined(u.externId, liveMeeting.registeredUsers) == false
|
RegisteredUsers.checkUserExtIdHasJoined(user.externId, liveMeeting.registeredUsers) == false) {
|
||||||
|
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||||
// Check if banned user is rejoining.
|
|
||||||
// Fail validation if ejected user is rejoining.
|
|
||||||
// ralam april 21, 2020
|
|
||||||
if (u.guestStatus == GuestStatus.ALLOW && !u.banned && !u.loggedOut && !hasReachedMaxParticipants) {
|
|
||||||
userValidated(u, state)
|
|
||||||
} else {
|
} else {
|
||||||
if (u.banned) {
|
Right(())
|
||||||
failReason = "Banned user rejoining"
|
|
||||||
failReasonCode = EjectReasonCode.BANNED_USER_REJOINING
|
|
||||||
} else if (u.loggedOut) {
|
|
||||||
failReason = "User had logged out"
|
|
||||||
failReasonCode = EjectReasonCode.USER_LOGGED_OUT
|
|
||||||
} else if (hasReachedMaxParticipants) {
|
|
||||||
failReason = "The maximum number of participants allowed for this meeting has been reached."
|
|
||||||
failReasonCode = EjectReasonCode.MAX_PARTICIPANTS
|
|
||||||
}
|
|
||||||
validateTokenFailed(
|
|
||||||
outGW,
|
|
||||||
meetingId = liveMeeting.props.meetingProp.intId,
|
|
||||||
userId = msg.header.userId,
|
|
||||||
authToken = msg.body.authToken,
|
|
||||||
valid = false,
|
|
||||||
waitForApproval = false,
|
|
||||||
failReason,
|
|
||||||
failReasonCode,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
validateTokenFailed(
|
|
||||||
outGW,
|
|
||||||
meetingId = liveMeeting.props.meetingProp.intId,
|
|
||||||
userId = msg.header.userId,
|
|
||||||
authToken = msg.body.authToken,
|
|
||||||
valid = false,
|
|
||||||
waitForApproval = false,
|
|
||||||
failReason,
|
|
||||||
failReasonCode,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def validateTokenFailed(
|
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
outGW: OutMsgRouter,
|
if (user.guestStatus != GuestStatus.ALLOW) {
|
||||||
meetingId: String,
|
Left(("User is not allowed to join", EjectReasonCode.PERMISSION_FAILED))
|
||||||
userId: String,
|
} else {
|
||||||
authToken: String,
|
Right(())
|
||||||
valid: Boolean,
|
}
|
||||||
waitForApproval: Boolean,
|
|
||||||
reason: String,
|
|
||||||
reasonCode: String,
|
|
||||||
state: MeetingState2x
|
|
||||||
): MeetingState2x = {
|
|
||||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval, 0,
|
|
||||||
0, reasonCode, reason)
|
|
||||||
outGW.send(event)
|
|
||||||
|
|
||||||
// send a system message to force disconnection
|
|
||||||
// Comment out as meteor will disconnect the client. Requested by Tiago (ralam apr 28, 2020)
|
|
||||||
//Sender.sendDisconnectClientSysMsg(meetingId, userId, SystemUser.ID, reasonCode, outGW)
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String,
|
private def checkIfUserIsBanned(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
valid: Boolean, waitForApproval: Boolean, registeredOn: Long, authTokenValidatedOn: Long,
|
if (user.banned) {
|
||||||
reasonCode: String = EjectReasonCode.NOT_EJECT, reason: String = "User not ejected"): Unit = {
|
Left(("Banned user rejoining", EjectReasonCode.BANNED_USER_REJOINING))
|
||||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval, registeredOn,
|
} else {
|
||||||
authTokenValidatedOn, reasonCode, reason)
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.loggedOut) {
|
||||||
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendFailedValidateAuthTokenRespMsg(msg: ValidateAuthTokenReqMsg, failReason: String, failReasonCode: String) = {
|
||||||
|
UserDAO.updateJoinError(msg.body.userId, failReasonCode, failReason)
|
||||||
|
|
||||||
|
val event = MsgBuilder.buildValidateAuthTokenRespMsg(liveMeeting.props.meetingProp.intId, msg.header.userId, msg.body.authToken, false, false, 0,
|
||||||
|
0, failReasonCode, failReason)
|
||||||
outGW.send(event)
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
def userValidated(user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
|
def sendSuccessfulValidateAuthTokenRespMsg(user: RegisteredUser) = {
|
||||||
val meetingId = liveMeeting.props.meetingProp.intId
|
val meetingId = liveMeeting.props.meetingProp.intId
|
||||||
val updatedUser = RegisteredUsers.updateUserLastAuthTokenValidated(liveMeeting.registeredUsers, user)
|
val updatedUser = RegisteredUsers.updateUserLastAuthTokenValidated(liveMeeting.registeredUsers, user)
|
||||||
|
|
||||||
sendValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, valid = true, waitForApproval = false, updatedUser.registeredOn, updatedUser.lastAuthTokenValidatedOn)
|
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, true, false, updatedUser.registeredOn,
|
||||||
state
|
updatedUser.lastAuthTokenValidatedOn, EjectReasonCode.NOT_EJECT, "User not ejected")
|
||||||
|
outGW.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendAllUsersInMeeting(requesterId: String): Unit = {
|
def sendAllUsersInMeeting(requesterId: String): Unit = {
|
||||||
|
@ -77,12 +77,12 @@ object MeetingDAO {
|
|||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => {
|
case Success(rowsAffected) => {
|
||||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in Meeting table!")
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in Meeting table!")
|
||||||
|
ChatDAO.insert(meetingProps.meetingProp.intId, GroupChatApp.createDefaultPublicGroupChat())
|
||||||
MeetingUsersPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp)
|
MeetingUsersPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.usersProp)
|
||||||
MeetingLockSettingsDAO.insert(meetingProps.meetingProp.intId, meetingProps.lockSettingsProps)
|
MeetingLockSettingsDAO.insert(meetingProps.meetingProp.intId, meetingProps.lockSettingsProps)
|
||||||
MeetingMetadataDAO.insert(meetingProps.meetingProp.intId, meetingProps.metadataProp)
|
MeetingMetadataDAO.insert(meetingProps.meetingProp.intId, meetingProps.metadataProp)
|
||||||
MeetingRecordingPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.recordProp)
|
MeetingRecordingPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.recordProp)
|
||||||
MeetingVoiceDAO.insert(meetingProps.meetingProp.intId, meetingProps.voiceProp)
|
MeetingVoiceDAO.insert(meetingProps.meetingProp.intId, meetingProps.voiceProp)
|
||||||
ChatDAO.insert(meetingProps.meetingProp.intId, GroupChatApp.createDefaultPublicGroupChat())
|
|
||||||
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
||||||
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
||||||
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
|
MeetingBreakoutDAO.insert(meetingProps.meetingProp.intId, meetingProps.breakoutProps)
|
||||||
|
@ -19,7 +19,7 @@ class UserConnectionStatusDbTableDef(tag: Tag) extends Table[UserConnectionStatu
|
|||||||
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
val connectionAliveAt = column[Option[java.sql.Timestamp]]("connectionAliveAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserConnectionStatusdDAO {
|
object UserConnectionStatusDAO {
|
||||||
|
|
||||||
def insert(meetingId: String, userId: String) = {
|
def insert(meetingId: String, userId: String) = {
|
||||||
DatabaseConnection.db.run(
|
DatabaseConnection.db.run(
|
||||||
|
@ -16,6 +16,8 @@ case class UserDbModel(
|
|||||||
sessionToken: String = "",
|
sessionToken: String = "",
|
||||||
authed: Boolean = false,
|
authed: Boolean = false,
|
||||||
joined: Boolean = false,
|
joined: Boolean = false,
|
||||||
|
joinErrorMessage: Option[String],
|
||||||
|
joinErrorCode: Option[String],
|
||||||
banned: Boolean = false,
|
banned: Boolean = false,
|
||||||
loggedOut: Boolean = false,
|
loggedOut: Boolean = false,
|
||||||
guest: Boolean,
|
guest: Boolean,
|
||||||
@ -28,7 +30,7 @@ case class UserDbModel(
|
|||||||
|
|
||||||
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
||||||
override def * = (
|
override def * = (
|
||||||
userId,extId,meetingId,name,role,avatar,color, sessionToken, authed,joined,banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard) <> (UserDbModel.tupled, UserDbModel.unapply)
|
userId,extId,meetingId,name,role,avatar,color, sessionToken, authed,joined,joinErrorCode, joinErrorMessage, banned,loggedOut,guest,guestStatus,registeredOn,excludeFromDashboard) <> (UserDbModel.tupled, UserDbModel.unapply)
|
||||||
val userId = column[String]("userId", O.PrimaryKey)
|
val userId = column[String]("userId", O.PrimaryKey)
|
||||||
val extId = column[String]("extId")
|
val extId = column[String]("extId")
|
||||||
val meetingId = column[String]("meetingId")
|
val meetingId = column[String]("meetingId")
|
||||||
@ -39,6 +41,8 @@ class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
|||||||
val sessionToken = column[String]("sessionToken")
|
val sessionToken = column[String]("sessionToken")
|
||||||
val authed = column[Boolean]("authed")
|
val authed = column[Boolean]("authed")
|
||||||
val joined = column[Boolean]("joined")
|
val joined = column[Boolean]("joined")
|
||||||
|
val joinErrorCode = column[Option[String]]("joinErrorCode")
|
||||||
|
val joinErrorMessage = column[Option[String]]("joinErrorMessage")
|
||||||
val banned = column[Boolean]("banned")
|
val banned = column[Boolean]("banned")
|
||||||
val loggedOut = column[Boolean]("loggedOut")
|
val loggedOut = column[Boolean]("loggedOut")
|
||||||
val guest = column[Boolean]("guest")
|
val guest = column[Boolean]("guest")
|
||||||
@ -62,6 +66,8 @@ object UserDAO {
|
|||||||
sessionToken = regUser.sessionToken,
|
sessionToken = regUser.sessionToken,
|
||||||
authed = regUser.authed,
|
authed = regUser.authed,
|
||||||
joined = regUser.joined,
|
joined = regUser.joined,
|
||||||
|
joinErrorCode = None,
|
||||||
|
joinErrorMessage = None,
|
||||||
banned = regUser.banned,
|
banned = regUser.banned,
|
||||||
loggedOut = regUser.loggedOut,
|
loggedOut = regUser.loggedOut,
|
||||||
guest = regUser.guest,
|
guest = regUser.guest,
|
||||||
@ -73,10 +79,10 @@ object UserDAO {
|
|||||||
).onComplete {
|
).onComplete {
|
||||||
case Success(rowsAffected) => {
|
case Success(rowsAffected) => {
|
||||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
||||||
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
UserConnectionStatusDAO.insert(meetingId, regUser.id)
|
||||||
UserConnectionStatusdDAO.insert(meetingId, regUser.id)
|
|
||||||
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
||||||
UserClientSettingsDAO.insert(regUser.id, meetingId)
|
UserClientSettingsDAO.insert(regUser.id, meetingId)
|
||||||
|
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
||||||
}
|
}
|
||||||
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user: $e")
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting user: $e")
|
||||||
}
|
}
|
||||||
@ -94,6 +100,17 @@ object UserDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = {
|
||||||
|
DatabaseConnection.db.run(
|
||||||
|
TableQuery[UserDbTableDef]
|
||||||
|
.filter(_.userId === userId)
|
||||||
|
.map(u => (u.joined, u.joinErrorCode, u.joinErrorMessage))
|
||||||
|
.update((false, Some(joinErrorCode), Some(joinErrorMessage)))
|
||||||
|
).onComplete {
|
||||||
|
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user (Joined) table!")
|
||||||
|
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user (Joined): $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def delete(intId: String) = {
|
def delete(intId: String) = {
|
||||||
|
@ -30,6 +30,7 @@ function App() {
|
|||||||
const [sessionToken, setSessionToken] = useState(null);
|
const [sessionToken, setSessionToken] = useState(null);
|
||||||
const [userId, setUserId] = useState(null);
|
const [userId, setUserId] = useState(null);
|
||||||
const [userName, setUserName] = useState(null);
|
const [userName, setUserName] = useState(null);
|
||||||
|
const [userAuthToken, setUserAuthToken] = useState(null);
|
||||||
const [graphqlClient, setGraphqlClient] = useState(null);
|
const [graphqlClient, setGraphqlClient] = useState(null);
|
||||||
const [enterApiResponse, setEnterApiResponse] = useState('');
|
const [enterApiResponse, setEnterApiResponse] = useState('');
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ function App() {
|
|||||||
if(json?.response?.internalUserID) {
|
if(json?.response?.internalUserID) {
|
||||||
setUserId(json.response.internalUserID);
|
setUserId(json.response.internalUserID);
|
||||||
setUserName(json.response.fullname);
|
setUserName(json.response.fullname);
|
||||||
|
setUserAuthToken(json.response.authToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -110,7 +112,7 @@ function App() {
|
|||||||
<br />
|
<br />
|
||||||
<PluginDataChannel userId={userId} />
|
<PluginDataChannel userId={userId} />
|
||||||
<br />
|
<br />
|
||||||
<MyInfo />
|
<MyInfo userAuthToken={userAuthToken} />
|
||||||
<br />
|
<br />
|
||||||
<PresPresentationUploadToken />
|
<PresPresentationUploadToken />
|
||||||
<br />
|
<br />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {gql, useMutation, useQuery, useSubscription} from '@apollo/client';
|
import {gql, useMutation, useSubscription} from '@apollo/client';
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export default function MyInfo() {
|
export default function MyInfo({userAuthToken}) {
|
||||||
|
|
||||||
//where is not necessary once user can update only its own status
|
//where is not necessary once user can update only its own status
|
||||||
//Hasura accepts "now()" as value to timestamp fields
|
//Hasura accepts "now()" as value to timestamp fields
|
||||||
@ -20,17 +20,32 @@ export default function MyInfo() {
|
|||||||
updateUserClientEchoTestRunningAtMeAsNow();
|
updateUserClientEchoTestRunningAtMeAsNow();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [dispatchUserJoin] = useMutation(gql`
|
||||||
|
mutation UserJoin($authToken: String!, $clientType: String!) {
|
||||||
|
userJoin(
|
||||||
|
authToken: $authToken,
|
||||||
|
clientType: $clientType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const handleDispatchUserJoin = (authToken) => {
|
||||||
|
dispatchUserJoin({
|
||||||
|
variables: {
|
||||||
|
authToken: authToken,
|
||||||
|
clientType: 'HTML5',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const { loading, error, data } = useSubscription(
|
const { loading, error, data } = useSubscription(
|
||||||
gql`subscription {
|
gql`subscription {
|
||||||
user_current {
|
user_current {
|
||||||
userId
|
userId
|
||||||
name
|
name
|
||||||
meeting {
|
joined
|
||||||
name
|
joinErrorCode
|
||||||
}
|
joinErrorMessage
|
||||||
echoTestRunningAt
|
|
||||||
isRunningEchoTest
|
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
@ -45,23 +60,23 @@ export default function MyInfo() {
|
|||||||
{/*<th>Id</th>*/}
|
{/*<th>Id</th>*/}
|
||||||
<th>userId</th>
|
<th>userId</th>
|
||||||
<th>name</th>
|
<th>name</th>
|
||||||
<th>Meeting</th>
|
<th>joined</th>
|
||||||
<th>echoTestRunningAt</th>
|
<th>joinErrorCode</th>
|
||||||
<th>isRunningEchoTest</th>
|
<th>joinErrorMessage</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{data.user_current.map((curr) => {
|
{data.user_current.map((curr) => {
|
||||||
console.log('meeting', curr);
|
console.log('user_current', curr);
|
||||||
return (
|
return (
|
||||||
<tr key={curr.userId}>
|
<tr key={curr.userId}>
|
||||||
<td>{curr.userId}</td>
|
<td>{curr.userId}</td>
|
||||||
<td>{curr.name}</td>
|
<td>{curr.name}</td>
|
||||||
<td>{curr.meeting.name}</td>
|
<td>{curr.joined ? 'Yes' : 'No'}
|
||||||
<td>{curr.echoTestRunningAt}
|
{curr.joined ? '' : <button onClick={() => handleDispatchUserJoin(userAuthToken)}>Join Now!</button>}
|
||||||
<button onClick={() => handleUpdateUserEchoTestRunningAt()}>Set running now!</button>
|
|
||||||
</td>
|
</td>
|
||||||
<td>{curr.isRunningEchoTest ? 'Yes' : 'No'}</td>
|
<td>{curr.joinErrorCode}</td>
|
||||||
|
<td>{curr.joinErrorMessage}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -230,6 +230,8 @@ CREATE TABLE "user" (
|
|||||||
"sessionToken" varchar(16),
|
"sessionToken" varchar(16),
|
||||||
"authed" bool,
|
"authed" bool,
|
||||||
"joined" bool,
|
"joined" bool,
|
||||||
|
"joinErrorCode" varchar(50),
|
||||||
|
"joinErrorMessage" varchar(400),
|
||||||
"banned" bool,
|
"banned" bool,
|
||||||
"loggedOut" bool, -- when user clicked Leave meeting button
|
"loggedOut" bool, -- when user clicked Leave meeting button
|
||||||
"guest" bool, --used for dialIn
|
"guest" bool, --used for dialIn
|
||||||
@ -392,6 +394,8 @@ AS SELECT "user"."userId",
|
|||||||
"user"."role",
|
"user"."role",
|
||||||
"user"."authed",
|
"user"."authed",
|
||||||
"user"."joined",
|
"user"."joined",
|
||||||
|
"user"."joinErrorCode",
|
||||||
|
"user"."joinErrorMessage",
|
||||||
"user"."disconnected",
|
"user"."disconnected",
|
||||||
"user"."expired",
|
"user"."expired",
|
||||||
"user"."ejected",
|
"user"."ejected",
|
||||||
|
@ -23,3 +23,10 @@ type Mutation {
|
|||||||
): Boolean
|
): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
userJoin(
|
||||||
|
authToken: String!
|
||||||
|
clientType: String!
|
||||||
|
): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,13 @@ actions:
|
|||||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||||
permissions:
|
permissions:
|
||||||
- role: bbb_client
|
- role: bbb_client
|
||||||
|
- name: userJoin
|
||||||
|
definition:
|
||||||
|
kind: synchronous
|
||||||
|
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||||
|
permissions:
|
||||||
|
- role: pre_join_bbb_client
|
||||||
|
- role: bbb_client
|
||||||
custom_types:
|
custom_types:
|
||||||
enums: []
|
enums: []
|
||||||
input_objects: []
|
input_objects: []
|
||||||
|
@ -149,6 +149,8 @@ select_permissions:
|
|||||||
- isDialIn
|
- isDialIn
|
||||||
- isModerator
|
- isModerator
|
||||||
- isRunningEchoTest
|
- isRunningEchoTest
|
||||||
|
- joinErrorCode
|
||||||
|
- joinErrorMessage
|
||||||
- joined
|
- joined
|
||||||
- locked
|
- locked
|
||||||
- loggedOut
|
- loggedOut
|
||||||
@ -158,16 +160,39 @@ select_permissions:
|
|||||||
- pinned
|
- pinned
|
||||||
- presenter
|
- presenter
|
||||||
- raiseHand
|
- raiseHand
|
||||||
|
- registeredAt
|
||||||
- registeredOn
|
- registeredOn
|
||||||
- role
|
- role
|
||||||
- speechLocale
|
- speechLocale
|
||||||
- userId
|
- userId
|
||||||
filter:
|
filter:
|
||||||
_and:
|
userId:
|
||||||
- meetingId:
|
|
||||||
_eq: X-Hasura-MeetingId
|
|
||||||
- userId:
|
|
||||||
_eq: X-Hasura-UserId
|
_eq: X-Hasura-UserId
|
||||||
|
- role: pre_join_bbb_client
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- authed
|
||||||
|
- banned
|
||||||
|
- color
|
||||||
|
- disconnected
|
||||||
|
- ejectReason
|
||||||
|
- ejectReasonCode
|
||||||
|
- ejected
|
||||||
|
- expired
|
||||||
|
- extId
|
||||||
|
- guest
|
||||||
|
- joinErrorCode
|
||||||
|
- joinErrorMessage
|
||||||
|
- joined
|
||||||
|
- loggedOut
|
||||||
|
- name
|
||||||
|
- registeredAt
|
||||||
|
- registeredOn
|
||||||
|
- userId
|
||||||
|
filter:
|
||||||
|
userId:
|
||||||
|
_eq: X-Hasura-UserId
|
||||||
|
comment: ""
|
||||||
update_permissions:
|
update_permissions:
|
||||||
- role: bbb_client
|
- role: bbb_client
|
||||||
permission:
|
permission:
|
||||||
|
@ -81,12 +81,12 @@ class ConnectionController {
|
|||||||
def builder = new JsonBuilder()
|
def builder = new JsonBuilder()
|
||||||
builder {
|
builder {
|
||||||
"response" "authorized"
|
"response" "authorized"
|
||||||
"X-Hasura-Role" "bbb_client"
|
"X-Hasura-Role" u ? "bbb_client" : "pre_join_bbb_client"
|
||||||
"X-Hasura-Locked" u.locked ? "true" : "false"
|
"X-Hasura-Locked" u && u.locked ? "true" : "false"
|
||||||
"X-Hasura-LockedInMeeting" u.locked ? userSession.meetingID : ""
|
"X-Hasura-LockedInMeeting" u && u.locked ? userSession.meetingID : ""
|
||||||
"X-Hasura-LockedUserId" u.locked ? userSession.internalUserId : ""
|
"X-Hasura-LockedUserId" u && u.locked ? userSession.internalUserId : ""
|
||||||
"X-Hasura-ModeratorInMeeting" u.isModerator() ? userSession.meetingID : ""
|
"X-Hasura-ModeratorInMeeting" u && u.isModerator() ? userSession.meetingID : ""
|
||||||
"X-Hasura-PresenterInMeeting" u.isPresenter() ? userSession.meetingID : ""
|
"X-Hasura-PresenterInMeeting" u && u.isPresenter() ? userSession.meetingID : ""
|
||||||
"X-Hasura-UserId" userSession.internalUserId
|
"X-Hasura-UserId" userSession.internalUserId
|
||||||
"X-Hasura-MeetingId" userSession.meetingID
|
"X-Hasura-MeetingId" userSession.meetingID
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user