From cdc4744f2875c2e9d0fa65eeab819ec1bdb8bede Mon Sep 17 00:00:00 2001 From: ChiaraBm Date: Wed, 26 Feb 2025 13:09:31 +0100 Subject: [PATCH] Added theme saving. Added interfaces for overview pages. Renamed sidebar interface function --- .../Controllers/Admin/Sys/FilesController.cs | 2 +- .../Controllers/Admin/Sys/ThemeController.cs | 24 ++++ .../Http/Controllers/FrontendController.cs | 15 ++- .../Interfaces/Auth/IAuthInterceptor.cs | 9 -- Moonlight.ApiServer/Startup.cs | 22 ---- .../Implementations/CoreStartup.cs | 1 + .../DefaultAdminOverviewElementProvider.cs | 11 ++ .../DefaultOverviewElementProvider.cs | 12 ++ .../DefaultSidebarItemProvider.cs | 111 +++++++++--------- .../Implementations/SysFileSystemProvider.cs | 2 +- .../IAdminOverviewElementProvider.cs | 6 + .../Interfaces/IOverviewElementProvider.cs | 6 + .../Interfaces/ISidebarItemProvider.cs | 2 +- .../Components/WelcomeOverviewElement.razor | 24 ++++ Moonlight.Client/UI/Partials/AppSidebar.razor | 9 +- .../UI/Partials/Design/ThemeSettings.razor | 9 +- Moonlight.Client/UI/Views/Admin/Index.razor | 29 ++++- Moonlight.Client/UI/Views/Index.razor | 36 +++--- .../Requests/Admin/Sys/UpdateThemeRequest.cs | 9 ++ .../{Files => }/FileSystemEntryResponse.cs | 2 +- 20 files changed, 224 insertions(+), 117 deletions(-) create mode 100644 Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs delete mode 100644 Moonlight.ApiServer/Interfaces/Auth/IAuthInterceptor.cs create mode 100644 Moonlight.Client/Implementations/DefaultAdminOverviewElementProvider.cs create mode 100644 Moonlight.Client/Implementations/DefaultOverviewElementProvider.cs create mode 100644 Moonlight.Client/Interfaces/IAdminOverviewElementProvider.cs create mode 100644 Moonlight.Client/Interfaces/IOverviewElementProvider.cs create mode 100644 Moonlight.Client/UI/Components/WelcomeOverviewElement.razor create mode 100644 Moonlight.Shared/Http/Requests/Admin/Sys/UpdateThemeRequest.cs rename Moonlight.Shared/Http/Responses/Admin/Sys/{Files => }/FileSystemEntryResponse.cs (79%) diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/FilesController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/FilesController.cs index 9724e844..2a1fedde 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/FilesController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/FilesController.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc; using MoonCore.Extended.PermFilter; using MoonCore.Helpers; using Moonlight.Shared.Http.Requests.Admin.Sys.Files; -using Moonlight.Shared.Http.Responses.Admin.Sys.Files; +using Moonlight.Shared.Http.Responses.Admin.Sys; namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys; diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs new file mode 100644 index 00000000..81eaa4eb --- /dev/null +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/ThemeController.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; +using MoonCore.Extended.PermFilter; +using MoonCore.Helpers; +using Moonlight.Shared.Http.Requests.Admin.Sys; + +namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys; + +[ApiController] +[Route("api/admin/system/theme")] +public class ThemeController : Controller +{ + [HttpPatch] + [RequirePermission("admin.system.theme.update")] + public async Task Patch([FromBody] UpdateThemeRequest request) + { + var themePath = PathBuilder.File("storage", "theme.json"); + + await System.IO.File.WriteAllTextAsync( + themePath, + JsonSerializer.Serialize(request.Variables) + ); + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/FrontendController.cs b/Moonlight.ApiServer/Http/Controllers/FrontendController.cs index 4247db31..e997862f 100644 --- a/Moonlight.ApiServer/Http/Controllers/FrontendController.cs +++ b/Moonlight.ApiServer/Http/Controllers/FrontendController.cs @@ -1,5 +1,7 @@ +using System.Text.Json; using Microsoft.AspNetCore.Mvc; using MoonCore.Exceptions; +using MoonCore.Helpers; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Services; using Moonlight.Shared.Misc; @@ -25,7 +27,7 @@ public class FrontendController : Controller } [HttpGet("frontend.json")] - public Task GetConfiguration() + public async Task GetConfiguration() { var configuration = new FrontendConfiguration() { @@ -33,13 +35,22 @@ public class FrontendController : Controller ApiUrl = Configuration.PublicUrl, HostEnvironment = "ApiServer" }; + + // Load theme if it exists + var themePath = PathBuilder.File("storage", "theme.json"); + + if (System.IO.File.Exists(themePath)) + { + var variablesJson = await System.IO.File.ReadAllTextAsync(themePath); + configuration.Theme.Variables = JsonSerializer.Deserialize>(variablesJson) ?? new(); + } configuration.Plugins.Entrypoints = PluginService.HostedPluginsManifest.Entrypoints; configuration.Plugins.Assemblies = PluginService.HostedPluginsManifest.Assemblies; configuration.Scripts = AssetService.GetJavascriptAssets(); - return Task.FromResult(configuration); + return configuration; } [HttpGet("plugins/{assemblyName}")] // TODO: Test this diff --git a/Moonlight.ApiServer/Interfaces/Auth/IAuthInterceptor.cs b/Moonlight.ApiServer/Interfaces/Auth/IAuthInterceptor.cs deleted file mode 100644 index e2ace94f..00000000 --- a/Moonlight.ApiServer/Interfaces/Auth/IAuthInterceptor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Moonlight.ApiServer.Database.Entities; - -namespace Moonlight.ApiServer.Interfaces.Auth; - -public interface IAuthInterceptor -{ - public bool AllowAccess(User user, IServiceProvider serviceProvider); - public bool AllowRefresh(User user, IServiceProvider serviceProvider); -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs index b0184c47..6cf4ef89 100644 --- a/Moonlight.ApiServer/Startup.cs +++ b/Moonlight.ApiServer/Startup.cs @@ -17,7 +17,6 @@ using MoonCore.Services; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Helpers; -using Moonlight.ApiServer.Interfaces.Auth; using Moonlight.ApiServer.Interfaces.OAuth2; using Moonlight.ApiServer.Interfaces.Startup; using Moonlight.ApiServer.Services; @@ -248,27 +247,6 @@ public class Startup #endregion - #region Interfaces - - private Task RegisterInterfaces() - { - WebApplicationBuilder.Services.AddInterfaces(configuration => - { - // We use moonlight itself as a plugin assembly - configuration.AddAssembly(typeof(Startup).Assembly); - - configuration.AddAssemblies(AdditionalAssemblies); - configuration.AddAssemblies(PluginLoaderService.PluginAssemblies); - - configuration.AddInterface(); - configuration.AddInterface(); - }); - - return Task.CompletedTask; - } - - #endregion - #region Plugin Loading private async Task LoadPlugins() diff --git a/Moonlight.Client/Implementations/CoreStartup.cs b/Moonlight.Client/Implementations/CoreStartup.cs index c7f59b59..248d45ce 100644 --- a/Moonlight.Client/Implementations/CoreStartup.cs +++ b/Moonlight.Client/Implementations/CoreStartup.cs @@ -8,6 +8,7 @@ public class CoreStartup : IPluginStartup public Task BuildApplication(WebAssemblyHostBuilder builder) { builder.Services.AddSingleton(); + builder.Services.AddSingleton(); return Task.CompletedTask; } diff --git a/Moonlight.Client/Implementations/DefaultAdminOverviewElementProvider.cs b/Moonlight.Client/Implementations/DefaultAdminOverviewElementProvider.cs new file mode 100644 index 00000000..5a81811f --- /dev/null +++ b/Moonlight.Client/Implementations/DefaultAdminOverviewElementProvider.cs @@ -0,0 +1,11 @@ +using Moonlight.Client.Interfaces; + +namespace Moonlight.Client.Implementations; + +public class DefaultAdminOverviewElementProvider : IAdminOverviewElementProvider +{ + public void ModifyOverview(List overviewComponents) + { + + } +} \ No newline at end of file diff --git a/Moonlight.Client/Implementations/DefaultOverviewElementProvider.cs b/Moonlight.Client/Implementations/DefaultOverviewElementProvider.cs new file mode 100644 index 00000000..ddc6bdd2 --- /dev/null +++ b/Moonlight.Client/Implementations/DefaultOverviewElementProvider.cs @@ -0,0 +1,12 @@ +using Moonlight.Client.Interfaces; +using Moonlight.Client.UI.Components; + +namespace Moonlight.Client.Implementations; + +public class DefaultOverviewElementProvider : IOverviewElementProvider +{ + public void ModifyOverview(List overviewComponents) + { + overviewComponents.Add(typeof(WelcomeOverviewElement)); + } +} \ No newline at end of file diff --git a/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs b/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs index 952d008e..0de5fd3b 100644 --- a/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs +++ b/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs @@ -5,61 +5,62 @@ namespace Moonlight.Client.Implementations; public class DefaultSidebarItemProvider : ISidebarItemProvider { - public SidebarItem[] Get() + public void ModifySidebar(List items) { - return - [ - // User - new SidebarItem() - { - Icon = "icon-chart-no-axes-gantt", - Name = "Overview", - Path = "/", - Priority = 0, - RequiresExactMatch = true - }, - - // Admin - new SidebarItem() - { - Icon = "icon-chart-no-axes-gantt", - Name = "Overview", - Group = "Admin", - Path = "/admin", - Priority = 0, - RequiresExactMatch = true, - Permission = "admin.overview" - }, - new SidebarItem() - { - Icon = "icon-users", - Name = "Users", - Group = "Admin", - Path = "/admin/users", - Priority = 1, - RequiresExactMatch = false, - Permission = "admin.users.read" - }, - new SidebarItem() - { - Icon = "icon-key-square", - Name = "API", - Group = "Admin", - Path = "/admin/api", - Priority = 2, - RequiresExactMatch = false, - Permission = "admin.api.read" - }, - new SidebarItem() - { - Icon = "icon-settings", - Name = "System", - Group = "Admin", - Path = "/admin/system", - Priority = 3, - RequiresExactMatch = false, - Permission = "admin.system.overview" - }, - ]; + items.AddRange( + [ + // User + new SidebarItem() + { + Icon = "icon-chart-no-axes-gantt", + Name = "Overview", + Path = "/", + Priority = 0, + RequiresExactMatch = true + }, + + // Admin + new SidebarItem() + { + Icon = "icon-chart-no-axes-gantt", + Name = "Overview", + Group = "Admin", + Path = "/admin", + Priority = 0, + RequiresExactMatch = true, + Permission = "admin.overview" + }, + new SidebarItem() + { + Icon = "icon-users", + Name = "Users", + Group = "Admin", + Path = "/admin/users", + Priority = 1, + RequiresExactMatch = false, + Permission = "admin.users.read" + }, + new SidebarItem() + { + Icon = "icon-key-square", + Name = "API", + Group = "Admin", + Path = "/admin/api", + Priority = 2, + RequiresExactMatch = false, + Permission = "admin.api.read" + }, + new SidebarItem() + { + Icon = "icon-settings", + Name = "System", + Group = "Admin", + Path = "/admin/system", + Priority = 3, + RequiresExactMatch = false, + Permission = "admin.system.overview" + }, + ] + ); } } \ No newline at end of file diff --git a/Moonlight.Client/Implementations/SysFileSystemProvider.cs b/Moonlight.Client/Implementations/SysFileSystemProvider.cs index 0a1454c0..e12ed16e 100644 --- a/Moonlight.Client/Implementations/SysFileSystemProvider.cs +++ b/Moonlight.Client/Implementations/SysFileSystemProvider.cs @@ -2,7 +2,7 @@ using MoonCore.Blazor.Tailwind.Fm.Models; using MoonCore.Helpers; using Moonlight.Shared.Http.Requests.Admin.Sys.Files; -using Moonlight.Shared.Http.Responses.Admin.Sys.Files; +using Moonlight.Shared.Http.Responses.Admin.Sys; namespace Moonlight.Client.Implementations; diff --git a/Moonlight.Client/Interfaces/IAdminOverviewElementProvider.cs b/Moonlight.Client/Interfaces/IAdminOverviewElementProvider.cs new file mode 100644 index 00000000..183866dc --- /dev/null +++ b/Moonlight.Client/Interfaces/IAdminOverviewElementProvider.cs @@ -0,0 +1,6 @@ +namespace Moonlight.Client.Interfaces; + +public interface IAdminOverviewElementProvider +{ + public void ModifyOverview(List overviewComponents); +} \ No newline at end of file diff --git a/Moonlight.Client/Interfaces/IOverviewElementProvider.cs b/Moonlight.Client/Interfaces/IOverviewElementProvider.cs new file mode 100644 index 00000000..05351aa8 --- /dev/null +++ b/Moonlight.Client/Interfaces/IOverviewElementProvider.cs @@ -0,0 +1,6 @@ +namespace Moonlight.Client.Interfaces; + +public interface IOverviewElementProvider +{ + public void ModifyOverview(List overviewComponents); +} \ No newline at end of file diff --git a/Moonlight.Client/Interfaces/ISidebarItemProvider.cs b/Moonlight.Client/Interfaces/ISidebarItemProvider.cs index 088fc356..4c5c7e0c 100644 --- a/Moonlight.Client/Interfaces/ISidebarItemProvider.cs +++ b/Moonlight.Client/Interfaces/ISidebarItemProvider.cs @@ -4,5 +4,5 @@ namespace Moonlight.Client.Interfaces; public interface ISidebarItemProvider { - public SidebarItem[] Get(); + public void ModifySidebar(List items); } \ No newline at end of file diff --git a/Moonlight.Client/UI/Components/WelcomeOverviewElement.razor b/Moonlight.Client/UI/Components/WelcomeOverviewElement.razor new file mode 100644 index 00000000..00c11c74 --- /dev/null +++ b/Moonlight.Client/UI/Components/WelcomeOverviewElement.razor @@ -0,0 +1,24 @@ +@using Microsoft.AspNetCore.Components.Authorization + +
+
+
+ Welcome, @(Username) +
+
What do you want to do today?
+
+
+ +@code +{ + [CascadingParameter] public Task AuthState { get; set; } + + private string Username; + + protected override async Task OnInitializedAsync() + { + var identity = await AuthState; + var usernameClaim = identity.User.Claims.ToArray().First(x => x.Type == "username"); + Username = usernameClaim.Value; + } +} diff --git a/Moonlight.Client/UI/Partials/AppSidebar.razor b/Moonlight.Client/UI/Partials/AppSidebar.razor index 6bae4330..4e1012a3 100644 --- a/Moonlight.Client/UI/Partials/AppSidebar.razor +++ b/Moonlight.Client/UI/Partials/AppSidebar.razor @@ -1,6 +1,5 @@ @using Moonlight.Client.Interfaces @using Moonlight.Client.Models -@using Moonlight.Client.Services @using Moonlight.Client.UI.Layouts @inject NavigationManager Navigation @@ -129,8 +128,12 @@ protected override void OnInitialized() { - Items = SidebarItemProviders - .SelectMany(x => x.Get()) + var sidebarItems = new List(); + + foreach (var provider in SidebarItemProviders) + provider.ModifySidebar(sidebarItems); + + Items = sidebarItems //.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission))) .GroupBy(x => x.Group ?? "") .OrderByDescending(x => string.IsNullOrEmpty(x.Key)) diff --git a/Moonlight.Client/UI/Partials/Design/ThemeSettings.razor b/Moonlight.Client/UI/Partials/Design/ThemeSettings.razor index f17c5091..7f1bded9 100644 --- a/Moonlight.Client/UI/Partials/Design/ThemeSettings.razor +++ b/Moonlight.Client/UI/Partials/Design/ThemeSettings.razor @@ -2,6 +2,7 @@ @using MoonCore.Helpers @using Moonlight.Client.Services @using Moonlight.Client.UI.Components +@using Moonlight.Shared.Http.Requests.Admin.Sys @using Moonlight.Shared.Misc @inject HttpApiClient ApiClient @@ -199,9 +200,13 @@ return; } - await ToastService.Success("Successfully saved theme settings"); + // Send new variables + await ApiClient.Patch("api/admin/system/theme", new UpdateThemeRequest() + { + Variables = ThemeService.Variables + }); - //TODO: Implement saving on the api server + await ToastService.Success("Successfully saved theme settings"); } private async Task Export() diff --git a/Moonlight.Client/UI/Views/Admin/Index.razor b/Moonlight.Client/UI/Views/Admin/Index.razor index 8fa47d90..315a3811 100644 --- a/Moonlight.Client/UI/Views/Admin/Index.razor +++ b/Moonlight.Client/UI/Views/Admin/Index.razor @@ -1,15 +1,34 @@ @page "/admin" -@using Moonlight.Client.Services -@inject DownloadService DownloadService +@using MoonCore.Helpers +@using Moonlight.Client.Interfaces -Test DownloadService +@inject IEnumerable ElementProviders + +
+ @foreach (var render in Renders) + { + @render + } +
@code { - private async Task OnClick(WButton _) + private RenderFragment[] Renders; + + protected override void OnInitialized() { - await DownloadService.DownloadString("test.txt", "Download seems to be working"); + var renders = new List(); + + var elementTypes = new List(); + + foreach (var elementProvider in ElementProviders) + elementProvider.ModifyOverview(elementTypes); + + foreach (var elementType in elementTypes) + renders.Add(ComponentHelper.FromType(elementType)); + + Renders = renders.ToArray(); } } diff --git a/Moonlight.Client/UI/Views/Index.razor b/Moonlight.Client/UI/Views/Index.razor index e430c6cd..9c4506d4 100644 --- a/Moonlight.Client/UI/Views/Index.razor +++ b/Moonlight.Client/UI/Views/Index.razor @@ -1,26 +1,32 @@ @page "/" +@using MoonCore.Helpers +@using Moonlight.Client.Interfaces -@using Microsoft.AspNetCore.Components.Authorization +@inject IEnumerable ElementProviders -
-
- Welcome, @(Username) -
-
What do you want to do today?
+
+ @foreach (var render in Renders) + { + @render + }
-
- @code { - [CascadingParameter] public Task AuthState { get; set; } - - private string Username; + private RenderFragment[] Renders; - protected override async Task OnInitializedAsync() + protected override void OnInitialized() { - var identity = await AuthState; - var usernameClaim = identity.User.Claims.ToArray().First(x => x.Type == "username"); - Username = usernameClaim.Value; + var renders = new List(); + + var elementTypes = new List(); + + foreach (var elementProvider in ElementProviders) + elementProvider.ModifyOverview(elementTypes); + + foreach (var elementType in elementTypes) + renders.Add(ComponentHelper.FromType(elementType)); + + Renders = renders.ToArray(); } } diff --git a/Moonlight.Shared/Http/Requests/Admin/Sys/UpdateThemeRequest.cs b/Moonlight.Shared/Http/Requests/Admin/Sys/UpdateThemeRequest.cs new file mode 100644 index 00000000..ac9b76eb --- /dev/null +++ b/Moonlight.Shared/Http/Requests/Admin/Sys/UpdateThemeRequest.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.Shared.Http.Requests.Admin.Sys; + +public class UpdateThemeRequest +{ + [Required(ErrorMessage = "You need to provide Variables")] + public Dictionary Variables { get; set; } +} \ No newline at end of file diff --git a/Moonlight.Shared/Http/Responses/Admin/Sys/Files/FileSystemEntryResponse.cs b/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs similarity index 79% rename from Moonlight.Shared/Http/Responses/Admin/Sys/Files/FileSystemEntryResponse.cs rename to Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs index 5de853c7..a665a15f 100644 --- a/Moonlight.Shared/Http/Responses/Admin/Sys/Files/FileSystemEntryResponse.cs +++ b/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs @@ -1,4 +1,4 @@ -namespace Moonlight.Shared.Http.Responses.Admin.Sys.Files; +namespace Moonlight.Shared.Http.Responses.Admin.Sys; public class FileSystemEntryResponse {