Enhance SyncManager to raise events which can be handled on the server within hosted services. Raise create, update, delete events for all major entities. Include support for refresh and reload events to synchronize client state. Move client state cache invalidation to a hosted service to separate concerns and demonstrate events.

This commit is contained in:
Shaun Walker 2022-10-04 19:20:02 -04:00
parent 68ada8fbe4
commit c5b632cb24
27 changed files with 212 additions and 92 deletions

View File

@ -1,9 +0,0 @@
namespace Oqtane.UI
{
public enum Refresh
{
None,
Site,
Application
}
}

View File

@ -79,7 +79,7 @@
Page page;
User user = null;
var editmode = false;
var refresh = UI.Refresh.None;
var refresh = false;
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
_error = "";
@ -116,7 +116,7 @@
// the refresh parameter is used to refresh the client-side PageState
if (querystring.ContainsKey("refresh"))
{
refresh = UI.Refresh.Site;
refresh = true;
}
if (PageState != null)
@ -126,7 +126,7 @@
}
// get user
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity.IsAuthenticated)
@ -149,27 +149,27 @@
if (sync.SyncEvents.Any())
{
// reload client application if server was restarted or site runtime/rendermode was modified
if (PageState != null && sync.SyncEvents.Exists(item => (item.TenantId == -1 || item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId) && item.Reload))
if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
{
NavigationManager.NavigateTo(_absoluteUri, true);
return;
}
// when a site has changed the state needs to be refreshed
// when site information has changed the PageState needs to be refreshed
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{
refresh = UI.Refresh.Site;
refresh = true;
}
// when a user changed the site needs to be refreshed as the list of pages/modules may have changed
// when user information has changed the PageState needs to be refreshed as the list of pages/modules may have changed
if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
{
refresh = UI.Refresh.Site;
refresh = true;
}
}
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
refresh = UI.Refresh.Site;
refresh = true;
}
else
{
@ -178,7 +178,7 @@
if (site != null)
{
if (PageState == null || refresh == UI.Refresh.Site || PageState.Page.Path != route.PagePath)
if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
{
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
editmode = false;

View File

@ -17,13 +17,15 @@ namespace Oqtane.Controllers
{
private readonly IAliasRepository _aliases;
private readonly ITenantRepository _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public AliasController(IAliasRepository aliases, ITenantRepository tenants, ILogManager logger, ITenantManager tenantManager)
public AliasController(IAliasRepository aliases, ITenantRepository tenants, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_aliases = aliases;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -57,6 +59,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid)
{
alias = _aliases.AddAlias(alias);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
}
else
@ -76,6 +79,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && _aliases.GetAlias(alias.AliasId, false) != null)
{
alias = _aliases.UpdateAlias(alias);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
}
else
@ -96,6 +100,7 @@ namespace Oqtane.Controllers
if (alias != null)
{
_aliases.DeleteAlias(id);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Alias Deleted {AliasId}", id);
var aliases = _aliases.GetAliases();

View File

@ -32,15 +32,17 @@ namespace Oqtane.Controllers
private readonly IFileRepository _files;
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_environment = environment;
_files = files;
_folders = folders;
_userPermissions = userPermissions;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -148,6 +150,7 @@ namespace Oqtane.Controllers
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
file = _files.UpdateFile(file);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
}
else
@ -168,8 +171,6 @@ namespace Oqtane.Controllers
Models.File file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
{
_files.DeleteFile(id);
string filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
@ -180,6 +181,8 @@ namespace Oqtane.Controllers
}
}
_files.DeleteFile(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
}
else
@ -251,6 +254,7 @@ namespace Oqtane.Controllers
if (file != null)
{
file = _files.AddFile(file);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
}
}
catch (Exception ex)
@ -324,7 +328,8 @@ namespace Oqtane.Controllers
var file = CreateFile(upload, FolderId, Path.Combine(folderPath, upload));
if (file != null)
{
_files.AddFile(file);
file = _files.AddFile(file);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
}
}
}

View File

@ -20,13 +20,15 @@ namespace Oqtane.Controllers
{
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_folders = folders;
_userPermissions = userPermissions;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -124,6 +126,7 @@ namespace Oqtane.Controllers
}
folder.Path = folder.Path + "/";
folder = _folders.AddFolder(folder);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
}
else
@ -172,6 +175,7 @@ namespace Oqtane.Controllers
}
folder = _folders.UpdateFolder(folder);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
}
else
@ -205,6 +209,7 @@ namespace Oqtane.Controllers
{
folder.Order = order;
_folders.UpdateFolder(folder);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
}
order += 2;
}
@ -230,6 +235,7 @@ namespace Oqtane.Controllers
Directory.Delete(_folders.GetFolderPath(folder));
}
_folders.DeleteFolder(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
}
else

