From 06071679a94d081f7e4cdc07dfa4e6c12e93fb17 Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Thu, 16 May 2013 19:27:28 +1000 Subject: [PATCH] Fix: SignalR Authorize & Firefox Compatibility Changes in architecture to support authorization and temporary workaround for NTLM in Firefox (to be removed in SignalR 2.x). Thanks to @davidfowl --- .../AdminAuthorizedPersistentConnection.cs | 16 +++++ .../AuthorizedPersistentConnection.cs | 36 +++++++++++ .../SignalRHandlers/LogNotifications.cs | 60 +++++++++++++++++++ .../RepositoryMonitorNotifications.cs | 2 +- .../ScheduledTasksStatusNotifications.cs | 38 ++++++------ .../SignalRAuthenticationWorkaround.cs | 58 ++++++++++++++++++ ...ices.cs => UserHeldDeviceNotifications.cs} | 6 +- Disco.Services/Logging/LogContext.cs | 16 +---- .../Logging/Targets/LogLiveContext.cs | 52 +++------------- Disco.Services/Tasks/ScheduledTaskStatus.cs | 6 +- Disco.Web/App_Start/RouteConfig.cs | 5 +- Disco.Web/Areas/API/APIAreaRegistration.cs | 13 ++-- .../Config/Views/Shared/LogEvents.cshtml | 2 +- .../Views/Shared/LogEvents.generated.cs | 2 +- .../Areas/Public/PublicAreaRegistration.cs | 5 +- Disco.Web/Views/Job/JobParts/Resources.cshtml | 3 +- .../Views/Job/JobParts/Resources.generated.cs | 3 +- 17 files changed, 227 insertions(+), 96 deletions(-) create mode 100644 Disco.BI/BI/Interop/SignalRHandlers/AdminAuthorizedPersistentConnection.cs create mode 100644 Disco.BI/BI/Interop/SignalRHandlers/AuthorizedPersistentConnection.cs create mode 100644 Disco.BI/BI/Interop/SignalRHandlers/LogNotifications.cs rename Disco.Services/Tasks/ScheduledTasksLiveStatusService.cs => Disco.BI/BI/Interop/SignalRHandlers/ScheduledTasksStatusNotifications.cs (59%) create mode 100644 Disco.BI/BI/Interop/SignalRHandlers/SignalRAuthenticationWorkaround.cs rename Disco.BI/BI/Interop/SignalRHandlers/{UserHeldDevices.cs => UserHeldDeviceNotifications.cs} (88%) diff --git a/Disco.BI/BI/Interop/SignalRHandlers/AdminAuthorizedPersistentConnection.cs b/Disco.BI/BI/Interop/SignalRHandlers/AdminAuthorizedPersistentConnection.cs new file mode 100644 index 00000000..85c082f9 --- /dev/null +++ b/Disco.BI/BI/Interop/SignalRHandlers/AdminAuthorizedPersistentConnection.cs @@ -0,0 +1,16 @@ +using Disco.Models.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.BI.Interop.SignalRHandlers +{ + public class AdminAuthorizedPersistentConnection : AuthorizedPersistentConnection + { + private string[] authorizedUserTypes = { User.Types.Admin }; + + protected override string[] AuthorizedUserTypes { get { return authorizedUserTypes; } } + } +} \ No newline at end of file diff --git a/Disco.BI/BI/Interop/SignalRHandlers/AuthorizedPersistentConnection.cs b/Disco.BI/BI/Interop/SignalRHandlers/AuthorizedPersistentConnection.cs new file mode 100644 index 00000000..6d32d41f --- /dev/null +++ b/Disco.BI/BI/Interop/SignalRHandlers/AuthorizedPersistentConnection.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNet.SignalR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.BI.Interop.SignalRHandlers +{ + public class AuthorizedPersistentConnection : PersistentConnection + { + private string[] authorizedUserTypes = null; + + protected virtual string[] AuthorizedUserTypes { get { return authorizedUserTypes; } } + + protected override bool AuthorizeRequest(IRequest request) + { + if (!request.User.Identity.IsAuthenticated) + return false; + else + { + var user = UserBI.UserCache.CurrentUser; + if (user == null) + return false; + + if (AuthorizedUserTypes == null || AuthorizedUserTypes.Length == 0) + return true; + + if (AuthorizedUserTypes.Contains(user.Type)) + return true; + + return false; + } + } + } +} diff --git a/Disco.BI/BI/Interop/SignalRHandlers/LogNotifications.cs b/Disco.BI/BI/Interop/SignalRHandlers/LogNotifications.cs new file mode 100644 index 00000000..a8ba7c23 --- /dev/null +++ b/Disco.BI/BI/Interop/SignalRHandlers/LogNotifications.cs @@ -0,0 +1,60 @@ +using Disco.Services.Logging; +using Disco.Services.Logging.Models; +using Microsoft.AspNet.SignalR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.BI.Interop.SignalRHandlers +{ + public class LogNotifications : AdminAuthorizedPersistentConnection + { + public static bool initialized = false; + + public LogNotifications() + { + if (!initialized) + { + initialized = true; + Disco.Services.Logging.Targets.LogLiveContext.LogBroadcast += Broadcast; + } + } + + protected override System.Threading.Tasks.Task OnReceived(IRequest request, string connectionId, string data) + { + // Add to Group + if (!string.IsNullOrWhiteSpace(data) && data.StartsWith("/addToGroups:") && data.Length > 13) + { + var groups = data.Substring(13).Split(','); + foreach (var g in groups) + { + this.Groups.Add(connectionId, g); + } + } + + return base.OnReceived(request, connectionId, data); + } + + internal static void Broadcast(LogBase logModule, LogEventType eventType, DateTime Timestamp, params object[] Arguments) + { + var message = LogLiveEvent.Create(logModule, eventType, Timestamp, Arguments); + + var connectionManager = GlobalHost.ConnectionManager; + var connectionContext = connectionManager.GetConnectionContext(); + connectionContext.Groups.Send(_GroupNameAll, message); + connectionContext.Groups.Send(logModule.ModuleName, message); + } + + private const string _GroupNameAll = "__All"; + + public static string AllNotifications + { + get + { + return _GroupNameAll; + } + } + } +} diff --git a/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs b/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs index 1e336b0e..7a193f66 100644 --- a/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs +++ b/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace Disco.BI.Interop.SignalRHandlers { - public class RepositoryMonitorNotifications : PersistentConnection + public class RepositoryMonitorNotifications : AdminAuthorizedPersistentConnection { public static void Initialize() { diff --git a/Disco.Services/Tasks/ScheduledTasksLiveStatusService.cs b/Disco.BI/BI/Interop/SignalRHandlers/ScheduledTasksStatusNotifications.cs similarity index 59% rename from Disco.Services/Tasks/ScheduledTasksLiveStatusService.cs rename to Disco.BI/BI/Interop/SignalRHandlers/ScheduledTasksStatusNotifications.cs index 4c21a8d6..96fbcd62 100644 --- a/Disco.Services/Tasks/ScheduledTasksLiveStatusService.cs +++ b/Disco.BI/BI/Interop/SignalRHandlers/ScheduledTasksStatusNotifications.cs @@ -1,14 +1,25 @@ -using System; +using Disco.Services.Tasks; +using Microsoft.AspNet.SignalR; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -using Microsoft.AspNet.SignalR; -using Microsoft.AspNet.SignalR.Infrastructure; +using System.Threading.Tasks; -namespace Disco.Services.Tasks +namespace Disco.BI.Interop.SignalRHandlers { - public class ScheduledTasksLiveStatusService : PersistentConnection + public class ScheduledTasksStatusNotifications : AdminAuthorizedPersistentConnection { + public static bool initialized = false; + + public ScheduledTasksStatusNotifications() + { + if (!initialized) + { + initialized = true; + Disco.Services.Tasks.ScheduledTaskStatus.UpdatedBroadcast += Broadcast; + } + } protected override System.Threading.Tasks.Task OnReceived(IRequest request, string connectionId, string data) { @@ -26,29 +37,20 @@ namespace Disco.Services.Tasks internal static void Broadcast(ScheduledTaskStatusLive SessionStatus) { - //var message = Models.LogLiveEvent.Create(logModule, eventType, Timestamp, Arguments); - - var connectionManager = GlobalHost.ConnectionManager; //AspNetHost.DependencyResolver.Resolve(); - var connectionContext = connectionManager.GetConnectionContext(); + var connectionManager = GlobalHost.ConnectionManager; + var connectionContext = connectionManager.GetConnectionContext(); connectionContext.Groups.Send(_GroupNameAll, SessionStatus); connectionContext.Groups.Send(SessionStatus.SessionId, SessionStatus); } private const string _GroupNameAll = "__All"; - //private static string _QualifiedSessionName = typeof(ScheduledTasksLiveStatusService).FullName + "."; - //private static string _QualifiedSessionNameAll = _QualifiedSessionName + "__All"; - //private static string LiveStatusGroup(string SessionId) - //{ - // return string.Concat(_QualifiedSessionName, SessionId); - //} - public static string LiveStatusAll + + public static string AllNotifications { get { - //return _QualifiedTypeNameAll; return _GroupNameAll; } } - } } diff --git a/Disco.BI/BI/Interop/SignalRHandlers/SignalRAuthenticationWorkaround.cs b/Disco.BI/BI/Interop/SignalRHandlers/SignalRAuthenticationWorkaround.cs new file mode 100644 index 00000000..fba2d313 --- /dev/null +++ b/Disco.BI/BI/Interop/SignalRHandlers/SignalRAuthenticationWorkaround.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using System.Web.Routing; +using System.Web.Security; +using System.Web.SessionState; +using Microsoft.AspNet.SignalR; +using Owin; +using AppFunc = System.Func, System.Threading.Tasks.Task>; + +namespace Disco.BI.Interop.SignalRHandlers +{ + /// + /// Required for SignalR 1.1.0 NTLM support in Firefox & Safari + /// Returns 401 (Unauthorized) instead of 403 (Forbidden) when an unauthenticated request is processed + /// + /// TODO: Remove this workaround when implementing SignalR 2.x + /// + /// Thanks to David Fowler (@davidfowl) + /// + public static class SignalRAuthenticationWorkaround + { + public static void AddMiddleware(IAppBuilder app) + { + Func convert403To401 = Convert403To401; + + app.Use(convert403To401); + } + + private static AppFunc Convert403To401(AppFunc next) + { + return env => + { + // Execute the SignalR pipeline + Task task = next(env); + + // Get the status code + int statusCode = 0; + if (env.ContainsKey("owin.ResponseStatusCode")) + { + statusCode = (int)env["owin.ResponseStatusCode"]; + } + + // If its 403 then convert it to 401 (we shouldn't do + // this if it's a cross domain request since it doesn't make sense) + if (statusCode == 403) + { + env["owin.ResponseStatusCode"] = 401; + } + + // Return the original task + return task; + }; + } + } +} diff --git a/Disco.BI/BI/Interop/SignalRHandlers/UserHeldDevices.cs b/Disco.BI/BI/Interop/SignalRHandlers/UserHeldDeviceNotifications.cs similarity index 88% rename from Disco.BI/BI/Interop/SignalRHandlers/UserHeldDevices.cs rename to Disco.BI/BI/Interop/SignalRHandlers/UserHeldDeviceNotifications.cs index 5fdfeb11..09a854b2 100644 --- a/Disco.BI/BI/Interop/SignalRHandlers/UserHeldDevices.cs +++ b/Disco.BI/BI/Interop/SignalRHandlers/UserHeldDeviceNotifications.cs @@ -9,12 +9,12 @@ using Disco.Models.Repository; namespace Disco.BI.Interop.SignalRHandlers { - public class UserHeldDevices : PersistentConnection + public class UserHeldDeviceNotifications : PersistentConnection { private static bool subscribed = false; private static object subscribeLock = new object(); - static UserHeldDevices() + static UserHeldDeviceNotifications() { if (!subscribed) lock (subscribeLock) @@ -32,7 +32,7 @@ namespace Disco.BI.Interop.SignalRHandlers if (j.UserId != null) { var connectionManager = GlobalHost.ConnectionManager; - var connectionContext = connectionManager.GetConnectionContext(); + var connectionContext = connectionManager.GetConnectionContext(); if (connectionContext != null) connectionContext.Connection.Broadcast(j.UserId); } diff --git a/Disco.Services/Logging/LogContext.cs b/Disco.Services/Logging/LogContext.cs index 09fe63bd..d1dfd317 100644 --- a/Disco.Services/Logging/LogContext.cs +++ b/Disco.Services/Logging/LogContext.cs @@ -237,13 +237,6 @@ namespace Disco.Services.Logging var reInitalizeJobDetail = new JobDetailImpl("DiscoLogContextReinialize", typeof(LogReInitalizeJob)); - // Simple Trigger - Issue with Day light savings - //var reInitalizeTrigger = TriggerBuilder.Create() - // .WithIdentity("DiscoLogContextReinializeTrigger") - // .StartAt(DateBuilder.TomorrowAt(0,0,0)) - // .WithSchedule(SimpleScheduleBuilder.Create().WithIntervalInHours(24).RepeatForever()) - // .Build(); - // Use Cron Schedule instead var reInitalizeTrigger = TriggerBuilder.Create() .WithIdentity("DiscoLogContextReinializeTrigger") .StartNow() @@ -252,13 +245,6 @@ namespace Disco.Services.Logging _ReInitializeScheduler.ScheduleJob(reInitalizeJobDetail, reInitalizeTrigger); } - public static string LiveLogAllEventsGroupName - { - get - { - return Targets.LogLiveContext.LiveLogNameAll; - } - } private LogContext(string PersistantStorePath, string PersistantStoreConnectionString) { @@ -286,7 +272,7 @@ namespace Disco.Services.Logging { string args = null; if (Args != null && Args.Length > 0) - { //args = fastJSON.JSON.Instance.ToJSON(Args, false); // Old fastJSON Implementation + { args = JsonConvert.SerializeObject(Args); } using (var context = new Targets.LogPersistContext(PersistantStoreConnectionString)) diff --git a/Disco.Services/Logging/Targets/LogLiveContext.cs b/Disco.Services/Logging/Targets/LogLiveContext.cs index bca05b2b..d96125c5 100644 --- a/Disco.Services/Logging/Targets/LogLiveContext.cs +++ b/Disco.Services/Logging/Targets/LogLiveContext.cs @@ -1,55 +1,21 @@ -using System; +using Disco.Services.Logging.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -using Microsoft.AspNet.SignalR; -using Microsoft.AspNet.SignalR.Infrastructure; +using System.Threading.Tasks; namespace Disco.Services.Logging.Targets { - public class LogLiveContext : PersistentConnection + public static class LogLiveContext { + public delegate void LogBroadcastEvent(LogBase logModule, LogEventType eventType, DateTime Timestamp, params object[] Arguments); + public static event LogBroadcastEvent LogBroadcast; - protected override System.Threading.Tasks.Task OnReceived(IRequest request, string connectionId, string data) + internal static void Broadcast(LogBase logModule, LogEventType eventType, DateTime Timestamp, params object[] Arguments) { - // Add to Group - if (!string.IsNullOrWhiteSpace(data) && data.StartsWith("/addToGroups:") && data.Length > 13) - { - var groups = data.Substring(13).Split(','); - foreach (var g in groups) - { - this.Groups.Add(connectionId, g); - } - } - - return base.OnReceived(request, connectionId, data); + if (LogBroadcast != null) + LogBroadcast.Invoke(logModule, eventType, Timestamp, Arguments); } - - internal static void Broadcast(LogBase logModule, Models.LogEventType eventType, DateTime Timestamp, params object[] Arguments) - { - var message = Models.LogLiveEvent.Create(logModule, eventType, Timestamp, Arguments); - - var connectionManager = GlobalHost.ConnectionManager; - var connectionContext = connectionManager.GetConnectionContext(); - connectionContext.Groups.Send(_GroupNameAll, message); - connectionContext.Groups.Send(logModule.ModuleName, message); - } - - private const string _GroupNameAll = "__All"; - //private static string _QualifiedTypeName = typeof(LogLiveContext).FullName + "."; - //private static string _QualifiedTypeNameAll = _QualifiedTypeName + "__All"; - //private static string LiveLogNameGroup(string LogName) - //{ - // return string.Concat(_QualifiedTypeName, LogName); - //} - public static string LiveLogNameAll - { - get - { - //return _QualifiedTypeNameAll; - return _GroupNameAll; - } - } - } } diff --git a/Disco.Services/Tasks/ScheduledTaskStatus.cs b/Disco.Services/Tasks/ScheduledTaskStatus.cs index edf75990..3c77477e 100644 --- a/Disco.Services/Tasks/ScheduledTaskStatus.cs +++ b/Disco.Services/Tasks/ScheduledTaskStatus.cs @@ -73,8 +73,10 @@ namespace Disco.Services.Tasks #endregion #region Events + public delegate void UpdatedBroadcastEvent(ScheduledTaskStatusLive SessionStatus); public delegate void UpdatedEvent(ScheduledTaskStatus sender, string[] ChangedProperties); public delegate void CancelingEvent(ScheduledTaskStatus sender); + public static event UpdatedBroadcastEvent UpdatedBroadcast; public event UpdatedEvent Updated; public event CancelingEvent Canceling; #endregion @@ -344,8 +346,8 @@ namespace Disco.Services.Tasks if (Updated != null) Updated(this, ChangedProperties); - if (!_isSilent) - ScheduledTasksLiveStatusService.Broadcast(ScheduledTaskStatusLive.FromScheduledTaskStatus(this, ChangedProperties)); + if (!_isSilent && UpdatedBroadcast != null) + UpdatedBroadcast.Invoke(ScheduledTaskStatusLive.FromScheduledTaskStatus(this, ChangedProperties)); } } } diff --git a/Disco.Web/App_Start/RouteConfig.cs b/Disco.Web/App_Start/RouteConfig.cs index 693380f2..32d9bece 100644 --- a/Disco.Web/App_Start/RouteConfig.cs +++ b/Disco.Web/App_Start/RouteConfig.cs @@ -5,6 +5,7 @@ using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.AspNet.SignalR; +using Disco.BI.Interop.SignalRHandlers; namespace Disco.Web { @@ -73,9 +74,9 @@ namespace Disco.Web public static void RegisterUpdateRoutes(RouteCollection routes) { // Task Status SignalR Route - routes.MapConnection( + routes.MapConnection( "API_Logging_TaskStatusNotifications", - "API/Logging/TaskStatusNotifications"); + "API/Logging/TaskStatusNotifications", new ConnectionConfiguration(), SignalRAuthenticationWorkaround.AddMiddleware); // Task Status Ajax Route routes.MapRoute( diff --git a/Disco.Web/Areas/API/APIAreaRegistration.cs b/Disco.Web/Areas/API/APIAreaRegistration.cs index 3d2fe083..7e4ce8a8 100644 --- a/Disco.Web/Areas/API/APIAreaRegistration.cs +++ b/Disco.Web/Areas/API/APIAreaRegistration.cs @@ -1,6 +1,7 @@ using System.Web.Mvc; using System.Web.Routing; using Microsoft.AspNet.SignalR; +using Disco.BI.Interop.SignalRHandlers; namespace Disco.Web.Areas.API { @@ -16,14 +17,14 @@ namespace Disco.Web.Areas.API public override void RegisterArea(AreaRegistrationContext context) { - context.Routes.MapConnection( - "API_Logging_Notifications", "API/Logging/Notifications"); + context.Routes.MapConnection( + "API_Logging_Notifications", "API/Logging/Notifications", new ConnectionConfiguration(), SignalRAuthenticationWorkaround.AddMiddleware); - context.Routes.MapConnection( - "API_Logging_TaskStatusNotifications", "API/Logging/TaskStatusNotifications"); + context.Routes.MapConnection( + "API_Logging_TaskStatusNotifications", "API/Logging/TaskStatusNotifications", new ConnectionConfiguration(), SignalRAuthenticationWorkaround.AddMiddleware); - context.Routes.MapConnection( - "API_Repository_Notifications", "API/Repository/Notifications"); + context.Routes.MapConnection( + "API_Repository_Notifications", "API/Repository/Notifications", new ConnectionConfiguration(), SignalRAuthenticationWorkaround.AddMiddleware); context.MapRoute( "API_Update", diff --git a/Disco.Web/Areas/Config/Views/Shared/LogEvents.cshtml b/Disco.Web/Areas/Config/Views/Shared/LogEvents.cshtml index b090b49d..eac35342 100644 --- a/Disco.Web/Areas/Config/Views/Shared/LogEvents.cshtml +++ b/Disco.Web/Areas/Config/Views/Shared/LogEvents.cshtml @@ -44,7 +44,7 @@ $(function () { var logEventsHost = $('LogEvents_@(uniqueId)'); var logModuleId = '@(Model.ModuleFilter != null ? Model.ModuleFilter.ModuleId.ToString() : null)'; - var logModuleLiveGroupName = '@(Model.ModuleFilter != null ? Model.ModuleFilter.LiveLogGroupName : Disco.Services.Logging.LogContext.LiveLogAllEventsGroupName)'; + var logModuleLiveGroupName = '@(Model.ModuleFilter != null ? Model.ModuleFilter.LiveLogGroupName : Disco.BI.Interop.SignalRHandlers.LogNotifications.AllNotifications)'; var logEventTypeFiltered = @(eventTypesFilterJson); var logStartFiler = @(AjaxHelpers.JsonDate(Model.StartFilter)); var logEndFiler = @(AjaxHelpers.JsonDate(Model.EndFilter)); diff --git a/Disco.Web/Areas/Config/Views/Shared/LogEvents.generated.cs b/Disco.Web/Areas/Config/Views/Shared/LogEvents.generated.cs index 6fc7fa5b..c65a69a2 100644 --- a/Disco.Web/Areas/Config/Views/Shared/LogEvents.generated.cs +++ b/Disco.Web/Areas/Config/Views/Shared/LogEvents.generated.cs @@ -196,7 +196,7 @@ WriteLiteral("\';\r\n var logModuleLiveGroupName = \'"); #line 47 "..\..\Areas\Config\Views\Shared\LogEvents.cshtml" - Write(Model.ModuleFilter != null ? Model.ModuleFilter.LiveLogGroupName : Disco.Services.Logging.LogContext.LiveLogAllEventsGroupName); + Write(Model.ModuleFilter != null ? Model.ModuleFilter.LiveLogGroupName : Disco.BI.Interop.SignalRHandlers.LogNotifications.AllNotifications); #line default diff --git a/Disco.Web/Areas/Public/PublicAreaRegistration.cs b/Disco.Web/Areas/Public/PublicAreaRegistration.cs index 146d2ee4..ad0d7ffb 100644 --- a/Disco.Web/Areas/Public/PublicAreaRegistration.cs +++ b/Disco.Web/Areas/Public/PublicAreaRegistration.cs @@ -1,6 +1,7 @@ using System.Web.Mvc; using System.Web.Routing; using Microsoft.AspNet.SignalR; +using Disco.BI.Interop.SignalRHandlers; namespace Disco.Web.Areas.Public { @@ -16,8 +17,8 @@ namespace Disco.Web.Areas.Public public override void RegisterArea(AreaRegistrationContext context) { - context.Routes.MapConnection( - "Public_UserHeldDevices_Notifications", "Public/UserHeldDevices/Notifications"); + context.Routes.MapConnection( + "Public_UserHeldDevices_Notifications", "Public/UserHeldDevices/Notifications", new ConnectionConfiguration(), SignalRAuthenticationWorkaround.AddMiddleware); context.MapRoute( "Public_Credits", diff --git a/Disco.Web/Views/Job/JobParts/Resources.cshtml b/Disco.Web/Views/Job/JobParts/Resources.cshtml index f4338231..b82e6820 100644 --- a/Disco.Web/Views/Job/JobParts/Resources.cshtml +++ b/Disco.Web/Views/Job/JobParts/Resources.cshtml @@ -402,7 +402,8 @@ var liveMessagesConnection = $.connection('@(Url.Content("~/API/Repository/Notifications"))') liveMessagesConnection.received(liveMessageRecieved); liveMessagesConnection.error(function (e) { - alert('Error: ' + JSON.stringify(e)); + if (e) + alert('Error: ' + JSON.stringify(e)); }); liveMessagesConnection.start(function () { liveMessagesConnection.send('/addToGroups:JobLog,JobAttachment'); diff --git a/Disco.Web/Views/Job/JobParts/Resources.generated.cs b/Disco.Web/Views/Job/JobParts/Resources.generated.cs index e49220e4..f11d5351 100644 --- a/Disco.Web/Views/Job/JobParts/Resources.generated.cs +++ b/Disco.Web/Views/Job/JobParts/Resources.generated.cs @@ -786,7 +786,8 @@ WriteLiteral("\',\r\n dataType: \'json\',\r\n WriteLiteral(@"') liveMessagesConnection.received(liveMessageRecieved); liveMessagesConnection.error(function (e) { - alert('Error: ' + JSON.stringify(e)); + if (e) + alert('Error: ' + JSON.stringify(e)); }); liveMessagesConnection.start(function () { liveMessagesConnection.send('/addToGroups:JobLog,JobAttachment');