/// <summary> /// Event raise every heartbeat /// </summary> /// <param name="source"></param> /// <param name="e"></param> private static void Heartbeat(Object source, ElapsedEventArgs e) { IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); if (hub == null) { return; } Info("Running heartbeat ..."); using (Module.Administration.DatabaseContext requester = new Module.Administration.DatabaseContext()) { Debug("Ping all clients connected ..."); foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { try { Verbose($"Ping the client {connection}"); hub.Clients.Client(connection.ConnectionId).ping(); } catch (System.Exception exception) { Exception("Unable to ping the client", exception); } } Debug("Disconnect all clients without any updates since a while ..."); foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { if (connection.ConnectionLast < DateTime.Now.AddSeconds(-ConfigurationManager.ClientHubTimeout)) { try { Info($"Disconnecting the client {connection} ..."); hub.Clients.Client(connection.ConnectionId).stop(); connection.Allow = false; requester.SaveChanges(); } catch (System.Exception exception) { Exception("Unable to disconnect the client", exception); } } } } }
/// <summary> /// Launch the heartbeat of the application if it is enabled /// </summary> public static void StartHeartbeat() { // Start Heartbeat to disconnect client if no updated done since a while if (ConfigurationManager.HeartbeatDelay <= 0 || ConfigurationManager.ClientHubTimeout <= 0) { return; } Info($"Launching heartbeat every {ConfigurationManager.HeartbeatDelay} seconds and disconnect clients after {ConfigurationManager.ClientHubTimeout} seconds without response ..."); // Disconnect all connections if no updates done since a while ... IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); using (Module.Administration.DatabaseContext requester = new Module.Administration.DatabaseContext()) foreach (ConnectionRecord connection in requester._Connection.Where(c => c.Allow && c.Status).ToList()) { if (connection.ConnectionLast < DateTime.Now.AddSeconds(-ConfigurationManager.ClientHubTimeout)) { try { Info($"Disconnecting the client {connection} ..."); if (hub != null) { hub.Clients.Client(connection.ConnectionId).stop(); } connection.Allow = false; requester.SaveChanges(); } catch (System.Exception exception) { Exception("Unable to disconnect the client", exception); } } } // Run the heartbeat Debug("Starting heartbeat ..."); Timer heartbeat = new Timer(ConfigurationManager.HeartbeatDelay * 1000); heartbeat.Elapsed += Heartbeat; heartbeat.Start(); }
/// <summary> /// The client tests if the server if up ... this call generally occurs on connection on an existing account! /// </summary> public void Ping() { try { DateTime now = DateTime.Now; Debug($"Ping from '{Context.ConnectionId}'"); using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { // receive the ping from the client PingRecord existingPing = database._Ping.Find(Context.ConnectionId); if (existingPing != null) { existingPing.Date = now; } else { database._Ping.Add(new PingRecord { ConnectionId = Context.ConnectionId }); } // update the last connection time of the client ConnectionRecord currentConnection = database._Connection.Find(Context.ConnectionId); if (currentConnection != null) { currentConnection.ConnectionLast = now; } database.SaveChanges(); } } catch (System.Exception ex) { Exception($"An exception occurs on pinging of the connection '{Context.ConnectionId}'", ex); Warn("This exception is ignored ... may be the connection was previously deleted or disconnected!"); } }
/// <summary> /// Check if the given user is already connected through hub /// </summary> /// <param name="userId"></param> /// <returns></returns> public static bool IsAlreadyConnected(int userId) { // the user exists, check if it's already connected // In case of reloading page, the connection can't be disconnected as quick as expected ... IHubContext hub = GlobalHost.ConnectionManager.GetHubContext <DatabaseHub>(); if (hub == null) { // no hub ... disconnect the user and go to the index page Common.Logger.LoggerManager.Instance.Info(MODULE, $"Cleaning up all connections for the user {userId} ..."); using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { ConnectionRecord currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); while (currentConnection != null) { Common.Logger.LoggerManager.Instance.Debug(MODULE, $"Connection {currentConnection} removed ..."); try { database._Connection.Remove(currentConnection); } catch { } currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); } database.SaveChanges(); } return(false); } // DatabaseCommonContext is frequently open/close here to clean up the cache of the database (else the ping record is never updated) ... bool alreadyConnected = true; DateTime pingDate = DateTime.Now; int count = 0; while (count < ConfigurationManager.ConnectionMaxWaiting && alreadyConnected) { count++; ConnectionRecord currentConnection = null; using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) currentConnection = database._Connection.FirstOrDefault(c => c.UserId == userId && c.Allow); if (currentConnection != null && count < ConfigurationManager.ConnectionMaxWaiting) { // Ping the client and wait 1 secondes to check if the client responses if (count == 1) { Thread.Sleep(1000); Common.Logger.LoggerManager.Instance.Debug(MODULE, $"Ping to '{currentConnection.ConnectionId}'"); hub.Clients.Client(currentConnection.ConnectionId).ping(); } // Did the client answer to the ping ? PingRecord currentPing = null; using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) currentPing = database._Ping.Find(currentConnection.ConnectionId); // The client has answered to the ping ... So, it already exists if (currentPing != null && DateTime.Compare(currentPing.Date, pingDate) >= 0) { break; } // The client didn't answer ... wait a few more second if (count > 1) { Thread.Sleep(1000); } } else if (currentConnection != null) { // The max seconds has expired ... it means that no client is already connected ... disconnect it alreadyConnected = false; try { hub.Clients.Client(currentConnection.ConnectionId).stop(); Common.Logger.LoggerManager.Instance.Info(MODULE, $"The connection '{currentConnection.ConnectionId}' is disconnected"); } catch (System.Exception ex) { Common.Logger.LoggerManager.Instance.Exception(MODULE, $"(Warning) Unable to disconnect '{currentConnection.ConnectionId}' due to an exception", ex); } using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { currentConnection = database._Connection.Find(currentConnection.ConnectionId); currentConnection.Allow = false; database.SaveChanges(); } } else { alreadyConnected = false; Common.Logger.LoggerManager.Instance.Info(MODULE, $"No connection already existing or the previous connection is disconnected ..."); } } if (alreadyConnected) { Common.Logger.LoggerManager.Instance.Debug(MODULE, $"The user '{userId}' is already connected on another support"); return(true); } return(false); }
/// <summary> /// Initialize the application /// </summary> private void Initialize() { StatusManager.Status = StatusManager.EStatus.STATUS_OK; // Initialize Log4Net and LoggerManager try { log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config"))); Common.Logger.LoggerManager.Instance.Initialize(); } catch (System.Exception ex) { // Impossible to start the application due to an abnormal situation on initializing // The log manager is not currently running ! StatusManager.Status = StatusManager.EStatus.STATUS_FAIL; StatusManager.Exception = ex; return; } // Log settings from web.config try { Info("Settings into the web.config"); foreach (KeyValuePair <string, string> setting in ConfigurationManager.Settings) { Info($"Setting[{setting.Key}] = '{setting.Value}'"); } } catch (System.Exception ex) { Exception("An exception occurs during reading the settings", ex); } try { string connectionString = WebConfigurationManager.ConnectionStrings[ConfigurationManager.CONNEXION_STRING]?.ConnectionString; if (connectionString == null) { connectionString = ""; } Debug($"Connection String to the database is '{connectionString}'"); using (Module.Administration.DatabaseContext database = new Module.Administration.DatabaseContext()) { // Open a connection to the database database.Database.Connection.Open(); // Does the database upgrade towards the latest version ? if (database.HasToUpgrade()) { Warn("A upgrading process must be run"); StatusManager.Status = StatusManager.EStatus.STATUS_UPGRADING; return; } // Build the database schema ConfigurationManager.Schemas[Module.Administration.DatabaseContext.AREA_NAME] = new Common.Database.DSSchema.DSDatabase(typeof(Module.Administration.DatabaseContext), new DatabaseRequest()); Info($"Database schema[{Module.Administration.DatabaseContext.AREA_NAME}] : '{ConfigurationManager.Schemas[Module.Administration.DatabaseContext.AREA_NAME]}'"); ConfigurationManager.Schemas[Module.Customer.DatabaseContext.AREA_NAME] = new Common.Database.DSSchema.DSDatabase(typeof(Module.Customer.DatabaseContext), new DatabaseRequest()); Info($"Database schema[{Module.Customer.DatabaseContext.AREA_NAME}] : '{ConfigurationManager.Schemas[Module.Customer.DatabaseContext.AREA_NAME]}'"); // Log settings from database Info("Settings into the database"); foreach (ParameterRecord parameter in database._Parameter) { Info($"Parameter[{parameter.Key}] = '{parameter.Value}'"); } /* Clean up all connection from this server */ if (ConfigurationManager.ConnectionCleanup) { database.CleanupConnection(); } /* Remove files older than max number of days from ~/App_Data */ if (ConfigurationManager.AppDataMaxDays < 0) { Info("Do not clean up the folder ~/App_Data"); } else { int nbFilesDeleted = 0; Info("Cleaning up the folder ~/App_Data ..."); foreach (String filename in Directory.GetFiles(Server.MapPath("~/App_Data"))) { Debug($"Cleaning up the file '{filename}' ..."); FileAttributes fileAttr = File.GetAttributes(filename); if (fileAttr.HasFlag(FileAttributes.Directory) || Path.GetFileName(filename).Equals("UploadFiles.txt")) { Debug($"The file '{filename}' mustn't be deleted ..."); continue; } FileInfo fileInfo = new FileInfo(filename); if (fileInfo.CreationTime >= DateTime.Now.AddDays(-ConfigurationManager.AppDataMaxDays)) { Info($"Keep the file '{filename}'"); continue; } try { fileInfo.Delete(); Info($"'{filename}' deleted"); nbFilesDeleted++; } catch (System.Exception ex) { Exception($"Unable to delete '{filename}'", ex); } } Info($"{nbFilesDeleted} files deleted"); } // Initialize the cache manager for all known customers if (DatabaseCacheManager.Instance.IsEnable) { Debug($"Loading data into the cache manager for all customers ..."); List <int> customerIds = database.Customer.Select(c => c.Id).ToList(); DatabaseCacheManager.Instance.Initialize(database, customerIds, Syncytium.Managers.DatabaseManager.GetDatabase); Info($"Data loaded into the cache manager for all customers ..."); } else { Info("The database cache manager is disabled"); } // Load all labels LanguageManager.GetInstance(database); } } catch (System.Exception ex) { Exception("An exception occurs during initializing database connection", ex); StatusManager.Status = StatusManager.EStatus.STATUS_FAIL; StatusManager.Exception = ex; return; } // Run threads DatabaseQueue.Instance.StartConsumer(); StartHeartbeat(); }
/// <summary> /// Apply the filter to an action of the controller /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { if (IsVerbose()) { Verbose($"Check if the current user is allowed to get access to the target page ({filterContext.HttpContext.Request.RawUrl}) - AllModules = {(AllModules ? "true" : "false")} - Module = {Module} - Role = {Role}"); } // if the user is not authenticated ... can't access to the page ... redirect to the sign in page if (!HttpContext.Current.User.Identity.IsAuthenticated) { Verbose("The user has to be authenticated!"); filterContext.Result = ((SyncytiumController)filterContext.Controller).RedirectToAction("SignIn", "User", "Administration", filterContext.HttpContext.Request.RawUrl); base.OnActionExecuting(filterContext); return; } if (Ping) { base.OnActionExecuting(filterContext); return; } // it is due to a upgrading process ... no error because StatusFilterAttribute has already rejected the action if (StatusManager.Status != StatusManager.EStatus.STATUS_OK) { Info("The server is not available!"); if (!HttpContext.Current.User.Identity.Name.Equals("-1")) { Warn("Only the administrator default user can get access! "); FormsAuthentication.SignOut(); filterContext.Result = ((SyncytiumController)filterContext.Controller).RedirectToAction("SignIn", "User", "Administration", "", "ERR_UNAUTHORIZED"); } Verbose("The user is allowed!"); base.OnActionExecuting(filterContext); return; } // check if the user expected must be only connected (default) if (AllModules && Role == UserProfile.EUserProfile.None) { Verbose("The user is authenticated!"); base.OnActionExecuting(filterContext); return; } // check if the user gets access on Customer or Administration screen if (!AllModules && Module == EModule.None) { // the default administrator user is allowed to get to this feature if (HttpContext.Current.User.Identity.Name.Equals("-1")) { Warn("The default administrator is allowed!"); base.OnActionExecuting(filterContext); return; } // the user connected must be for CustomerId = 1 try { using (Syncytium.Module.Administration.DatabaseContext dbContext = new Module.Administration.DatabaseContext()) { UserManager database = new UserManager(dbContext); if (!(database.GetById(int.Parse(HttpContext.Current.User.Identity.Name)) is UserRecord currentUser) || currentUser.CustomerId != 1) { Warn("The current user is not the user of the first customer!"); // The login doesn't exist, is it the administrator default login ? // Or, Only the user defined for CustomerId = 1 can get access to the screen FormsAuthentication.SignOut(); filterContext.Result = ((SyncytiumController)filterContext.Controller).RedirectToAction("SignIn", "User", "Administration", "", "ERR_UNAUTHORIZED"); } else { Info("The user of the first customer is allowed!"); } }