View File

@ -102,7 +102,8 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
{
language = _languages.AddLanguage(language);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Create);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
}
else
@ -122,7 +123,8 @@ namespace Oqtane.Controllers
if (language != null && language.SiteId == _alias.SiteId)
{
_languages.DeleteLanguage(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Delete);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
}
else

View File

@ -124,7 +124,8 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
{
module = _modules.AddModule(module);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Create);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
}
else
@ -174,7 +175,8 @@ namespace Oqtane.Controllers
}
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Update);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
}
else
@ -195,7 +197,8 @@ namespace Oqtane.Controllers
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
{
_modules.DeleteModule(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Delete);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
}
else

View File

@ -29,10 +29,11 @@ namespace Oqtane.Controllers
private readonly IWebHostEnvironment _environment;
private readonly IServiceProvider _serviceProvider;
private readonly ITenantManager _tenantManager;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ILogManager logger)
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_moduleDefinitions = moduleDefinitions;
_tenants = tenants;
@ -42,6 +43,7 @@ namespace Oqtane.Controllers
_environment = environment;
_serviceProvider = serviceProvider;
_tenantManager = tenantManager;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -145,6 +147,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
{
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition);
}
else
@ -227,6 +230,7 @@ namespace Oqtane.Controllers
// remove module definition
_moduleDefinitions.DeleteModuleDefinition(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
}
else

View File

@ -8,6 +8,7 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
using System.Reflection.Metadata;
namespace Oqtane.Controllers
{
@ -16,13 +17,15 @@ namespace Oqtane.Controllers
{
private readonly INotificationRepository _notifications;
private readonly IUserPermissions _userPermissions;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_notifications = notifications;
_userPermissions = userPermissions;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -83,6 +86,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
{
notification = _notifications.AddNotification(notification);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
}
else
@ -102,6 +106,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && _notifications.GetNotification(notification.NotificationId, false) != null && IsAuthorized(notification.FromUserId))
{
notification = _notifications.UpdateNotification(notification);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
}
else
@ -122,6 +127,7 @@ namespace Oqtane.Controllers
if (notification != null && notification.SiteId == _alias.SiteId && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
{
_notifications.DeleteNotification(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Notification Deleted {NotificationId}", id);
}
else

View File

@ -143,7 +143,8 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
{
page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
if (!page.Path.StartsWith("admin/"))
@ -200,7 +201,8 @@ namespace Oqtane.Controllers
page.IsPersonalizable = false;
page.UserId = int.Parse(userid);
page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
// copy modules
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
@ -313,7 +315,8 @@ namespace Oqtane.Controllers
}
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Update);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
}
else
@ -353,10 +356,12 @@ namespace Oqtane.Controllers
{
page.Order = order;
_pages.UpdatePage(page);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Update);
}
order += 2;
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
}
else
@ -375,7 +380,8 @@ namespace Oqtane.Controllers
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{
_pages.DeletePage(page.PageId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Delete);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId);
}
else

View File

@ -9,6 +9,7 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Oqtane.Controllers
{
@ -75,7 +76,8 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
{
pageModule = _pageModules.AddPageModule(pageModule);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Create);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
}
else
@ -96,7 +98,8 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
{
pageModule = _pageModules.UpdatePageModule(pageModule);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Update);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
}
else
@ -124,10 +127,11 @@ namespace Oqtane.Controllers
{
pagemodule.Order = order;
_pageModules.UpdatePageModule(pagemodule);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pagemodule.PageModuleId, SyncEventActions.Update);
}
order += 2;
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
}
else
@ -146,7 +150,8 @@ namespace Oqtane.Controllers
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
{
_pageModules.DeletePageModule(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pagemodule.PageModuleId, SyncEventActions.Delete);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
}
else

View File

