Merge pull request #5857 from sbwalker/dev

move user workload from siterouter to app component to improve performance and 404 handling
This commit is contained in:
Shaun Walker
2025-12-05 08:40:57 -05:00
committed by GitHub
12 changed files with 91 additions and 39 deletions

View File

@@ -29,8 +29,6 @@ namespace Oqtane.UI
public bool Refresh { get; set; } public bool Refresh { get; set; }
public bool AllowCookies { get; set; } public bool AllowCookies { get; set; }
public int? StatusCode { get; set; }
public List<Page> Pages public List<Page> Pages
{ {
get { return Site?.Pages; } get { return Site?.Pages; }
@@ -65,8 +63,7 @@ namespace Oqtane.UI
IsInternalNavigation = IsInternalNavigation, IsInternalNavigation = IsInternalNavigation,
RenderId = RenderId, RenderId = RenderId,
Refresh = Refresh, Refresh = Refresh,
AllowCookies = AllowCookies, AllowCookies = AllowCookies
StatusCode = StatusCode
}; };
} }
} }

View File

@@ -158,13 +158,17 @@
// verify user is authenticated for current site // verify user is authenticated for current site
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity.IsAuthenticated if (authState.User.IsAuthenticated() && authState.User.SiteKey() == SiteState.Alias.SiteKey)
&& authState.User.Claims.Any(item => item.Type == Constants.SiteKeyClaimType && item.Value == SiteState.Alias.SiteKey)
&& PageState.StatusCode != (int)HttpStatusCode.NotFound)
{ {
// get user if (PageState == null || PageState.User == null || PageState.User.UserId != authState.User.UserId())
var userid = int.Parse(authState.User.Claims.First(item => item.Type == ClaimTypes.NameIdentifier).Value); {
user = await UserService.GetUserAsync(userid, SiteState.Alias.SiteId); // get user
user = await UserService.GetUserAsync(authState.User.UserId(), SiteState.Alias.SiteId);
}
else
{
user = PageState.User;
}
if (user != null) if (user != null)
{ {
user.IsAuthenticated = authState.User.Identity.IsAuthenticated; user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
@@ -339,7 +343,6 @@
IsInternalNavigation = _isInternalNavigation, IsInternalNavigation = _isInternalNavigation,
RenderId = renderid, RenderId = renderid,
Refresh = false, Refresh = false,
StatusCode = PageState?.StatusCode,
AllowCookies = _allowCookies.GetValueOrDefault(true) AllowCookies = _allowCookies.GetValueOrDefault(true)
}; };
OnStateChange?.Invoke(_pagestate); OnStateChange?.Invoke(_pagestate);

View File

@@ -27,3 +27,4 @@
@using Oqtane.Enums @using Oqtane.Enums
@using Oqtane.Installer @using Oqtane.Installer
@using Oqtane.Interfaces @using Oqtane.Interfaces
@using Oqtane.Extensions

View File

@@ -60,7 +60,7 @@
} }
@((MarkupString)_headResources) @((MarkupString)_headResources)
</head> </head>
<body data-enhance-nav="@_enhancedNavigation.ToString().ToLower()"> <body @attributes="_bodyAttributes">
@if (string.IsNullOrEmpty(_message)) @if (string.IsNullOrEmpty(_message))
{ {
@if (_renderMode == RenderModes.Static) @if (_renderMode == RenderModes.Static)
@@ -97,7 +97,7 @@
private string _renderMode = RenderModes.Interactive; private string _renderMode = RenderModes.Interactive;
private string _runtime = Runtimes.Server; private string _runtime = Runtimes.Server;
private bool _prerender = true; private bool _prerender = true;
private bool _enhancedNavigation = true; Dictionary<string, object> _bodyAttributes { get; set; } = new();
private string _fingerprint = ""; private string _fingerprint = "";
private int _visitorId = -1; private int _visitorId = -1;
private string _antiForgeryToken = ""; private string _antiForgeryToken = "";
@@ -142,7 +142,10 @@
_renderMode = site.RenderMode; _renderMode = site.RenderMode;
_runtime = site.Runtime; _runtime = site.Runtime;
_prerender = site.Prerender; _prerender = site.Prerender;
_enhancedNavigation = site.EnhancedNavigation; if (_renderMode == RenderModes.Static && !site.EnhancedNavigation)
{
_bodyAttributes.Add("data-enhance-nav", "false");
}
_fingerprint = site.Fingerprint; _fingerprint = site.Fingerprint;
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty); var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
@@ -234,7 +237,7 @@
Site = site, Site = site,
Page = page, Page = page,
Modules = modules, Modules = modules,
User = null, User = site.User,
Uri = new Uri(url, UriKind.Absolute), Uri = new Uri(url, UriKind.Absolute),
Route = route, Route = route,
QueryString = Utilities.ParseQueryString(route.Query), QueryString = Utilities.ParseQueryString(route.Query),
@@ -251,7 +254,6 @@
IsInternalNavigation = false, IsInternalNavigation = false,
RenderId = Guid.NewGuid(), RenderId = Guid.NewGuid(),
Refresh = true, Refresh = true,
StatusCode = Context.Response.StatusCode,
AllowCookies = _allowCookies AllowCookies = _allowCookies
}; };
} }

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface ISiteNamedOptions<TOptions>
where TOptions : class, new()
{
void Configure(string name, TOptions options, Alias alias, Dictionary<string, string> sitesettings);
}
}

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface ISiteOptions<TOptions>
where TOptions : class, new()
{
void Configure(TOptions options, Alias alias, Dictionary<string, string> sitesettings);
}
}

