From 65a3a086cc29886151af7bd895a16f885f4dd121 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Thu, 19 Dec 2024 22:54:19 +0100 Subject: [PATCH 01/19] Improve Boilerplate home page data loading (#9515) --- .../Components/Pages/HomePage.razor | 98 +++++++++---------- .../Components/Pages/HomePage.razor.cs | 18 ++-- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor index b49e8792bf..df148a62f5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor @@ -23,71 +23,71 @@ Statistics - - @if (isLoadingGitHub) + + @if (isLoadingNuget) { - - - + + + } - else if (gitHubStats is not null) + else if (nugetStats is not null) { - - - Name: - @gitHubStats.Name - - - Total stars: - @gitHubStats.StargazersCount.ToString("N0") - - - Forks count: - @gitHubStats.ForksCount.ToString("N0") - - - Repo: - - - + var data = nugetStats.Data[0]; + + + Package id: + @data.Id + + + Version: + @data.Version + + + Total downloads: + @data.TotalDownloads.ToString("N0") + - } else { - GitHub stats could not be loaded. + Nuget stats could not be loaded. } - - @if (isLoadingNuget) + + @if (isLoadingGitHub) { - - - + + + } - else if (nugetStats is not null) + else if (gitHubStats is not null) { - var data = nugetStats.Data[0]; - - - Package id: - @data.Id - - - Version: - @data.Version - - - Total downloads: - @data.TotalDownloads.ToString("N0") + + + Name: + @gitHubStats.Name + + + Total stars: + @gitHubStats.StargazersCount.ToString("N0") + + + Forks count: + @gitHubStats.ForksCount.ToString("N0") + + + Repo: + + + + - } else { - Nuget stats could not be loaded. + GitHub stats could not be loaded. } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs index b2846c7c99..74752535ed 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs @@ -30,31 +30,35 @@ protected override async Task OnInitAsync() // However, the logic in other HTTP message handlers, such as **LoggingDelegatingHandler** and **RetryDelegatingHandler**, // effectively addresses most scenarios. - await Task.WhenAll(LoadGitHub(), LoadNuget()); + await Task.WhenAll(LoadNuget(), LoadGitHub()); } - private async Task LoadGitHub() + private async Task LoadNuget() { try { - gitHubStats = await statisticsController.GetGitHubStats(CurrentCancellationToken); + nugetStats = await statisticsController.GetNugetStats(packageId: "Bit.BlazorUI", CurrentCancellationToken); } finally { - isLoadingGitHub = false; + isLoadingNuget = false; await InvokeAsync(StateHasChanged); } } - private async Task LoadNuget() + private async Task LoadGitHub() { try { - nugetStats = await statisticsController.GetNugetStats(packageId: "Bit.BlazorUI", CurrentCancellationToken); + if (InPrerenderSession is false) // GitHub results (2nd Bit Pivot tab) aren't shown by default and aren't critical for SEO, so skip pre-rendering to save time. + { + gitHubStats = await statisticsController.GetGitHubStats(CurrentCancellationToken); + } } + catch { /* `GetGitHubStats` calls the GitHub API directly from the client. We've intentionally skipped proper exception handling to keep this example simple. */ } finally { - isLoadingNuget = false; + isLoadingGitHub = false; await InvokeAsync(StateHasChanged); } } From db924504e40df2b99403d2254a788e7568ea7322 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 00:09:51 +0100 Subject: [PATCH 02/19] fix --- .../Controllers/AttachmentController.cs | 5 +++++ .../Controllers/Identity/UserController.cs | 9 +++++++-- .../src/Shared/Controllers/Identity/IUserController.cs | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs index f5ec34cbf0..c188328908 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs @@ -101,6 +101,11 @@ public async Task GetProfileImage(CancellationToken cancellationT if (user?.ProfileImageName is null) throw new ResourceNotFoundException(); + var currentETag = user.ConcurrencyStamp; // Let's re-use cache if the image hasn't changed. + if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) + return StatusCode(StatusCodes.Status304NotModified); + Response.Headers["ETag"] = currentETag; + var filePath = $"{AppSettings.UserProfileImagesDir}{user.ProfileImageName}"; if (await blobStorage.ExistsAsync(filePath, cancellationToken) is false) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs index bde3081140..30ad887b32 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs @@ -33,13 +33,18 @@ public partial class UserController : AppControllerBase, IUserController //#endif [HttpGet] - public async Task GetCurrentUser(CancellationToken cancellationToken) + public async Task> GetCurrentUser(CancellationToken cancellationToken) { var userId = User.GetUserId(); var user = await userManager.FindByIdAsync(userId.ToString()) ?? throw new ResourceNotFoundException(); + var currentETag = user.ConcurrencyStamp; // Let's re-use cache if the user hasn't changed. + if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) + return StatusCode(StatusCodes.Status304NotModified); + Response.Headers["ETag"] = currentETag; + return user.Map(); } @@ -94,7 +99,7 @@ public async Task RevokeSession(Guid id, CancellationToken cancellationToken) } [HttpPut] - public async Task Update(EditUserDto userDto, CancellationToken cancellationToken) + public async Task> Update(EditUserDto userDto, CancellationToken cancellationToken) { var userId = User.GetUserId(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs index 9796336df9..dd13706a60 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Shared.Controllers.Identity; public interface IUserController : IAppController { [HttpGet] - Task GetCurrentUser(CancellationToken cancellationToken); + Task GetCurrentUser(CancellationToken cancellationToken) => default!; [HttpGet] Task> GetUserSessions(CancellationToken cancellationToken) => default!; @@ -18,7 +18,7 @@ public interface IUserController : IAppController Task RevokeSession(Guid id, CancellationToken cancellationToken); [HttpPut] - Task Update(EditUserDto userDto, CancellationToken cancellationToken); + Task Update(EditUserDto userDto, CancellationToken cancellationToken) => default!; [HttpPost] Task ChangePassword(ChangePasswordRequestDto request, CancellationToken cancellationToken); From 3431f85c06e7e6d4a3ae99f07e6fbc78002317bd Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 00:52:33 +0100 Subject: [PATCH 03/19] fix --- .../Controllers/AppControllerBase.cs | 16 ++++++++++++++++ .../Controllers/AttachmentController.cs | 5 ----- .../Controllers/Categories/CategoryController.cs | 5 ++++- .../Controllers/Identity/UserController.cs | 9 ++------- .../Categories/ICategoryController.cs | 2 +- .../Controllers/Identity/IUserController.cs | 4 ++-- .../Extensions/ByteArrayExtensions.cs | 0 7 files changed, 25 insertions(+), 16 deletions(-) rename src/Templates/Boilerplate/Bit.Boilerplate/src/{Client/Boilerplate.Client.Core => Shared}/Extensions/ByteArrayExtensions.cs (100%) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs index 0f1711e787..0a11ece42e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs @@ -9,4 +9,20 @@ public partial class AppControllerBase : ControllerBase [AutoInject] protected AppDbContext DbContext = default!; [AutoInject] protected IStringLocalizer Localizer = default!; + + protected bool ValidateETag(string currentETag, out StatusCodeResult? statusCode) + { + statusCode = null; + + if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) + { + statusCode = StatusCode(StatusCodes.Status304NotModified); + return true; + } + + Response.Headers.ETag = currentETag; + Response.Headers.CacheControl = $"public, no-cache, max-age={TimeSpan.FromDays(7).TotalSeconds}"; // no-cache doesn't mean don't cache, it means revalidate. + + return false; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs index c188328908..f5ec34cbf0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs @@ -101,11 +101,6 @@ public async Task GetProfileImage(CancellationToken cancellationT if (user?.ProfileImageName is null) throw new ResourceNotFoundException(); - var currentETag = user.ConcurrencyStamp; // Let's re-use cache if the image hasn't changed. - if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) - return StatusCode(StatusCodes.Status304NotModified); - Response.Headers["ETag"] = currentETag; - var filePath = $"{AppSettings.UserProfileImagesDir}{user.ProfileImageName}"; if (await blobStorage.ExistsAsync(filePath, cancellationToken) is false) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs index ba89bde5e3..4cf1b37be0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs @@ -36,13 +36,16 @@ public async Task> GetCategories(ODataQueryOptions Get(Guid id, CancellationToken cancellationToken) + public async Task> Get(Guid id, CancellationToken cancellationToken) { var dto = await Get().FirstOrDefaultAsync(t => t.Id == id, cancellationToken); if (dto is null) throw new ResourceNotFoundException(Localizer[nameof(AppStrings.CategoryCouldNotBeFound)]); + if (ValidateETag(dto.ConcurrencyStamp.ToStampString()!, out var statusCode)) + return statusCode!; + return dto; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs index 30ad887b32..bde3081140 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs @@ -33,18 +33,13 @@ public partial class UserController : AppControllerBase, IUserController //#endif [HttpGet] - public async Task> GetCurrentUser(CancellationToken cancellationToken) + public async Task GetCurrentUser(CancellationToken cancellationToken) { var userId = User.GetUserId(); var user = await userManager.FindByIdAsync(userId.ToString()) ?? throw new ResourceNotFoundException(); - var currentETag = user.ConcurrencyStamp; // Let's re-use cache if the user hasn't changed. - if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) - return StatusCode(StatusCodes.Status304NotModified); - Response.Headers["ETag"] = currentETag; - return user.Map(); } @@ -99,7 +94,7 @@ public async Task RevokeSession(Guid id, CancellationToken cancellationToken) } [HttpPut] - public async Task> Update(EditUserDto userDto, CancellationToken cancellationToken) + public async Task Update(EditUserDto userDto, CancellationToken cancellationToken) { var userId = User.GetUserId(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs index 2f3e35b1f3..5b56b3f374 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Shared.Controllers.Categories; public interface ICategoryController : IAppController { [HttpGet("{id}")] - Task Get(Guid id, CancellationToken cancellationToken); + Task Get(Guid id, CancellationToken cancellationToken) => default!; [HttpPost] Task Create(CategoryDto dto, CancellationToken cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs index dd13706a60..9796336df9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Identity/IUserController.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Shared.Controllers.Identity; public interface IUserController : IAppController { [HttpGet] - Task GetCurrentUser(CancellationToken cancellationToken) => default!; + Task GetCurrentUser(CancellationToken cancellationToken); [HttpGet] Task> GetUserSessions(CancellationToken cancellationToken) => default!; @@ -18,7 +18,7 @@ public interface IUserController : IAppController Task RevokeSession(Guid id, CancellationToken cancellationToken); [HttpPut] - Task Update(EditUserDto userDto, CancellationToken cancellationToken) => default!; + Task Update(EditUserDto userDto, CancellationToken cancellationToken); [HttpPost] Task ChangePassword(ChangePasswordRequestDto request, CancellationToken cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ByteArrayExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ByteArrayExtensions.cs similarity index 100% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/ByteArrayExtensions.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ByteArrayExtensions.cs From 1ff17eb01f4c1f8f79a0a3a671ec1a2770e26b20 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 01:07:23 +0100 Subject: [PATCH 04/19] fix --- .../Controllers/Categories/CategoryController.cs | 5 +---- .../src/Shared/Controllers/Categories/ICategoryController.cs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs index 4cf1b37be0..ba89bde5e3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Categories/CategoryController.cs @@ -36,16 +36,13 @@ public async Task> GetCategories(ODataQueryOptions> Get(Guid id, CancellationToken cancellationToken) + public async Task Get(Guid id, CancellationToken cancellationToken) { var dto = await Get().FirstOrDefaultAsync(t => t.Id == id, cancellationToken); if (dto is null) throw new ResourceNotFoundException(Localizer[nameof(AppStrings.CategoryCouldNotBeFound)]); - if (ValidateETag(dto.ConcurrencyStamp.ToStampString()!, out var statusCode)) - return statusCode!; - return dto; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs index 5b56b3f374..2f3e35b1f3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Categories/ICategoryController.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Shared.Controllers.Categories; public interface ICategoryController : IAppController { [HttpGet("{id}")] - Task Get(Guid id, CancellationToken cancellationToken) => default!; + Task Get(Guid id, CancellationToken cancellationToken); [HttpPost] Task Create(CategoryDto dto, CancellationToken cancellationToken); From e102478b2d83356f87736f8a94261d826fdccadc Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:12:10 +0100 Subject: [PATCH 05/19] fix --- .../Components/Layout/UserMenu.razor.cs | 5 +++-- .../Authorized/Settings/ProfileSection.razor.cs | 9 ++++++++- .../Authorized/Settings/SettingsPage.razor.cs | 5 ++--- .../Controllers/AppControllerBase.cs | 16 ---------------- .../Controllers/AttachmentController.cs | 10 ++++------ .../Controllers/Identity/IdentityController.cs | 2 ++ .../Controllers/Identity/UserController.cs | 1 + .../src/Shared/Dtos/Identity/UserDto.cs | 2 ++ .../Extensions/ClaimsPrincipalExtensions.cs | 8 ++++++++ .../src/Shared/Services/AuthPolicies.cs | 11 ++++++++++- 10 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs index 1686228ba8..778ebcb385 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs @@ -42,10 +42,11 @@ protected override async Task OnInitAsync() await InvokeAsync(StateHasChanged); }); + var concurrencyStamp = await PrerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.GetConcurrencyStamp()); + userController.AddQueryString("version", concurrencyStamp); user = await userController.GetCurrentUser(CurrentCancellationToken); - var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?version={concurrencyStamp}").ToString(); await base.OnInitAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index 6bc1b0e665..e46cebfc42 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -27,7 +27,12 @@ protected override async Task OnInitAsync() removeProfileImageHttpUrl = $"api/Attachment/RemoveProfileImage?access_token={accessToken}"; - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); + var (userId, concurrencyStamp) = await PrerenderStateService.GetValue(async () => + { + var user = (await AuthenticationStateTask).User; + return (user.GetUserId(), user.GetConcurrencyStamp()); + }); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{userId}&version={concurrencyStamp}").ToString(); profileImageUploadUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/UploadProfileImage?access_token={accessToken}").ToString(); await base.OnInitAsync(); @@ -96,6 +101,8 @@ private async Task HandleOnUploadComplete() try { + var concurrencyStamp = (await AuthenticationStateTask).User.GetConcurrencyStamp(); + userController.AddQueryString("version", concurrencyStamp); var updatedUser = await userController.GetCurrentUser(CurrentCancellationToken); User.ProfileImageName = updatedUser.ProfileImageName; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs index 87eda360e3..d44141dfb5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs @@ -30,10 +30,9 @@ protected override async Task OnInitAsync() try { - user = user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", JsonSerializerOptions.GetTypeInfo(), CurrentCancellationToken)))!; + user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", JsonSerializerOptions.GetTypeInfo(), CurrentCancellationToken)))!; - var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?v={user.ConcurrencyStamp}").ToString(); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs index 0a11ece42e..0f1711e787 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AppControllerBase.cs @@ -9,20 +9,4 @@ public partial class AppControllerBase : ControllerBase [AutoInject] protected AppDbContext DbContext = default!; [AutoInject] protected IStringLocalizer Localizer = default!; - - protected bool ValidateETag(string currentETag, out StatusCodeResult? statusCode) - { - statusCode = null; - - if (Request.Headers.TryGetValue("If-None-Match", out var requestETag) && requestETag == currentETag) - { - statusCode = StatusCode(StatusCodes.Status304NotModified); - return true; - } - - Response.Headers.ETag = currentETag; - Response.Headers.CacheControl = $"public, no-cache, max-age={TimeSpan.FromDays(7).TotalSeconds}"; // no-cache doesn't mean don't cache, it means revalidate. - - return false; - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs index f5ec34cbf0..83cf8ba68c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs @@ -10,8 +10,6 @@ public partial class AttachmentController : AppControllerBase { [AutoInject] private UserManager userManager = default!; - [AutoInject] private IWebHostEnvironment webHostEnvironment = default!; - [AutoInject] private IBlobStorage blobStorage = default!; [HttpPost] @@ -91,11 +89,11 @@ public async Task RemoveProfileImage(CancellationToken cancellationToken) await blobStorage.DeleteAsync(filePath, cancellationToken); } - [HttpGet] - public async Task GetProfileImage(CancellationToken cancellationToken) + [AllowAnonymous] + [HttpGet("{userId}")] + [ResponseCache(Duration = 7 * 24 * 3600, Location = ResponseCacheLocation.Any)] + public async Task GetProfileImage(Guid userId, CancellationToken cancellationToken) { - var userId = User.GetUserId(); - var user = await userManager.FindByIdAsync(userId.ToString()); if (user?.ProfileImageName is null) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs index 3edd135389..3e7b6b285f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs @@ -104,6 +104,7 @@ public async Task SignIn(SignInRequestDto request, CancellationToken cancellatio } userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_ID, userSession.Id.ToString())); + userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.CONCURRENCY_STAMP, user.ConcurrencyStamp!)); userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_STAMP, userSession.StartedOn.ToUnixTimeSeconds().ToString())); if (userSession.Privileged) { @@ -250,6 +251,7 @@ public async Task> Refresh(RefreshRequestDto requ userSession.RenewedOn = DateTimeOffset.UtcNow; userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_ID, currentSessionId.ToString())); + userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.CONCURRENCY_STAMP, user.ConcurrencyStamp!)); userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_STAMP, userSession.RenewedOn.Value.ToUnixTimeSeconds().ToString())); userSession.Privileged = await IsUserSessionPrivileged(userSession, cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs index bde3081140..d5a8449300 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs @@ -33,6 +33,7 @@ public partial class UserController : AppControllerBase, IUserController //#endif [HttpGet] + [ResponseCache(Duration = 7 * 24 * 3600, Location = ResponseCacheLocation.Any)] public async Task GetCurrentUser(CancellationToken cancellationToken) { var userId = User.GetUserId(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs index b19cfc4f7c..8c9a510350 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserDto.cs @@ -33,6 +33,8 @@ public partial class UserDto : IValidatableObject public string? ProfileImageName { get; set; } + public string? ConcurrencyStamp { get; set; } + public string? DisplayName => FullName ?? Email ?? PhoneNumber ?? UserName; public IEnumerable Validate(ValidationContext validationContext) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs index 62719b4959..58aaf8396d 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs @@ -34,4 +34,12 @@ public static Guid GetSessionId(this ClaimsPrincipal claimsPrincipal) { return Guid.Parse(claimsPrincipal.FindFirst(AppClaimTypes.SESSION_ID)!.Value); } + + /// + /// + /// + public static string GetConcurrencyStamp(this ClaimsPrincipal claimsPrincipal) + { + return claimsPrincipal.FindFirst(AppClaimTypes.CONCURRENCY_STAMP)!.Value; + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs index 4129b88afc..4a3fdeb37e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs @@ -1,4 +1,6 @@ -namespace Boilerplate.Shared.Services; +using Boilerplate.Shared.Controllers.Identity; + +namespace Boilerplate.Shared.Services; public class AuthPolicies { @@ -46,4 +48,11 @@ public class AppClaimTypes /// on the maximum number of concurrent privileged sessions of the user. /// public const string SESSION_STAMP = "session_stamp"; + + /// + /// Stores the user's concurrency stamp in the JWT token. + /// This value is included as a query parameter (?version=xxx) when calling or GetProfileImage. + /// The server uses this for accurate caching and ensuring updated data is served. + /// + public const string CONCURRENCY_STAMP = "concurrency_stamp"; } From b417729b34eca1ad9ee956e54a6b1f1cedff589a Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:24:24 +0100 Subject: [PATCH 06/19] fix --- .../Components/Layout/UserMenu.razor.cs | 4 +--- .../Authorized/Settings/ProfileSection.razor.cs | 12 +++--------- .../Controllers/Identity/IdentityController.cs | 2 -- .../Controllers/Identity/UserController.cs | 1 - .../Shared/Extensions/ClaimsPrincipalExtensions.cs | 8 -------- .../src/Shared/Services/AuthPolicies.cs | 7 ------- 6 files changed, 4 insertions(+), 30 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs index 778ebcb385..ab9f50cdc5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs @@ -42,11 +42,9 @@ protected override async Task OnInitAsync() await InvokeAsync(StateHasChanged); }); - var concurrencyStamp = await PrerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.GetConcurrencyStamp()); - userController.AddQueryString("version", concurrencyStamp); user = await userController.GetCurrentUser(CurrentCancellationToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?version={concurrencyStamp}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?version={user.ConcurrencyStamp}").ToString(); await base.OnInitAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index e46cebfc42..db9418bff3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -27,12 +27,6 @@ protected override async Task OnInitAsync() removeProfileImageHttpUrl = $"api/Attachment/RemoveProfileImage?access_token={accessToken}"; - var (userId, concurrencyStamp) = await PrerenderStateService.GetValue(async () => - { - var user = (await AuthenticationStateTask).User; - return (user.GetUserId(), user.GetConcurrencyStamp()); - }); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{userId}&version={concurrencyStamp}").ToString(); profileImageUploadUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/UploadProfileImage?access_token={accessToken}").ToString(); await base.OnInitAsync(); @@ -40,7 +34,9 @@ protected override async Task OnInitAsync() protected override void OnParametersSet() { - User?.Patch(editUserDto); + User!.Patch(editUserDto); + + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User!.Id}?version={User.ConcurrencyStamp}").ToString(); base.OnParametersSet(); } @@ -101,8 +97,6 @@ private async Task HandleOnUploadComplete() try { - var concurrencyStamp = (await AuthenticationStateTask).User.GetConcurrencyStamp(); - userController.AddQueryString("version", concurrencyStamp); var updatedUser = await userController.GetCurrentUser(CurrentCancellationToken); User.ProfileImageName = updatedUser.ProfileImageName; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs index 3e7b6b285f..3edd135389 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs @@ -104,7 +104,6 @@ public async Task SignIn(SignInRequestDto request, CancellationToken cancellatio } userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_ID, userSession.Id.ToString())); - userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.CONCURRENCY_STAMP, user.ConcurrencyStamp!)); userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_STAMP, userSession.StartedOn.ToUnixTimeSeconds().ToString())); if (userSession.Privileged) { @@ -251,7 +250,6 @@ public async Task> Refresh(RefreshRequestDto requ userSession.RenewedOn = DateTimeOffset.UtcNow; userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_ID, currentSessionId.ToString())); - userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.CONCURRENCY_STAMP, user.ConcurrencyStamp!)); userClaimsPrincipalFactory.SessionClaims.Add(new(AppClaimTypes.SESSION_STAMP, userSession.RenewedOn.Value.ToUnixTimeSeconds().ToString())); userSession.Privileged = await IsUserSessionPrivileged(userSession, cancellationToken); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs index d5a8449300..bde3081140 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs @@ -33,7 +33,6 @@ public partial class UserController : AppControllerBase, IUserController //#endif [HttpGet] - [ResponseCache(Duration = 7 * 24 * 3600, Location = ResponseCacheLocation.Any)] public async Task GetCurrentUser(CancellationToken cancellationToken) { var userId = User.GetUserId(); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs index 58aaf8396d..62719b4959 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs @@ -34,12 +34,4 @@ public static Guid GetSessionId(this ClaimsPrincipal claimsPrincipal) { return Guid.Parse(claimsPrincipal.FindFirst(AppClaimTypes.SESSION_ID)!.Value); } - - /// - /// - /// - public static string GetConcurrencyStamp(this ClaimsPrincipal claimsPrincipal) - { - return claimsPrincipal.FindFirst(AppClaimTypes.CONCURRENCY_STAMP)!.Value; - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs index 4a3fdeb37e..d3fb456dd8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs @@ -48,11 +48,4 @@ public class AppClaimTypes /// on the maximum number of concurrent privileged sessions of the user. /// public const string SESSION_STAMP = "session_stamp"; - - /// - /// Stores the user's concurrency stamp in the JWT token. - /// This value is included as a query parameter (?version=xxx) when calling or GetProfileImage. - /// The server uses this for accurate caching and ensuring updated data is served. - /// - public const string CONCURRENCY_STAMP = "concurrency_stamp"; } From 22949bc82e1328ea31a9823127e728d206a9903f Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:26:33 +0100 Subject: [PATCH 07/19] fix --- .../Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs index d3fb456dd8..4129b88afc 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AuthPolicies.cs @@ -1,6 +1,4 @@ -using Boilerplate.Shared.Controllers.Identity; - -namespace Boilerplate.Shared.Services; +namespace Boilerplate.Shared.Services; public class AuthPolicies { From 07db5bb0ea33438831ada49f34a95f06661e444b Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:44:00 +0100 Subject: [PATCH 08/19] fix --- .../Authorized/Settings/ProfileSection.razor | 6 +----- .../Authorized/Settings/ProfileSection.razor.cs | 15 +++++++++------ .../Controllers/AttachmentController.cs | 2 +- .../Statistics/StatisticsController.cs | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor index 6aafb63c6b..eaa7c5e34b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor @@ -1,9 +1,5 @@ @inherits AppComponentBase -@{ - var imageUrl = User?.ProfileImageName is null ? null : $"{profileImageUrl}&file={User.ProfileImageName}"; -} -
@if (Loading) @@ -32,7 +28,7 @@ @if (isUploading is false) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index db9418bff3..8ff389a4f6 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -20,7 +20,6 @@ public partial class ProfileSection private BitFileUpload fileUploadRef = default!; private readonly EditUserDto editUserDto = new(); - protected override async Task OnInitAsync() { var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); @@ -34,11 +33,13 @@ protected override async Task OnInitAsync() protected override void OnParametersSet() { - User!.Patch(editUserDto); - - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User!.Id}?version={User.ConcurrencyStamp}").ToString(); - base.OnParametersSet(); + + if (User is not null) + { + User.Patch(editUserDto); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&version={User.ConcurrencyStamp}").ToString(); + } } @@ -99,7 +100,9 @@ private async Task HandleOnUploadComplete() { var updatedUser = await userController.GetCurrentUser(CurrentCancellationToken); - User.ProfileImageName = updatedUser.ProfileImageName; + updatedUser.Patch(User); + + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&version={User.ConcurrencyStamp}").ToString(); PublishUserDataUpdated(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs index 83cf8ba68c..7a409cf620 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs @@ -91,7 +91,7 @@ public async Task RemoveProfileImage(CancellationToken cancellationToken) [AllowAnonymous] [HttpGet("{userId}")] - [ResponseCache(Duration = 7 * 24 * 3600, Location = ResponseCacheLocation.Any)] + [ResponseCache(Duration = 7 * 24 * 3600, Location = ResponseCacheLocation.Any, VaryByQueryKeys = new string[] { "*" })] public async Task GetProfileImage(Guid userId, CancellationToken cancellationToken) { var user = await userManager.FindByIdAsync(userId.ToString()); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Statistics/StatisticsController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Statistics/StatisticsController.cs index 9cbba4b900..2801488586 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Statistics/StatisticsController.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Statistics/StatisticsController.cs @@ -11,7 +11,7 @@ public partial class StatisticsController : AppControllerBase, IStatisticsContro [AllowAnonymous] [HttpGet("{packageId}")] - [ResponseCache(Duration = 1 * 24 * 3600, Location = ResponseCacheLocation.Any)] + [ResponseCache(Duration = 1 * 24 * 3600, Location = ResponseCacheLocation.Any, VaryByQueryKeys = new string[] { "*" })] public async Task GetNugetStats(string packageId, CancellationToken cancellationToken) { return await nugetHttpClient.GetPackageStats(packageId, cancellationToken); From e750f2e74b0b3109f54ea33c1f9716c9a7168bcd Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:46:40 +0100 Subject: [PATCH 09/19] fix --- .../Components/Layout/UserMenu.razor.cs | 2 +- .../Pages/Authorized/Settings/ProfileSection.razor.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs index ab9f50cdc5..366938c02e 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs @@ -44,7 +44,7 @@ protected override async Task OnInitAsync() user = await userController.GetCurrentUser(CurrentCancellationToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?version={user.ConcurrencyStamp}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{user.Id}?v={user.ConcurrencyStamp}").ToString(); await base.OnInitAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index 8ff389a4f6..f5ccfc3923 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -38,7 +38,7 @@ protected override void OnParametersSet() if (User is not null) { User.Patch(editUserDto); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&version={User.ConcurrencyStamp}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&v={User.ConcurrencyStamp}").ToString(); } } @@ -102,7 +102,7 @@ private async Task HandleOnUploadComplete() updatedUser.Patch(User); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&version={User.ConcurrencyStamp}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage/{User.Id}?file={User.ProfileImageName}&v={User.ConcurrencyStamp}").ToString(); PublishUserDataUpdated(); } From bf7232a2d2e86a05b45e8b1345db4f8a1bd4c964 Mon Sep 17 00:00:00 2001 From: ysmoradi Date: Fri, 20 Dec 2024 02:55:03 +0100 Subject: [PATCH 10/19] fix --- .../Components/Layout/UserMenu.razor | 10 +++------- .../Components/Layout/UserMenu.razor.cs | 2 ++ .../Pages/Authorized/Settings/ProfileSection.razor.cs | 6 +++--- .../Pages/Authorized/Settings/SettingsPage.razor | 4 ---- .../Pages/Authorized/Settings/SettingsPage.razor.cs | 3 --- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor index d6c56d0648..24fb67ed06 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor @@ -1,8 +1,4 @@ -@inherits AppComponentBase - -@{ - var imageUrl = user.ProfileImageName is null ? null : $"{profileImageUrl}&file={user.ProfileImageName}"; -} +@inherits AppComponentBase