@ -14,12 +14,14 @@ namespace Oqtane.Controllers
public class ProfileController : Controller
{
private readonly IProfileRepository _profiles;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ProfileController(IProfileRepository profiles, ILogManager logger, ITenantManager tenantManager)
public ProfileController(IProfileRepository profiles, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_profiles = profiles;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -66,6 +68,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && profile.SiteId == _alias.SiteId)
{
profile = _profiles.AddProfile(profile);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Profile, profile.ProfileId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Profile Added {Profile}", profile);
}
else
@ -85,6 +88,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && profile.SiteId == _alias.SiteId && _profiles.GetProfile(profile.ProfileId, false) != null)
{
profile = _profiles.UpdateProfile(profile);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Profile, profile.ProfileId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Profile Updated {Profile}", profile);
}
else
@ -105,6 +109,7 @@ namespace Oqtane.Controllers
if (profile != null && profile.SiteId == _alias.SiteId)
{
_profiles.DeleteProfile(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Profile, profile.ProfileId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Profile Deleted {ProfileId}", id);
}
else

View File

@ -14,12 +14,14 @@ namespace Oqtane.Controllers
public class RoleController : Controller
{
private readonly IRoleRepository _roles;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public RoleController(IRoleRepository roles, ILogManager logger, ITenantManager tenantManager)
public RoleController(IRoleRepository roles, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_roles = roles;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -72,6 +74,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && role.SiteId == _alias.SiteId)
{
role = _roles.AddRole(role);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Role, role.RoleId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Role Added {Role}", role);
}
else
@ -91,6 +94,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && role.SiteId == _alias.SiteId && _roles.GetRole(role.RoleId, false) != null)
{
role = _roles.UpdateRole(role);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Role, role.RoleId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Role Updated {Role}", role);
}
else
@ -111,6 +115,7 @@ namespace Oqtane.Controllers
if (role != null && !role.IsSystem && role.SiteId == _alias.SiteId)
{
_roles.DeleteRole(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Role, role.RoleId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Role Deleted {RoleId}", id);
}
else

View File

@ -98,7 +98,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
setting = _settings.AddSetting(setting);
AddSyncEvent(setting.EntityName);
AddSyncEvent(setting.EntityName, setting.SettingId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting);
}
else
@ -117,7 +117,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
setting = _settings.UpdateSetting(setting);
AddSyncEvent(setting.EntityName);
AddSyncEvent(setting.EntityName, setting.SettingId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting);
}
else
@ -137,7 +137,7 @@ namespace Oqtane.Controllers
if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
_settings.DeleteSetting(setting.EntityName, id);
AddSyncEvent(setting.EntityName);
AddSyncEvent(setting.EntityName, setting.SettingId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting);
}
else
@ -277,14 +277,16 @@ namespace Oqtane.Controllers
return filter;
}
private void AddSyncEvent(string EntityName)
private void AddSyncEvent(string EntityName, int SettingId, string Action)
{
_syncManager.AddSyncEvent(_alias.TenantId, EntityName + "Setting", SettingId, Action);
switch (EntityName)
{
case EntityNames.Module:
case EntityNames.Page:
case EntityNames.Site:
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
break;
}
}

View File

@ -160,6 +160,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid)
{
site = _sites.AddSite(site);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Create);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", site);
}
else
@ -180,12 +181,13 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && current != null)
{
site = _sites.UpdateSite(site);
bool reload = false;
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update);
string action = SyncEventActions.Refresh;
if (current.Runtime != site.Runtime || current.RenderMode != site.RenderMode)
{
reload = true;
action = SyncEventActions.Reload;
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, reload);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, action);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site);
}
else
@ -206,6 +208,7 @@ namespace Oqtane.Controllers
if (site != null && site.SiteId == _alias.SiteId)
{
_sites.DeleteSite(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Delete);
_logger.Log(id, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id);
}
else

View File

