diff --git a/Constants/XWebinarHubAction.cs b/Constants/XWebinarHubAction.cs new file mode 100644 index 0000000..3d75696 --- /dev/null +++ b/Constants/XWebinarHubAction.cs @@ -0,0 +1,37 @@ +using xExceptions.Attributes; + +namespace xWebinarService.Constants +{ + public enum XWebinarHubAction + { + /// + /// When a Webinar is Available to Join ... + /// + [StringValue("WebinarOpened")] + WebinarOpened, + + /// + /// When a Webinar Closed ... + /// + [StringValue("WebinarClosed")] + WebinarClosed, + + /// + /// When a Viewer is Joined to Webinar ... + /// + [StringValue("ViewerJoined")] + ViewerJoined, + + /// + /// When a Webinar Subscriber Leaved a Webinar ... + /// + [StringValue("ViewerLeaved")] + ViewerLeaved, + + /// + /// Send a Message to Webinar ... + /// + [StringValue("Message")] + Message, + } +} \ No newline at end of file diff --git a/Hub/XWebinarHub.cs b/Hub/XWebinarHub.cs index 4b873ac..7760d7e 100644 --- a/Hub/XWebinarHub.cs +++ b/Hub/XWebinarHub.cs @@ -1,30 +1,435 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Models; using xPushService.Base; -using xPushService.Interfaces; -using xPushService.Models; +using xPushService.Extensions; +using xWebinarService.Constants; +using xWebinarService.Models.Dtos; +using xWebinarService.Providers; namespace xWebinarService.Hub { public class XWebinarHub : XBaseWebRTCHub { - protected XWebinarHub( - IXPushGroupStore groupStore, - IXPushConnectionStore connectionStore, - IXWebRTCConnectionStore webRTCConnectionStore - ) : base( - groupStore, - connectionStore, - webRTCConnectionStore - ) - { } + // + #region Props ... + public readonly XWebinarProvider webinarProvider; + #endregion - public override XPushConnectionDto GetConnection() + // + #region Constructor ... + public XWebinarHub( + ILogger logger, + XWebinarProvider webinarProvider + ) : base(logger) + { + this.webinarProvider = webinarProvider; + } + #endregion + + // + #region Actions ... + /// + /// a Webinar Owner Open Webinar for Broadcasting ... + /// + /// + /// + [Authorize] + public async Task WebinarOpened(Guid webinarId) { // - var result = GetBaseConnection(); - result.Type = GetType().Name; + // Validate Arg ... + var isValid = !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + // + logger.LogError($"Invalid Webinar Provider ..."); + return; + } // - return result; + // Reading and Validate Requirements ... + var userInfo = Context.GetUserInfo(); + var connectionId = Context.ConnectionId; + isValid = !userInfo.IsNullOrDefault() && + !connectionId.IsNullOrEmpty(); + if (!isValid) + { + // + logger.LogError($"Invaid User Info ..."); + return; + } + + // + // Retrieve Webinar Model and Validate it Exists ... + try + { + // + // Retrieve Webinar ... + var webinar = await webinarProvider.GetWebinar(webinarId); + + // + // Validate Webinar ... + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + // + XException.NotFound.Throw(); + logger.LogError($"Webinar Not Found ..."); + } + + // + // Validate User is Webinar Owner ... + isValid = + !userInfo.UserId.IsNullOrEmpty() && + userInfo.UserId == webinar.OwnerId; + if (!isValid) + { + // + XException.NotAllowed.Throw(); + logger.LogError($"Not Allowed to Present Webinar ..."); + } + + // + // Now try to Enable Webinar and Notify Webinars Client Which it's Open ... + webinar.Enabled = true; + webinar = await webinarProvider.UpdateWebinar(webinar); + await Clients.AllExcept(connectionId).SendAsync(XWebinarHubAction.WebinarOpened.GetStringValue(), webinarId); + + // + // Create a Group and Put User on it ... + await Groups.AddToGroupAsync(connectionId, webinarId.ToString()); + } + catch { } } + + /// + /// Close a Webinar by it's Owner ... + /// + /// + /// + [Authorize] + public async Task WebinarClosed(Guid webinarId) + { + // + // Validate Arg ... + var isValid = !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + // + logger.LogError($"Invalid Webinar Provider ..."); + return; + } + + // + // Reading and Validate Requirements ... + var userInfo = Context.GetUserInfo(); + var connectionId = Context.ConnectionId; + isValid = !userInfo.IsNullOrDefault() && + !connectionId.IsNullOrEmpty(); + if (!isValid) + { + // + logger.LogError($"Invaid User Info ..."); + return; + } + + // + // Retrieve Webinar Model and Validate it Exists ... + try + { + // + // Retrieve Webinar ... + var webinar = await webinarProvider.GetWebinar(webinarId); + + // + // Validate Webinar ... + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + // + XException.NotFound.Throw(); + logger.LogError($"Webinar Not Found ..."); + } + + // + // Validate User is Webinar Owner ... + isValid = + !userInfo.UserId.IsNullOrEmpty() && + userInfo.UserId == webinar.OwnerId; + if (!isValid) + { + // + XException.NotAllowed.Throw(); + logger.LogError($"Not Allowed to Present Webinar ..."); + } + + // + // Now try to Enable Webinar and Notify Webinars Client Which it's Open ... + webinar.Enabled = false; + webinar = await webinarProvider.UpdateWebinar(webinar); + await Clients.AllExcept(connectionId).SendAsync(XWebinarHubAction.WebinarClosed.GetStringValue(), webinarId); + + // + var webinarName = webinarId.ToString(); + await Clients.OthersInGroup(webinarName).SendAsync(XWebinarHubAction.WebinarClosed.ToString()); + await Groups.RemoveFromGroupAsync(connectionId, webinarName); + } + catch { } + } + + /// + /// a Webinar Subscriber Joined to Opened Webinar ... + /// + /// + /// + [Authorize] + public async Task ViewerJoined(Guid webinarId) + { + // + // Validate ... + var isValid = !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + // + logger.LogError($"Invalid Webinar Provider ..."); + return; + } + + // + // Retrieve requirements ... + var userInfo = Context.GetUserInfo(); + var connectionId = Context.ConnectionId; + isValid = !userInfo.IsNullOrDefault() && + !connectionId.IsNullOrEmpty(); + if (!isValid) + { + // + logger.LogError($"Invaid User Info ..."); + return; + } + + // + try + { + // + // Reading Webinar ... + var webinar = await webinarProvider.GetWebinar(webinarId); + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + // + logger.LogError($"Webinar Not Found ..."); + XException.NotFound.Throw(); + } + + // + // Validate User is Webinar Subscriber ... + // Or Webinar is Open ... + if (webinar.Type != XWebinarType.Public) + { + // + isValid = await webinarProvider.IsSubscribed( + webinarId: webinarId, + subscriberId: userInfo.UserId + ); + if (!isValid) + { + // + logger.LogError("User Not Have Permission to Join Webinar ..."); + XException.NotAllowed.Throw(); + } + } + + // + var webinarName = webinarId.ToString(); + + // + // Add User to Webinar ... + await Groups.AddToGroupAsync(connectionId, webinarName); + + // + // Notify Other Users which a Viewer Joined ... + await Clients.OthersInGroup(webinarName).SendAsync(XWebinarHubAction.ViewerJoined.GetStringValue(), connectionId, userInfo.UserId, userInfo.UserName); + } + catch + { } + } + + /// + /// a Webina Subscriber Lefts Group ... + /// + /// + /// + [Authorize] + public async Task ViewerLeaved(Guid webinarId) + { + // + // Validate ... + var isValid = !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + // + logger.LogError($"Invalid Webinar Provider ..."); + return; + } + + // + // Retrieve requirements ... + var userInfo = Context.GetUserInfo(); + var connectionId = Context.ConnectionId; + isValid = !userInfo.IsNullOrDefault() && + !connectionId.IsNullOrEmpty(); + if (!isValid) + { + // + logger.LogError($"Invaid User Info ..."); + return; + } + + // + try + { + // + // Reading Webinar ... + var webinar = await webinarProvider.GetWebinar(webinarId); + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + // + logger.LogError($"Webinar Not Found ..."); + XException.NotFound.Throw(); + } + + // + // Validate User is Webinar Subscriber ... + // Or Webinar is Open ... + if (webinar.Type != XWebinarType.Public) + { + // + isValid = await webinarProvider.IsSubscribed( + webinarId: webinarId, + subscriberId: userInfo.UserId + ); + if (!isValid) + { + // + logger.LogError("User Not Have Permission to Join Webinar ..."); + XException.NotAllowed.Throw(); + } + } + + // + var webinarName = webinarId.ToString(); + + // + // Add User to Webinar ... + await Groups.RemoveFromGroupAsync(connectionId, webinarName); + + // + // Notify Other Users which a Viewer Leaved ... + await Clients.OthersInGroup(webinarName).SendAsync(XWebinarHubAction.ViewerLeaved.GetStringValue(), + connectionId, + userInfo.UserId, + userInfo.UserName + ); + } + catch + { } + } + + /// + /// Send a Message to Specified Webinar ... + /// + /// + /// + /// + [Authorize] + public async Task Message(Guid webinarId, string message) + { + // + // Validate ... + var isValid = !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + // + logger.LogError($"Invalid Webinar Provider ..."); + return; + } + + // + // Retrieve requirements ... + var userInfo = Context.GetUserInfo(); + var connectionId = Context.ConnectionId; + isValid = !userInfo.IsNullOrDefault() && + !connectionId.IsNullOrEmpty(); + if (!isValid) + { + // + logger.LogError($"Invaid User Info ..."); + return; + } + + // + try + { + // + // Reading Webinar ... + var webinar = await webinarProvider.GetWebinar(webinarId); + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + // + logger.LogError($"Webinar Not Found ..."); + XException.NotFound.Throw(); + } + + // + // Validate User is Webinar Owner or Subscriber ... + // Or Webinar is Open ... + if (webinar.Type != XWebinarType.Public) + { + // + isValid = userInfo.UserId == webinar.OwnerId || + (await webinarProvider.IsSubscribed( + webinarId: webinarId, + subscriberId: userInfo.UserId + )); + if (!isValid) + { + // + logger.LogError("User Not Have Permission to Join Webinar ..."); + XException.NotAllowed.Throw(); + } + } + + // + var webinarName = webinarId.ToString(); + + // + // Notify Other Users which a Viewer Leaved ... + await Clients.OthersInGroup(webinarName).SendAsync(XWebinarHubAction.Message.GetStringValue(), + connectionId, + userInfo.UserId, + userInfo.UserName, + message + ); + } + catch + { } + } + #endregion } } \ No newline at end of file