Added theme saving. Added interfaces for overview pages. Renamed sidebar interface function

This commit is contained in:
2025-02-26 13:09:31 +01:00
parent f4a0aabb61
commit cdc4744f28
20 changed files with 224 additions and 117 deletions

View File

@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
using MoonCore.Extended.PermFilter; using MoonCore.Extended.PermFilter;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Shared.Http.Requests.Admin.Sys.Files; 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; namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;

View File

@@ -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)
);
}
}

View File

@@ -1,5 +1,7 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers;
using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Services; using Moonlight.ApiServer.Services;
using Moonlight.Shared.Misc; using Moonlight.Shared.Misc;
@@ -25,7 +27,7 @@ public class FrontendController : Controller
} }
[HttpGet("frontend.json")] [HttpGet("frontend.json")]
public Task<FrontendConfiguration> GetConfiguration() public async Task<FrontendConfiguration> GetConfiguration()
{ {
var configuration = new FrontendConfiguration() var configuration = new FrontendConfiguration()
{ {
@@ -33,13 +35,22 @@ public class FrontendController : Controller
ApiUrl = Configuration.PublicUrl, ApiUrl = Configuration.PublicUrl,
HostEnvironment = "ApiServer" 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<Dictionary<string, string>>(variablesJson) ?? new();
}
configuration.Plugins.Entrypoints = PluginService.HostedPluginsManifest.Entrypoints; configuration.Plugins.Entrypoints = PluginService.HostedPluginsManifest.Entrypoints;
configuration.Plugins.Assemblies = PluginService.HostedPluginsManifest.Assemblies; configuration.Plugins.Assemblies = PluginService.HostedPluginsManifest.Assemblies;
configuration.Scripts = AssetService.GetJavascriptAssets(); configuration.Scripts = AssetService.GetJavascriptAssets();
return Task.FromResult(configuration); return configuration;
} }
[HttpGet("plugins/{assemblyName}")] // TODO: Test this [HttpGet("plugins/{assemblyName}")] // TODO: Test this

View File

@@ -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);
}

View File

@@ -17,7 +17,6 @@ using MoonCore.Services;
using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Database.Entities;
using Moonlight.ApiServer.Helpers; using Moonlight.ApiServer.Helpers;
using Moonlight.ApiServer.Interfaces.Auth;
using Moonlight.ApiServer.Interfaces.OAuth2; using Moonlight.ApiServer.Interfaces.OAuth2;
using Moonlight.ApiServer.Interfaces.Startup; using Moonlight.ApiServer.Interfaces.Startup;
using Moonlight.ApiServer.Services; using Moonlight.ApiServer.Services;
@@ -248,27 +247,6 @@ public class Startup
#endregion #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<IOAuth2Provider>();
configuration.AddInterface<IAuthInterceptor>();
});
return Task.CompletedTask;
}
#endregion
#region Plugin Loading #region Plugin Loading
private async Task LoadPlugins() private async Task LoadPlugins()

View File

@@ -8,6 +8,7 @@ public class CoreStartup : IPluginStartup
public Task BuildApplication(WebAssemblyHostBuilder builder) public Task BuildApplication(WebAssemblyHostBuilder builder)
{ {
builder.Services.AddSingleton<ISidebarItemProvider, DefaultSidebarItemProvider>(); builder.Services.AddSingleton<ISidebarItemProvider, DefaultSidebarItemProvider>();
builder.Services.AddSingleton<IOverviewElementProvider, DefaultOverviewElementProvider>();
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -0,0 +1,11 @@
using Moonlight.Client.Interfaces;
namespace Moonlight.Client.Implementations;
public class DefaultAdminOverviewElementProvider : IAdminOverviewElementProvider
{
public void ModifyOverview(List<Type> overviewComponents)
{
}
}

View File

@@ -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<Type> overviewComponents)
{
overviewComponents.Add(typeof(WelcomeOverviewElement));
}
}

View File