@ -2,9 +2,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using System.Collections.Generic;
using Oqtane.Enums;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Repository;
namespace Oqtane.Controllers
@ -13,12 +11,10 @@ namespace Oqtane.Controllers
public class TenantController : Controller
{
private readonly ITenantRepository _tenants;
private readonly ILogManager _logger;
public TenantController(ITenantRepository tenants, ILogManager logger)
public TenantController(ITenantRepository tenants)
{
_tenants = tenants;
_logger = logger;
}
// GET: api/<controller>

View File

@ -14,12 +14,14 @@ namespace Oqtane.Controllers
public class UrlMappingController : Controller
{
private readonly IUrlMappingRepository _urlMappings;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public UrlMappingController(IUrlMappingRepository urlMappings, ILogManager logger, ITenantManager tenantManager)
public UrlMappingController(IUrlMappingRepository urlMappings, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_urlMappings = urlMappings;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
@ -85,6 +87,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && urlMapping.SiteId == _alias.SiteId)
{
urlMapping = _urlMappings.AddUrlMapping(urlMapping);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UrlMapping, urlMapping.UrlMappingId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "UrlMapping Added {UrlMapping}", urlMapping);
}
else
@ -104,6 +107,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && urlMapping.SiteId == _alias.SiteId && _urlMappings.GetUrlMapping(urlMapping.UrlMappingId, false) != null)
{
urlMapping = _urlMappings.UpdateUrlMapping(urlMapping);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UrlMapping, urlMapping.UrlMappingId, SyncEventActions.Update);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "UrlMapping Updated {UrlMapping}", urlMapping);
}
else
@ -124,6 +128,7 @@ namespace Oqtane.Controllers
if (urlMapping != null && urlMapping.SiteId == _alias.SiteId)
{
_urlMappings.DeleteUrlMapping(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UrlMapping, urlMapping.UrlMappingId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "UrlMapping Deleted {UrlMappingId}", id);
}
else

View File

@ -23,7 +23,6 @@ namespace Oqtane.Controllers
public class UserController : Controller
{
private readonly IUserRepository _users;
private readonly IRoleRepository _roles;
private readonly IUserRoleRepository _userRoles;
private readonly UserManager<IdentityUser> _identityUserManager;
private readonly SignInManager<IdentityUser> _identitySignInManager;
@ -35,10 +34,9 @@ namespace Oqtane.Controllers
private readonly IJwtManager _jwtManager;
private readonly ILogManager _logger;
public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, IJwtManager jwtManager, ILogManager logger)
public UserController(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, IJwtManager jwtManager, ILogManager logger)
{
_users = users;
_roles = roles;
_userRoles = userRoles;
_identityUserManager = identityUserManager;
_identitySignInManager = identitySignInManager;
@ -196,6 +194,7 @@ namespace Oqtane.Controllers
user.LastLoginOn = null;
user.LastIPAddress = "";
newUser = _users.AddUser(user);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, newUser.UserId, SyncEventActions.Create);
}
else
{
@ -255,7 +254,8 @@ namespace Oqtane.Controllers
await _identityUserManager.UpdateAsync(identityuser);
}
user = _users.UpdateUser(user);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
}
@ -310,6 +310,7 @@ namespace Oqtane.Controllers
{
// delete user
_users.DeleteUser(user.UserId);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId);
}
else

View File

