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.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.models.{ RegisteredUser, RegisteredUsers, Users2x, VoiceUsers }
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.running._
|
||||
import org.bigbluebutton.core2.message.senders._
|
||||
|
||||
trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||
this: MeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
@ -18,64 +17,136 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
||||
log.info("Received user joined meeting. user {} meetingId={}", msg.body.userId, msg.header.meetingId)
|
||||
|
||||
Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) match {
|
||||
case Some(reconnectingUser) =>
|
||||
if (reconnectingUser.userLeftFlag.left) {
|
||||
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)
|
||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||
}
|
||||
|
||||
state
|
||||
case None =>
|
||||
// Check if maxParticipants has been reached
|
||||
// User are able to reenter if he already joined previously with the same extId
|
||||
val userHasJoinedAlready = RegisteredUsers.findWithUserId(msg.body.userId, liveMeeting.registeredUsers) match {
|
||||
case Some(regUser: RegisteredUser) => RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
||||
case None => false
|
||||
}
|
||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
||||
userHasJoinedAlready == false
|
||||
|
||||
if (!hasReachedMaxParticipants) {
|
||||
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
|
||||
|
||||
if (liveMeeting.props.meetingProp.isBreakout) {
|
||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
||||
}
|
||||
|
||||
// Warn previous users that someone connected with same Id
|
||||
for {
|
||||
regUser <- RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
||||
liveMeeting.registeredUsers)
|
||||
} yield {
|
||||
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
||||
.filter(u => u.id != regUser.id)
|
||||
.foreach { previousUser =>
|
||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
previousUser.id,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"info",
|
||||
"promote",
|
||||
"app.mobileAppModal.userConnectedWithSameId",
|
||||
"Notification to warn that user connect again from other browser/device",
|
||||
Vector(regUser.name)
|
||||
)
|
||||
outGW.send(notifyUserEvent)
|
||||
}
|
||||
}
|
||||
|
||||
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
||||
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, msg.body.userId)
|
||||
UserStateDAO.updateExpired(msg.body.userId, false)
|
||||
|
||||
newState
|
||||
} else {
|
||||
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
|
||||
}
|
||||
case Some(user) => handleUserReconnecting(user, msg, state)
|
||||
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)
|
||||
sendUserLeftFlagUpdatedEvtMsg(outGW, liveMeeting, msg.body.userId, false)
|
||||
Users2x.resetUserLeftFlag(liveMeeting.users2x, msg.body.userId)
|
||||
}
|
||||
|
||||
private def validateMaxParticipants(regUser: RegisteredUser): Either[(String, String), Unit] = {
|
||||
val userHasJoinedAlready = RegisteredUsers.checkUserExtIdHasJoined(regUser.externId, liveMeeting.registeredUsers)
|
||||
val maxParticipants = liveMeeting.props.usersProp.maxUsers - 1
|
||||
|
||||
if (maxParticipants > 0 && //0 = no limit
|
||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= maxParticipants &&
|
||||
!userHasJoinedAlready) {
|
||||
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||
} else {
|
||||
Right(())
|
||||
}
|
||||
}
|
||||
|
||||
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||
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) {
|
||||
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
|
||||
}
|
||||
}
|
||||
|
||||
private def notifyPreviousUsersWithSameExtId(regUser: RegisteredUser) = {
|
||||
RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
||||
.filter(_.id != regUser.id)
|
||||
.foreach { previousUser =>
|
||||
sendUserConnectedNotification(previousUser, regUser, liveMeeting)
|
||||
}
|
||||
}
|
||||
|
||||
private def sendUserConnectedNotification(previousUser: RegisteredUser, newUser: RegisteredUser, liveMeeting: LiveMeeting) = {
|
||||
val notifyUserEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(
|
||||
previousUser.id,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"info",
|
||||
"promote",
|
||||
"app.mobileAppModal.userConnectedWithSameId",
|
||||
"Notification to warn that user connect again from other browser/device",
|
||||
Vector(newUser.name)
|
||||
)
|
||||
outGW.send(notifyUserEvent)
|
||||
}
|
||||
|
||||
private def clearCachedVoiceUser(regUser: RegisteredUser) =
|
||||
// fresh user joined (not due to reconnection). Clear (pop) the cached voice user
|
||||
VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, regUser.id)
|
||||
|
||||
private def clearExpiredUserState(regUser: RegisteredUser) =
|
||||
UserStateDAO.updateExpired(regUser.id, false)
|
||||
|
||||
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.core.bus.InternalEventBus
|
||||
import org.bigbluebutton.core.db.UserDAO
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models._
|
||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||
@ -15,103 +16,79 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
||||
val eventBus: InternalEventBus
|
||||
|
||||
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."
|
||||
var failReasonCode = EjectReasonCode.VALIDATE_TOKEN
|
||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers)
|
||||
log.info(s"Number of registered users [${RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers)}]")
|
||||
|
||||
log.info("Number of registered users [{}]", RegisteredUsers.numRegisteredUsers(liveMeeting.registeredUsers))
|
||||
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId,
|
||||
liveMeeting.registeredUsers)
|
||||
regUser match {
|
||||
case Some(u) =>
|
||||
// Check if maxParticipants has been reached
|
||||
// User are able to reenter if he already joined previously with the same extId
|
||||
val hasReachedMaxParticipants = liveMeeting.props.usersProp.maxUsers > 0 &&
|
||||
RegisteredUsers.numUniqueJoinedUsers(liveMeeting.registeredUsers) >= liveMeeting.props.usersProp.maxUsers &&
|
||||
RegisteredUsers.checkUserExtIdHasJoined(u.externId, liveMeeting.registeredUsers) == false
|
||||
regUser.fold {
|
||||
sendFailedValidateAuthTokenRespMsg(msg, "Invalid auth token.", EjectReasonCode.VALIDATE_TOKEN)
|
||||
} { user =>
|
||||
val validationResult = for {
|
||||
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||
_ <- checkIfUserIsBanned(user)
|
||||
_ <- checkIfUserLoggedOut(user)
|
||||
_ <- validateMaxParticipants(user)
|
||||
} yield user
|
||||
|
||||
// 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 {
|
||||
if (u.banned) {
|
||||
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
|
||||
)
|
||||
}
|
||||
validationResult.fold(
|
||||
reason => sendFailedValidateAuthTokenRespMsg(msg, reason._1, reason._2),
|
||||
validUser => sendSuccessfulValidateAuthTokenRespMsg(validUser)
|
||||
)
|
||||
}
|
||||
|
||||
case None =>
|
||||
validateTokenFailed(
|
||||
outGW,
|
||||
meetingId = liveMeeting.props.meetingProp.intId,
|
||||
userId = msg.header.userId,
|
||||
authToken = msg.body.authToken,
|
||||
valid = false,
|
||||
waitForApproval = false,
|
||||
failReason,
|
||||
failReasonCode,
|
||||
state
|
||||
)
|
||||
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.checkUserExtIdHasJoined(user.externId, liveMeeting.registeredUsers) == false) {
|
||||
Left(("The maximum number of participants allowed for this meeting has been reached.", EjectReasonCode.MAX_PARTICIPANTS))
|
||||
} else {
|
||||
Right(())
|
||||
}
|
||||
}
|
||||
|
||||
def validateTokenFailed(
|
||||
outGW: OutMsgRouter,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
authToken: String,
|
||||
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
|
||||
private def checkIfUserGuestStatusIsAllowed(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||
if (user.guestStatus != GuestStatus.ALLOW) {
|
||||
Left(("User is not allowed to join", EjectReasonCode.PERMISSION_FAILED))
|
||||
} else {
|
||||
Right(())
|
||||
}
|
||||
}
|
||||
|
||||
def sendValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String,
|
||||
valid: Boolean, waitForApproval: Boolean, registeredOn: Long, authTokenValidatedOn: Long,
|
||||
reasonCode: String = EjectReasonCode.NOT_EJECT, reason: String = "User not ejected"): Unit = {
|
||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval, registeredOn,
|
||||
authTokenValidatedOn, reasonCode, reason)
|
||||
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 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)
|
||||
}
|
||||
|
||||
def userValidated(user: RegisteredUser, state: MeetingState2x): MeetingState2x = {
|
||||
def sendSuccessfulValidateAuthTokenRespMsg(user: RegisteredUser) = {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val updatedUser = RegisteredUsers.updateUserLastAuthTokenValidated(liveMeeting.registeredUsers, user)
|
||||
|
||||
sendValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, valid = true, waitForApproval = false, updatedUser.registeredOn, updatedUser.lastAuthTokenValidatedOn)
|
||||
state
|
||||
val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, updatedUser.id, updatedUser.authToken, true, false, updatedUser.registeredOn,
|
||||
updatedUser.lastAuthTokenValidatedOn, EjectReasonCode.NOT_EJECT, "User not ejected")
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def sendAllUsersInMeeting(requesterId: String): Unit = {
|
||||
|
@ -77,12 +77,12 @@ object MeetingDAO {
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => {
|
||||
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)
|
||||
MeetingLockSettingsDAO.insert(meetingProps.meetingProp.intId, meetingProps.lockSettingsProps)
|
||||
MeetingMetadataDAO.insert(meetingProps.meetingProp.intId, meetingProps.metadataProp)
|
||||
MeetingRecordingPoliciesDAO.insert(meetingProps.meetingProp.intId, meetingProps.recordProp)
|
||||
MeetingVoiceDAO.insert(meetingProps.meetingProp.intId, meetingProps.voiceProp)
|
||||
ChatDAO.insert(meetingProps.meetingProp.intId, GroupChatApp.createDefaultPublicGroupChat())
|
||||
MeetingWelcomeDAO.insert(meetingProps.meetingProp.intId, meetingProps.welcomeProp)
|
||||
MeetingGroupDAO.insert(meetingProps.meetingProp.intId, meetingProps.groups)
|
||||
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")
|
||||
}
|
||||
|
||||
object UserConnectionStatusdDAO {
|
||||
object UserConnectionStatusDAO {
|
||||
|
||||
def insert(meetingId: String, userId: String) = {
|
||||
DatabaseConnection.db.run(
|
||||
|
@ -6,29 +6,31 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class UserDbModel(
|
||||
userId: String,
|
||||
extId: String,
|
||||
meetingId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
avatar: String = "",
|
||||
color: String = "",
|
||||
sessionToken: String = "",
|
||||
authed: Boolean = false,
|
||||
joined: Boolean = false,
|
||||
banned: Boolean = false,
|
||||
loggedOut: Boolean = false,
|
||||
guest: Boolean,
|
||||
guestStatus: String,
|
||||
registeredOn: Long,
|
||||
excludeFromDashboard: Boolean,
|
||||
userId: String,
|
||||
extId: String,
|
||||
meetingId: String,
|
||||
name: String,
|
||||
role: String,
|
||||
avatar: String = "",
|
||||
color: String = "",
|
||||
sessionToken: String = "",
|
||||
authed: Boolean = false,
|
||||
joined: Boolean = false,
|
||||
joinErrorMessage: Option[String],
|
||||
joinErrorCode: Option[String],
|
||||
banned: Boolean = false,
|
||||
loggedOut: Boolean = false,
|
||||
guest: Boolean,
|
||||
guestStatus: String,
|
||||
registeredOn: Long,
|
||||
excludeFromDashboard: Boolean,
|
||||
)
|
||||
|
||||
|
||||
|
||||
class UserDbTableDef(tag: Tag) extends Table[UserDbModel](tag, None, "user") {
|
||||
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 extId = column[String]("extId")
|
||||
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 authed = column[Boolean]("authed")
|
||||
val joined = column[Boolean]("joined")
|
||||
val joinErrorCode = column[Option[String]]("joinErrorCode")
|
||||
val joinErrorMessage = column[Option[String]]("joinErrorMessage")
|
||||
val banned = column[Boolean]("banned")
|
||||
val loggedOut = column[Boolean]("loggedOut")
|
||||
val guest = column[Boolean]("guest")
|
||||
@ -62,6 +66,8 @@ object UserDAO {
|
||||
sessionToken = regUser.sessionToken,
|
||||
authed = regUser.authed,
|
||||
joined = regUser.joined,
|
||||
joinErrorCode = None,
|
||||
joinErrorMessage = None,
|
||||
banned = regUser.banned,
|
||||
loggedOut = regUser.loggedOut,
|
||||
guest = regUser.guest,
|
||||
@ -73,10 +79,10 @@ object UserDAO {
|
||||
).onComplete {
|
||||
case Success(rowsAffected) => {
|
||||
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted in User table!")
|
||||
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
||||
UserConnectionStatusdDAO.insert(meetingId, regUser.id)
|
||||
UserConnectionStatusDAO.insert(meetingId, regUser.id)
|
||||
UserCustomParameterDAO.insert(regUser.id, regUser.customParameters)
|
||||
UserClientSettingsDAO.insert(regUser.id, meetingId)
|
||||
ChatUserDAO.insertUserPublicChat(meetingId, regUser.id)
|
||||
}
|
||||
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) = {
|
||||
|
@ -30,6 +30,7 @@ function App() {
|
||||
const [sessionToken, setSessionToken] = useState(null);
|
||||
const [userId, setUserId] = useState(null);
|
||||
const [userName, setUserName] = useState(null);
|
||||
const [userAuthToken, setUserAuthToken] = useState(null);
|
||||
const [graphqlClient, setGraphqlClient] = useState(null);
|
||||
const [enterApiResponse, setEnterApiResponse] = useState('');
|
||||
|
||||
@ -46,6 +47,7 @@ function App() {
|
||||
if(json?.response?.internalUserID) {
|
||||
setUserId(json.response.internalUserID);
|
||||
setUserName(json.response.fullname);
|
||||
setUserAuthToken(json.response.authToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -110,7 +112,7 @@ function App() {
|
||||
<br />
|
||||
<PluginDataChannel userId={userId} />
|
||||
<br />
|
||||
<MyInfo />
|
||||
<MyInfo userAuthToken={userAuthToken} />
|
||||
<br />
|
||||
<PresPresentationUploadToken />
|
||||
<br />
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {gql, useMutation, useQuery, useSubscription} from '@apollo/client';
|
||||
import {gql, useMutation, useSubscription} from '@apollo/client';
|
||||
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
|
||||
//Hasura accepts "now()" as value to timestamp fields
|
||||
@ -20,17 +20,32 @@ export default function MyInfo() {
|
||||
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(
|
||||
gql`subscription {
|
||||
user_current {
|
||||
userId
|
||||
name
|
||||
meeting {
|
||||
name
|
||||
}
|
||||
echoTestRunningAt
|
||||
isRunningEchoTest
|
||||
joined
|
||||
joinErrorCode
|
||||
joinErrorMessage
|
||||
}
|
||||
}`
|
||||
);
|
||||
@ -45,23 +60,23 @@ export default function MyInfo() {
|
||||
{/*<th>Id</th>*/}
|
||||
<th>userId</th>
|
||||
<th>name</th>
|
||||
<th>Meeting</th>
|
||||
<th>echoTestRunningAt</th>
|
||||
<th>isRunningEchoTest</th>
|
||||
<th>joined</th>
|
||||
<th>joinErrorCode</th>
|
||||
<th>joinErrorMessage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.user_current.map((curr) => {
|
||||
console.log('meeting', curr);
|
||||
console.log('user_current', curr);
|
||||
return (
|
||||
<tr key={curr.userId}>
|
||||
<td>{curr.userId}</td>
|
||||
<td>{curr.name}</td>
|
||||
<td>{curr.meeting.name}</td>
|
||||
<td>{curr.echoTestRunningAt}
|
||||
<button onClick={() => handleUpdateUserEchoTestRunningAt()}>Set running now!</button>
|
||||
<td>{curr.joined ? 'Yes' : 'No'}
|
||||
{curr.joined ? '' : <button onClick={() => handleDispatchUserJoin(userAuthToken)}>Join Now!</button>}
|
||||
</td>
|
||||
<td>{curr.isRunningEchoTest ? 'Yes' : 'No'}</td>
|
||||
<td>{curr.joinErrorCode}</td>
|
||||
<td>{curr.joinErrorMessage}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
@ -230,6 +230,8 @@ CREATE TABLE "user" (
|
||||
"sessionToken" varchar(16),
|
||||
"authed" bool,
|
||||
"joined" bool,
|
||||
"joinErrorCode" varchar(50),
|
||||
"joinErrorMessage" varchar(400),
|
||||
"banned" bool,
|
||||
"loggedOut" bool, -- when user clicked Leave meeting button
|
||||
"guest" bool, --used for dialIn
|
||||
@ -392,6 +394,8 @@ AS SELECT "user"."userId",
|
||||
"user"."role",
|
||||
"user"."authed",
|
||||
"user"."joined",
|
||||
"user"."joinErrorCode",
|
||||
"user"."joinErrorMessage",
|
||||
"user"."disconnected",
|
||||
"user"."expired",
|
||||
"user"."ejected",
|
||||
|
@ -23,3 +23,10 @@ type Mutation {
|
||||
): Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
userJoin(
|
||||
authToken: String!
|
||||
clientType: String!
|
||||
): Boolean
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,13 @@ actions:
|
||||
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
|
||||
permissions:
|
||||
- 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:
|
||||
enums: []
|
||||
input_objects: []
|
||||
|
@ -149,6 +149,8 @@ select_permissions:
|
||||
- isDialIn
|
||||
- isModerator
|
||||
- isRunningEchoTest
|
||||
- joinErrorCode
|
||||
- joinErrorMessage
|
||||
- joined
|
||||
- locked
|
||||
- loggedOut
|
||||
@ -158,16 +160,39 @@ select_permissions:
|
||||
- pinned
|
||||
- presenter
|
||||
- raiseHand
|
||||
- registeredAt
|
||||
- registeredOn
|
||||
- role
|
||||
- speechLocale
|
||||
- userId
|
||||
filter:
|
||||
_and:
|
||||
- meetingId:
|
||||
_eq: X-Hasura-MeetingId
|
||||
- userId:
|
||||
_eq: X-Hasura-UserId
|
||||
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:
|
||||
- role: bbb_client
|
||||
permission:
|
||||
|
@ -81,12 +81,12 @@ class ConnectionController {
|
||||
def builder = new JsonBuilder()
|
||||
builder {
|
||||
"response" "authorized"
|
||||
"X-Hasura-Role" "bbb_client"
|
||||
"X-Hasura-Locked" u.locked ? "true" : "false"
|
||||
"X-Hasura-LockedInMeeting" u.locked ? userSession.meetingID : ""
|
||||
"X-Hasura-LockedUserId" u.locked ? userSession.internalUserId : ""
|
||||
"X-Hasura-ModeratorInMeeting" u.isModerator() ? userSession.meetingID : ""
|
||||
"X-Hasura-PresenterInMeeting" u.isPresenter() ? userSession.meetingID : ""
|
||||
"X-Hasura-Role" u ? "bbb_client" : "pre_join_bbb_client"
|
||||
"X-Hasura-Locked" u && u.locked ? "true" : "false"
|
||||
"X-Hasura-LockedInMeeting" u && u.locked ? userSession.meetingID : ""
|
||||
"X-Hasura-LockedUserId" u && u.locked ? userSession.internalUserId : ""
|
||||
"X-Hasura-ModeratorInMeeting" u && u.isModerator() ? userSession.meetingID : ""
|
||||
"X-Hasura-PresenterInMeeting" u && u.isPresenter() ? userSession.meetingID : ""
|
||||
"X-Hasura-UserId" userSession.internalUserId
|
||||
"X-Hasura-MeetingId" userSession.meetingID
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user