View File

@@ -4,6 +4,12 @@ using Oqtane.Models;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
{ {
public interface ISiteNamedOptions<TOptions>
where TOptions : class, new()
{
void Configure(string name, TOptions options, Alias alias, Dictionary<string, string> sitesettings);
}
public class SiteNamedOptions<TOptions> : ISiteNamedOptions<TOptions> public class SiteNamedOptions<TOptions> : ISiteNamedOptions<TOptions>
where TOptions : class, new() where TOptions : class, new()
{ {

View File

@@ -4,6 +4,12 @@ using Oqtane.Models;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
{ {
public interface ISiteOptions<TOptions>
where TOptions : class, new()
{
void Configure(TOptions options, Alias alias, Dictionary<string, string> sitesettings);
}
public class SiteOptions<TOptions> : ISiteOptions<TOptions> public class SiteOptions<TOptions> : ISiteOptions<TOptions>
where TOptions : class, new() where TOptions : class, new()
{ {

View File

@@ -13,6 +13,7 @@ using Oqtane.Enums;
using Oqtane.Shared; using Oqtane.Shared;
using System.Globalization; using System.Globalization;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Managers;
namespace Oqtane.Services namespace Oqtane.Services
{ {
@@ -25,6 +26,7 @@ namespace Oqtane.Services
private readonly IPageModuleRepository _pageModules; private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ILanguageRepository _languages; private readonly ILanguageRepository _languages;
private readonly IUserManager _userManager;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly ITenantManager _tenantManager; private readonly ITenantManager _tenantManager;
@@ -35,7 +37,7 @@ namespace Oqtane.Services
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly string _private = "[PRIVATE]"; private readonly string _private = "[PRIVATE]";
public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, IConfigManager configManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor) public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserManager userManager, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, IConfigManager configManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor)
{ {
_sites = sites; _sites = sites;
_pages = pages; _pages = pages;
@@ -43,6 +45,7 @@ namespace Oqtane.Services
_pageModules = pageModules; _pageModules = pageModules;
_moduleDefinitions = moduleDefinitions; _moduleDefinitions = moduleDefinitions;
_languages = languages; _languages = languages;
_userManager = userManager;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_settings = settings; _settings = settings;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@@ -146,6 +149,12 @@ namespace Oqtane.Services
// themes // themes
site.Themes = _themes.FilterThemes(_themes.GetThemes(site.SiteId).ToList()); site.Themes = _themes.FilterThemes(_themes.GetThemes(site.SiteId).ToList());
// user
if (_accessor.HttpContext.User.IsAuthenticated())
{
site.User = _userManager.GetUser(_accessor.HttpContext.User.UserId(), site.SiteId);
}
// installation date used for fingerprinting static assets // installation date used for fingerprinting static assets
site.Fingerprint = Utilities.GenerateSimpleHash(_configManager.GetSetting("InstallationDate", DateTime.UtcNow.ToString("yyyyMMddHHmm"))); site.Fingerprint = Utilities.GenerateSimpleHash(_configManager.GetSetting("InstallationDate", DateTime.UtcNow.ToString("yyyyMMddHHmm")));

View File

@@ -8,6 +8,18 @@ namespace Oqtane.Extensions
{ {
// extension methods cannot be properties - the methods below must include a () suffix when referenced // extension methods cannot be properties - the methods below must include a () suffix when referenced
public static bool IsAuthenticated(this ClaimsPrincipal claimsPrincipal)
{
if (claimsPrincipal.Identity != null)
{
return claimsPrincipal.Identity.IsAuthenticated;
}
else
{
return false;
}
}
public static string Username(this ClaimsPrincipal claimsPrincipal) public static string Username(this ClaimsPrincipal claimsPrincipal)
{ {
if (claimsPrincipal.HasClaim(item => item.Type == ClaimTypes.Name)) if (claimsPrincipal.HasClaim(item => item.Type == ClaimTypes.Name))

View File

@@ -192,6 +192,12 @@ namespace Oqtane.Models
[NotMapped] [NotMapped]
public List<Theme> Themes { get; set; } public List<Theme> Themes { get; set; }
/// <summary>
/// Current user
/// </summary>
[NotMapped]
public User User { get; set; }
/// <summary> /// <summary>
/// fingerprint for framework static assets /// fingerprint for framework static assets
/// </summary> /// </summary>
@@ -246,6 +252,7 @@ namespace Oqtane.Models
Pages = Pages.ConvertAll(page => page.Clone()), Pages = Pages.ConvertAll(page => page.Clone()),
Languages = Languages.ConvertAll(language => language.Clone()), Languages = Languages.ConvertAll(language => language.Clone()),
Themes = Themes, Themes = Themes,
User = User.Clone(),
Fingerprint = Fingerprint, Fingerprint = Fingerprint,
TenantId = TenantId TenantId = TenantId
}; };

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Xml.Linq;
namespace Oqtane.Models namespace Oqtane.Models
{ {
@@ -128,5 +130,34 @@ namespace Oqtane.Models
/// </summary> /// </summary>
[NotMapped] [NotMapped]
public Dictionary<string, string> Settings { get; set; } public Dictionary<string, string> Settings { get; set; }
public User Clone()
{
return new User
{
UserId = UserId,
Username = Username,
DisplayName = DisplayName,
Email = Email,
TimeZoneId = TimeZoneId,
PhotoFileId = PhotoFileId,
LastLoginOn = LastLoginOn,
LastIPAddress = LastIPAddress,
TwoFactorRequired = TwoFactorRequired,
TwoFactorCode = TwoFactorCode,
TwoFactorExpiry = TwoFactorExpiry,
SecurityStamp = SecurityStamp,
SiteId = SiteId,
Roles = Roles,
DeletedBy = DeletedBy,
DeletedOn = DeletedOn,
IsDeleted = IsDeleted,
Password = Password,
IsAuthenticated = IsAuthenticated,
EmailConfirmed = EmailConfirmed,
SuppressNotification = SuppressNotification,
Settings = Settings.ToDictionary(setting => setting.Key, setting => setting.Value)
};
}
} }
} }