@ -122,9 +122,10 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && role != null && SiteValid(role.SiteId) && RoleValid(role.Name))
{
userRole = _userRoles.AddUserRole(userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Create);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
}
else
{
@ -144,7 +145,8 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && role != null && SiteValid(role.SiteId) && RoleValid(role.Name) && _userRoles.GetUserRole(userRole.UserRoleId, false) != null)
{
userRole = _userRoles.UpdateUserRole(userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Update);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole);
}
else
@ -165,6 +167,7 @@ namespace Oqtane.Controllers
if (userrole != null && SiteValid(userrole.Role.SiteId) && RoleValid(userrole.Role.Name))
{
_userRoles.DeleteUserRole(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userrole.UserRoleId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
if (userrole.Role.Name == RoleNames.Host)
@ -178,7 +181,7 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userrole);
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userrole.UserId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userrole.UserId, SyncEventActions.Refresh);
}
else
{

View File

@ -280,7 +280,7 @@ namespace Microsoft.Extensions.DependencyInjection
var serviceTypes = assembly.GetTypes(hostedServiceType);
foreach (var serviceType in serviceTypes)
{
if (serviceType.IsSubclassOf(typeof(HostedServiceBase)))
if (!services.Any(item => item.ServiceType == serviceType))
{
services.AddSingleton(hostedServiceType, serviceType);
}

View File

@ -0,0 +1,45 @@
using System.Threading.Tasks;
using System.Threading;
using Microsoft.Extensions.Hosting;
using Oqtane.Models;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public class EventJob : IHostedService
{
private readonly ISyncManager _syncManager;
private readonly IMemoryCache _cache;
public EventJob(ISyncManager syncManager, IMemoryCache cache)
{
_syncManager = syncManager;
_cache = cache;
}
void EntityChanged(object sender, SyncEvent e)
{
if (e.EntityName == "Site" && e.Action == SyncEventActions.Refresh)
{
_cache.Remove($"site:{e.TenantId}:{e.EntityId}");
}
}
public Task StartAsync(CancellationToken cancellationToken)
{
_syncManager.EntityChanged += EntityChanged;
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
_syncManager.EntityChanged -= EntityChanged;
}
}
}

View File

@ -6,8 +6,8 @@ namespace Oqtane.Infrastructure
{
public interface ISyncManager
{
event EventHandler<SyncEvent> EntityChanged;
List<SyncEvent> GetSyncEvents(int tenantId, DateTime lastSyncDate);
void AddSyncEvent(int tenantId, string entityName, int entityId);
void AddSyncEvent(int tenantId, string entityName, int entityId, bool reload);
void AddSyncEvent(int tenantId, string entityName, int entityId, string action);
}
}

View File

@ -1,21 +1,19 @@
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Models;
using Oqtane.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Oqtane.Infrastructure
{
public class SyncManager : ISyncManager
{
private readonly IMemoryCache _cache;
private List<SyncEvent> SyncEvents { get; set; }
public SyncManager(IMemoryCache cache)
public event EventHandler<SyncEvent> EntityChanged;
public SyncManager()
{
_cache = cache;
SyncEvents = new List<SyncEvent>();
}
@ -24,20 +22,22 @@ namespace Oqtane.Infrastructure
return SyncEvents.Where(item => (item.TenantId == tenantId || item.TenantId == -1) && item.ModifiedOn >= lastSyncDate).ToList();
}
public void AddSyncEvent(int tenantId, string entityName, int entityId)
public void AddSyncEvent(int tenantId, string entityName, int entityId, string action)
{
AddSyncEvent(tenantId, entityName, entityId, false);
}
var syncevent = new SyncEvent { TenantId = tenantId, EntityName = entityName, EntityId = entityId, Action = action, ModifiedOn = DateTime.UtcNow };
public void AddSyncEvent(int tenantId, string entityName, int entityId, bool reload)
// client actions for PageState management
if (action == SyncEventActions.Refresh || action == SyncEventActions.Reload)
{
SyncEvents.Add(new SyncEvent { TenantId = tenantId, EntityName = entityName, EntityId = entityId, Reload = reload, ModifiedOn = DateTime.UtcNow });
if (entityName == EntityNames.Site)
{
_cache.Remove($"site:{tenantId}:{entityId}");
}
// trim sync events
SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.UtcNow.AddHours(-1));
// add sync event
SyncEvents.Add(syncevent);
}
// raise event
EntityChanged?.Invoke(this, syncevent);
}
}
}

View File

@ -191,7 +191,7 @@ namespace Oqtane
});
// create a global sync event to identify server application startup
sync.AddSyncEvent(-1, "Application", -1, true);
sync.AddSyncEvent(-1, EntityNames.Host, -1, SyncEventActions.Reload);
}
}
}

View File

@ -9,12 +9,12 @@ namespace Oqtane.Models
public List<SyncEvent> SyncEvents { get; set; }
}
public class SyncEvent
public class SyncEvent : EventArgs
{
public int TenantId { get; set; }
public string EntityName { get; set; }
public int EntityId { get; set; }
public bool Reload { get; set; }
public string Action { get; set; }
public DateTime ModifiedOn { get; set; }
}
}

View File

@ -2,15 +2,25 @@ namespace Oqtane.Shared
{
public class EntityNames
{
public const string Alias = "Alias";
public const string File = "File";
public const string Folder = "Folder";
public const string Job = "Job";
public const string Language = "Language";
public const string Module = "Module";
public const string ModuleDefinition = "ModuleDefinition";
public const string PageModule = "PageModule";
public const string Tenant = "Tenant";
public const string Site = "Site";
public const string Notification = "Notification";
public const string Page = "Page";
public const string Folder = "Folder";
public const string PageModule = "PageModule";
public const string Profile = "Profile";
public const string Role = "Role";
public const string Setting = "Setting";
public const string Site = "Site";
public const string Tenant = "Tenant";
public const string UrlMapping = "UrlMapping";
public const string User = "User";
public const string UserRole = "UserRole";
public const string Visitor = "Visitor";
public const string Host = "Host";
public const string Host = "Host"; // a conceptual entity
}
}

View File

@ -0,0 +1,12 @@
namespace Oqtane.Shared {
public class SyncEventActions {
// client actions for PageState management
public const string Refresh = "Refresh";
public const string Reload = "Reload";
// server actions for raising Events
public const string Create = "Create";
public const string Update = "Update";
public const string Delete = "Delete";
}
}