@@ -5,61 +5,62 @@ namespace Moonlight.Client.Implementations;
public class DefaultSidebarItemProvider : ISidebarItemProvider public class DefaultSidebarItemProvider : ISidebarItemProvider
{ {
public SidebarItem[] Get() public void ModifySidebar(List<SidebarItem> items)
{ {
return items.AddRange(
[ [
// User // User
new SidebarItem() new SidebarItem()
{ {
Icon = "icon-chart-no-axes-gantt", Icon = "icon-chart-no-axes-gantt",
Name = "Overview", Name = "Overview",
Path = "/", Path = "/",
Priority = 0, Priority = 0,
RequiresExactMatch = true RequiresExactMatch = true
}, },
// Admin // Admin
new SidebarItem() new SidebarItem()
{ {
Icon = "icon-chart-no-axes-gantt", Icon = "icon-chart-no-axes-gantt",
Name = "Overview", Name = "Overview",
Group = "Admin", Group = "Admin",
Path = "/admin", Path = "/admin",
Priority = 0, Priority = 0,
RequiresExactMatch = true, RequiresExactMatch = true,
Permission = "admin.overview" Permission = "admin.overview"
}, },
new SidebarItem() new SidebarItem()
{ {
Icon = "icon-users", Icon = "icon-users",
Name = "Users", Name = "Users",
Group = "Admin", Group = "Admin",
Path = "/admin/users", Path = "/admin/users",
Priority = 1, Priority = 1,
RequiresExactMatch = false, RequiresExactMatch = false,
Permission = "admin.users.read" Permission = "admin.users.read"
}, },
new SidebarItem() new SidebarItem()
{ {
Icon = "icon-key-square", Icon = "icon-key-square",
Name = "API", Name = "API",
Group = "Admin", Group = "Admin",
Path = "/admin/api", Path = "/admin/api",
Priority = 2, Priority = 2,
RequiresExactMatch = false, RequiresExactMatch = false,
Permission = "admin.api.read" Permission = "admin.api.read"
}, },
new SidebarItem() new SidebarItem()
{ {
Icon = "icon-settings", Icon = "icon-settings",
Name = "System", Name = "System",
Group = "Admin", Group = "Admin",
Path = "/admin/system", Path = "/admin/system",
Priority = 3, Priority = 3,
RequiresExactMatch = false, RequiresExactMatch = false,
Permission = "admin.system.overview" Permission = "admin.system.overview"
}, },
]; ]
);
} }
} }

View File

@@ -2,7 +2,7 @@
using MoonCore.Blazor.Tailwind.Fm.Models; using MoonCore.Blazor.Tailwind.Fm.Models;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Shared.Http.Requests.Admin.Sys.Files; 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; namespace Moonlight.Client.Implementations;

View File

@@ -0,0 +1,6 @@
namespace Moonlight.Client.Interfaces;
public interface IAdminOverviewElementProvider
{
public void ModifyOverview(List<Type> overviewComponents);
}

View File

@@ -0,0 +1,6 @@
namespace Moonlight.Client.Interfaces;
public interface IOverviewElementProvider
{
public void ModifyOverview(List<Type> overviewComponents);
}

View File

@@ -4,5 +4,5 @@ namespace Moonlight.Client.Interfaces;
public interface ISidebarItemProvider public interface ISidebarItemProvider
{ {
public SidebarItem[] Get(); public void ModifySidebar(List<SidebarItem> items);
} }

View File

@@ -0,0 +1,24 @@
@using Microsoft.AspNetCore.Components.Authorization
<div class="col-span-12 md:col-span-6 xl:col-span-3">
<div class="font-medium leading-[1.1] tracking-tight">
<div class="animate-shimmer bg-gradient-to-r from-violet-400 via-sky-400 to-purple-400 bg-clip-text font-semibold text-transparent text-3xl" style="animation-duration: 5s; background-size: 200% 100%">
Welcome, @(Username)
</div>
<div class="text-gray-200 text-2xl">What do you want to do today?</div>
</div>
</div>
@code
{
[CascadingParameter] public Task<AuthenticationState> 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;
}
}

View File

@@ -1,6 +1,5 @@
@using Moonlight.Client.Interfaces @using Moonlight.Client.Interfaces
@using Moonlight.Client.Models @using Moonlight.Client.Models
@using Moonlight.Client.Services
@using Moonlight.Client.UI.Layouts @using Moonlight.Client.UI.Layouts
@inject NavigationManager Navigation @inject NavigationManager Navigation
@@ -129,8 +128,12 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
Items = SidebarItemProviders var sidebarItems = new List<SidebarItem>();
.SelectMany(x => x.Get())
foreach (var provider in SidebarItemProviders)
provider.ModifySidebar(sidebarItems);
Items = sidebarItems
//.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission))) //.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission)))
.GroupBy(x => x.Group ?? "") .GroupBy(x => x.Group ?? "")
.OrderByDescending(x => string.IsNullOrEmpty(x.Key)) .OrderByDescending(x => string.IsNullOrEmpty(x.Key))

View File

@@ -2,6 +2,7 @@
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Client.Services @using Moonlight.Client.Services
@using Moonlight.Client.UI.Components @using Moonlight.Client.UI.Components
@using Moonlight.Shared.Http.Requests.Admin.Sys
@using Moonlight.Shared.Misc @using Moonlight.Shared.Misc
@inject HttpApiClient ApiClient @inject HttpApiClient ApiClient
@@ -199,9 +200,13 @@
return; 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() private async Task Export()

View File

@@ -1,15 +1,34 @@
@page "/admin" @page "/admin"
@using Moonlight.Client.Services
@inject DownloadService DownloadService @using MoonCore.Helpers
@using Moonlight.Client.Interfaces
<WButton OnClick="OnClick">Test DownloadService</WButton> @inject IEnumerable<IAdminOverviewElementProvider> ElementProviders
<div class="grid grid-cols-12">
@foreach (var render in Renders)
{
@render
}
</div>
@code @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<RenderFragment>();
var elementTypes = new List<Type>();
foreach (var elementProvider in ElementProviders)
elementProvider.ModifyOverview(elementTypes);
foreach (var elementType in elementTypes)
renders.Add(ComponentHelper.FromType(elementType));
Renders = renders.ToArray();
} }
} }

View File

@@ -1,26 +1,32 @@
@page "/" @page "/"
@using MoonCore.Helpers
@using Moonlight.Client.Interfaces
@using Microsoft.AspNetCore.Components.Authorization @inject IEnumerable<IOverviewElementProvider> ElementProviders
<div class="font-medium leading-[1.1] tracking-tight"> <div class="grid grid-cols-12">
<div class="animate-shimmer bg-gradient-to-r from-violet-400 via-sky-400 to-purple-400 bg-clip-text font-semibold text-transparent text-3xl" style="animation-duration: 5s; background-size: 200% 100%"> @foreach (var render in Renders)
Welcome, @(Username) {
</div> @render
<div class="text-gray-200 text-2xl">What do you want to do today?</div> }
</div> </div>
<div class="text-primary-500/10"></div>
@code @code
{ {
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; } private RenderFragment[] Renders;
private string Username;
protected override async Task OnInitializedAsync() protected override void OnInitialized()
{ {
var identity = await AuthState; var renders = new List<RenderFragment>();
var usernameClaim = identity.User.Claims.ToArray().First(x => x.Type == "username");
Username = usernameClaim.Value; var elementTypes = new List<Type>();
foreach (var elementProvider in ElementProviders)
elementProvider.ModifyOverview(elementTypes);
foreach (var elementType in elementTypes)
renders.Add(ComponentHelper.FromType(elementType));
Renders = renders.ToArray();
} }
} }

View File

@@ -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<string, string> Variables { get; set; }
}

View File

@@ -1,4 +1,4 @@
namespace Moonlight.Shared.Http.Responses.Admin.Sys.Files; namespace Moonlight.Shared.Http.Responses.Admin.Sys;
public class FileSystemEntryResponse public class FileSystemEntryResponse
{ {