Compare commits
12 Commits
d7b725f541
...
v2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dff8c8f6d | |||
| 95a848e571 | |||
| 9d557eea4e | |||
| 94c1aac0ac | |||
| 3bddd64d91 | |||
| 5ad7a6db7b | |||
| 9b9272cd6e | |||
| 31cf34ed04 | |||
| a9b0020131 | |||
| e3b432aae6 | |||
| 06f27605ba | |||
| 0bd138df63 |
@@ -34,7 +34,7 @@ jobs:
|
|||||||
# Publish frontend
|
# Publish frontend
|
||||||
# We need to build it first so the class list files generate
|
# We need to build it first so the class list files generate
|
||||||
- name: Build project
|
- name: Build project
|
||||||
run: dotnet build Moonlight.Frontend --configuration Debug
|
run: dotnet build Hosts/Moonlight.Frontend.Host --configuration Debug
|
||||||
|
|
||||||
- name: Build tailwind styles and extract class list
|
- name: Build tailwind styles and extract class list
|
||||||
working-directory: Hosts/Moonlight.Frontend.Host/Styles
|
working-directory: Hosts/Moonlight.Frontend.Host/Styles
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
|
|
||||||
@import "../../../Moonlight.Frontend/bin/ShadcnBlazor/scrollbar.css";
|
@import "../bin/ShadcnBlazor/scrollbar.css";
|
||||||
@import "../../../Moonlight.Frontend/bin/ShadcnBlazor/default-theme.css";
|
@import "../bin/ShadcnBlazor/default-theme.css";
|
||||||
@import "./theme.css";
|
@import "./theme.css";
|
||||||
|
|
||||||
@source "../../../Moonlight.Frontend/bin/ShadcnBlazor/ShadcnBlazor.map";
|
@source "../bin/ShadcnBlazor/ShadcnBlazor.map";
|
||||||
|
|
||||||
@source "../../../Moonlight.Api/**/*.razor";
|
@source "../../../Moonlight.Api/**/*.razor";
|
||||||
@source "../../../Moonlight.Api/**/*.cs";
|
@source "../../../Moonlight.Api/**/*.cs";
|
||||||
|
|||||||
6
Moonlight.Api/Constants/FrontendSettingConstants.cs
Normal file
6
Moonlight.Api/Constants/FrontendSettingConstants.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Api.Constants;
|
||||||
|
|
||||||
|
public class FrontendSettingConstants
|
||||||
|
{
|
||||||
|
public const string Name = "Moonlight.Frontend.Name";
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moonlight.Api.Constants;
|
||||||
|
using Moonlight.Api.Services;
|
||||||
|
using Moonlight.Shared;
|
||||||
|
using Moonlight.Shared.Http.Requests.Admin.Settings;
|
||||||
|
using Moonlight.Shared.Http.Responses.Admin.Settings;
|
||||||
|
|
||||||
|
namespace Moonlight.Api.Http.Controllers.Admin.Settings;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize(Policy = Permissions.System.Settings)]
|
||||||
|
[Route("api/admin/system/settings/whiteLabeling")]
|
||||||
|
public class WhiteLabelingController : Controller
|
||||||
|
{
|
||||||
|
private readonly SettingsService SettingsService;
|
||||||
|
private readonly FrontendService FrontendService;
|
||||||
|
|
||||||
|
public WhiteLabelingController(SettingsService settingsService, FrontendService frontendService)
|
||||||
|
{
|
||||||
|
SettingsService = settingsService;
|
||||||
|
FrontendService = frontendService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<WhiteLabelingDto>> GetAsync()
|
||||||
|
{
|
||||||
|
var dto = new WhiteLabelingDto
|
||||||
|
{
|
||||||
|
Name = await SettingsService.GetValueAsync<string>(FrontendSettingConstants.Name) ?? "Moonlight"
|
||||||
|
};
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<WhiteLabelingDto>> PostAsync([FromBody] SetWhiteLabelingDto request)
|
||||||
|
{
|
||||||
|
await SettingsService.SetValueAsync(FrontendSettingConstants.Name, request.Name);
|
||||||
|
await FrontendService.ResetCacheAsync();
|
||||||
|
|
||||||
|
var dto = new WhiteLabelingDto
|
||||||
|
{
|
||||||
|
Name = await SettingsService.GetValueAsync<string>(FrontendSettingConstants.Name) ?? "Moonlight"
|
||||||
|
};
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,21 +9,9 @@ namespace Moonlight.Api.Http.Services.ContainerHelper;
|
|||||||
[JsonSerializable(typeof(ProblemDetails))]
|
[JsonSerializable(typeof(ProblemDetails))]
|
||||||
[JsonSerializable(typeof(RebuildEventDto))]
|
[JsonSerializable(typeof(RebuildEventDto))]
|
||||||
[JsonSerializable(typeof(RequestRebuildDto))]
|
[JsonSerializable(typeof(RequestRebuildDto))]
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
|
||||||
public partial class SerializationContext : JsonSerializerContext
|
public partial class SerializationContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
private static JsonSerializerOptions? InternalTunedOptions;
|
|
||||||
|
|
||||||
public static JsonSerializerOptions TunedOptions
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (InternalTunedOptions != null)
|
|
||||||
return InternalTunedOptions;
|
|
||||||
|
|
||||||
InternalTunedOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
|
||||||
InternalTunedOptions.TypeInfoResolverChain.Add(Default);
|
|
||||||
|
|
||||||
return InternalTunedOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Json;
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Moonlight.Api.Http.Services.ContainerHelper;
|
using Moonlight.Api.Http.Services.ContainerHelper;
|
||||||
using Moonlight.Api.Http.Services.ContainerHelper.Requests;
|
using Moonlight.Api.Http.Services.ContainerHelper.Requests;
|
||||||
@@ -42,7 +41,7 @@ public class ContainerHelperService
|
|||||||
request.Content = JsonContent.Create(
|
request.Content = JsonContent.Create(
|
||||||
new RequestRebuildDto(noBuildCache),
|
new RequestRebuildDto(noBuildCache),
|
||||||
null,
|
null,
|
||||||
SerializationContext.TunedOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
var response = await client.SendAsync(
|
var response = await client.SendAsync(
|
||||||
@@ -77,7 +76,7 @@ public class ContainerHelperService
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var data = line.Trim("data: ");
|
var data = line.Trim("data: ");
|
||||||
var deserializedData = JsonSerializer.Deserialize<RebuildEventDto>(data, SerializationContext.TunedOptions);
|
var deserializedData = JsonSerializer.Deserialize<RebuildEventDto>(data, SerializationContext.Default.Options);
|
||||||
|
|
||||||
yield return deserializedData;
|
yield return deserializedData;
|
||||||
|
|
||||||
@@ -100,14 +99,14 @@ public class ContainerHelperService
|
|||||||
var response = await client.PostAsJsonAsync(
|
var response = await client.PostAsJsonAsync(
|
||||||
"api/configuration/version",
|
"api/configuration/version",
|
||||||
new SetVersionDto(version),
|
new SetVersionDto(version),
|
||||||
SerializationContext.TunedOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var problemDetails =
|
var problemDetails =
|
||||||
await response.Content.ReadFromJsonAsync<ProblemDetails>(SerializationContext.TunedOptions);
|
await response.Content.ReadFromJsonAsync<ProblemDetails>(SerializationContext.Default.Options);
|
||||||
|
|
||||||
if (problemDetails == null)
|
if (problemDetails == null)
|
||||||
throw new HttpRequestException($"Failed to set version: {response.ReasonPhrase}");
|
throw new HttpRequestException($"Failed to set version: {response.ReasonPhrase}");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Moonlight.Api.Configuration;
|
using Moonlight.Api.Configuration;
|
||||||
|
using Moonlight.Api.Constants;
|
||||||
using Moonlight.Api.Database;
|
using Moonlight.Api.Database;
|
||||||
using Moonlight.Api.Database.Entities;
|
using Moonlight.Api.Database.Entities;
|
||||||
using Moonlight.Api.Models;
|
using Moonlight.Api.Models;
|
||||||
@@ -13,14 +14,16 @@ public class FrontendService
|
|||||||
private readonly IMemoryCache Cache;
|
private readonly IMemoryCache Cache;
|
||||||
private readonly DatabaseRepository<Theme> ThemeRepository;
|
private readonly DatabaseRepository<Theme> ThemeRepository;
|
||||||
private readonly IOptions<FrontendOptions> Options;
|
private readonly IOptions<FrontendOptions> Options;
|
||||||
|
private readonly SettingsService SettingsService;
|
||||||
|
|
||||||
private const string CacheKey = $"Moonlight.{nameof(FrontendService)}.{nameof(GetConfigurationAsync)}";
|
private const string CacheKey = $"Moonlight.{nameof(FrontendService)}.{nameof(GetConfigurationAsync)}";
|
||||||
|
|
||||||
public FrontendService(IMemoryCache cache, DatabaseRepository<Theme> themeRepository, IOptions<FrontendOptions> options)
|
public FrontendService(IMemoryCache cache, DatabaseRepository<Theme> themeRepository, IOptions<FrontendOptions> options, SettingsService settingsService)
|
||||||
{
|
{
|
||||||
Cache = cache;
|
Cache = cache;
|
||||||
ThemeRepository = themeRepository;
|
ThemeRepository = themeRepository;
|
||||||
Options = options;
|
Options = options;
|
||||||
|
SettingsService = settingsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FrontendConfiguration> GetConfigurationAsync()
|
public async Task<FrontendConfiguration> GetConfigurationAsync()
|
||||||
@@ -35,7 +38,9 @@ public class FrontendService
|
|||||||
.Query()
|
.Query()
|
||||||
.FirstOrDefaultAsync(x => x.IsEnabled);
|
.FirstOrDefaultAsync(x => x.IsEnabled);
|
||||||
|
|
||||||
var config = new FrontendConfiguration("Moonlight", theme?.CssContent);
|
var name = await SettingsService.GetValueAsync<string>(FrontendSettingConstants.Name);
|
||||||
|
|
||||||
|
var config = new FrontendConfiguration(name ?? "Moonlight", theme?.CssContent);
|
||||||
|
|
||||||
Cache.Set(CacheKey, config, TimeSpan.FromMinutes(Options.Value.CacheMinutes));
|
Cache.Set(CacheKey, config, TimeSpan.FromMinutes(Options.Value.CacheMinutes));
|
||||||
|
|
||||||
|
|||||||
26
Moonlight.Frontend/Configuration/LayoutMiddlewareOptions.cs
Normal file
26
Moonlight.Frontend/Configuration/LayoutMiddlewareOptions.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Moonlight.Frontend.Interfaces;
|
||||||
|
|
||||||
|
namespace Moonlight.Frontend.Configuration;
|
||||||
|
|
||||||
|
public class LayoutMiddlewareOptions
|
||||||
|
{
|
||||||
|
public IReadOnlyList<Type> Components => InnerComponents;
|
||||||
|
|
||||||
|
private readonly List<Type> InnerComponents = new();
|
||||||
|
|
||||||
|
public void Add<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>() where T : LayoutMiddlewareBase
|
||||||
|
{
|
||||||
|
InnerComponents.Add(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(int index) where T : LayoutMiddlewareBase
|
||||||
|
{
|
||||||
|
InnerComponents.Insert(index, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>() where T : LayoutMiddlewareBase
|
||||||
|
{
|
||||||
|
InnerComponents.Remove(typeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Moonlight.Frontend/Configuration/LayoutPageOptions.cs
Normal file
33
Moonlight.Frontend/Configuration/LayoutPageOptions.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Moonlight.Frontend.Configuration;
|
||||||
|
|
||||||
|
public class LayoutPageOptions
|
||||||
|
{
|
||||||
|
public IReadOnlyList<LayoutPageComponent> Components => InnerComponents;
|
||||||
|
|
||||||
|
private readonly List<LayoutPageComponent> InnerComponents = new();
|
||||||
|
|
||||||
|
public void Add<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(LayoutPageSlot slot, int order)
|
||||||
|
where T : ComponentBase
|
||||||
|
=> Add(typeof(T), slot, order);
|
||||||
|
|
||||||
|
public void Add(Type componentType, LayoutPageSlot slot, int order)
|
||||||
|
=> InnerComponents.Add(new LayoutPageComponent(componentType, order, slot));
|
||||||
|
|
||||||
|
public void Remove<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()
|
||||||
|
where T : ComponentBase
|
||||||
|
=> Remove(typeof(T));
|
||||||
|
|
||||||
|
public void Remove(Type componentType)
|
||||||
|
=> InnerComponents.RemoveAll(x => x.ComponentType == componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record LayoutPageComponent(Type ComponentType, int Order, LayoutPageSlot Slot);
|
||||||
|
|
||||||
|
public enum LayoutPageSlot
|
||||||
|
{
|
||||||
|
Header = 0,
|
||||||
|
Footer = 1
|
||||||
|
}
|
||||||
35
Moonlight.Frontend/Configuration/SystemSettingsOptions.cs
Normal file
35
Moonlight.Frontend/Configuration/SystemSettingsOptions.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Moonlight.Frontend.Configuration;
|
||||||
|
|
||||||
|
public class SystemSettingsOptions
|
||||||
|
{
|
||||||
|
public IReadOnlyList<SystemSettingsPage> Components => InnerComponents;
|
||||||
|
|
||||||
|
private readonly List<SystemSettingsPage> InnerComponents = new();
|
||||||
|
|
||||||
|
public void Add<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TIcon,
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TComponent>(string name, string description,
|
||||||
|
int order)
|
||||||
|
where TIcon : ComponentBase where TComponent : ComponentBase
|
||||||
|
=> Add(name, description, order, typeof(TIcon), typeof(TComponent));
|
||||||
|
|
||||||
|
public void Add(string name, string description, int order, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type iconComponent, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type component)
|
||||||
|
=> InnerComponents.Add(new SystemSettingsPage(name, description, order, iconComponent, component));
|
||||||
|
|
||||||
|
public void Remove<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TComponent>()
|
||||||
|
where TComponent : ComponentBase
|
||||||
|
=> Remove(typeof(TComponent));
|
||||||
|
|
||||||
|
public void Remove([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType)
|
||||||
|
=> InnerComponents.RemoveAll(x => x.ComponentType == componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SystemSettingsPage(
|
||||||
|
string Name,
|
||||||
|
string Description,
|
||||||
|
int Order,
|
||||||
|
Type IconComponentType,
|
||||||
|
Type ComponentType
|
||||||
|
);
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using Moonlight.Shared.Http;
|
|
||||||
|
|
||||||
namespace Moonlight.Frontend;
|
|
||||||
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public static JsonSerializerOptions SerializerOptions
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (InternalOptions != null)
|
|
||||||
return InternalOptions;
|
|
||||||
|
|
||||||
InternalOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add source generated options from shared project
|
|
||||||
InternalOptions.TypeInfoResolverChain.Add(SerializationContext.Default);
|
|
||||||
|
|
||||||
return InternalOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonSerializerOptions? InternalOptions;
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,7 @@ public sealed class PermissionProvider : IPermissionProvider
|
|||||||
new Permission(Permissions.System.Diagnose, "Diagnose", "Run diagnostics"),
|
new Permission(Permissions.System.Diagnose, "Diagnose", "Run diagnostics"),
|
||||||
new Permission(Permissions.System.Versions, "Versions", "Look at the available versions"),
|
new Permission(Permissions.System.Versions, "Versions", "Look at the available versions"),
|
||||||
new Permission(Permissions.System.Instance, "Instance", "Update the moonlight instance and add plugins"),
|
new Permission(Permissions.System.Instance, "Instance", "Update the moonlight instance and add plugins"),
|
||||||
|
new Permission(Permissions.System.Settings, "Settings", "Change settings of the instance"),
|
||||||
]),
|
]),
|
||||||
new PermissionCategory("API Keys", typeof(KeyIcon), [
|
new PermissionCategory("API Keys", typeof(KeyIcon), [
|
||||||
new Permission(Permissions.ApiKeys.Create, "Create", "Create new API keys"),
|
new Permission(Permissions.ApiKeys.Create, "Create", "Create new API keys"),
|
||||||
|
|||||||
8
Moonlight.Frontend/Interfaces/LayoutMiddlewareBase.cs
Normal file
8
Moonlight.Frontend/Interfaces/LayoutMiddlewareBase.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Moonlight.Frontend.Interfaces;
|
||||||
|
|
||||||
|
public abstract class LayoutMiddlewareBase : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||||
|
}
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.1"/>
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.1"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1"/>
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1"/>
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.3.1-next.0"/>
|
<PackageReference Include="Riok.Mapperly" Version="4.3.1-next.0"/>
|
||||||
<PackageReference Include="ShadcnBlazor" Version="1.0.11" />
|
<PackageReference Include="ShadcnBlazor" Version="1.0.13" />
|
||||||
<PackageReference Include="ShadcnBlazor.Extras" Version="1.0.11" />
|
<PackageReference Include="ShadcnBlazor.Extras" Version="1.0.13" />
|
||||||
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2" />
|
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -36,5 +36,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Styles/*" Pack="true" PackagePath="Styles/" />
|
<None Include="Styles/*" Pack="true" PackagePath="Styles/" />
|
||||||
<None Include="Moonlight.Frontend.targets" Pack="true" PackagePath="build\Moonlight.Frontend.targets" />
|
<None Include="Moonlight.Frontend.targets" Pack="true" PackagePath="build\Moonlight.Frontend.targets" />
|
||||||
|
<None Include="Moonlight.Frontend.targets" Pack="true" PackagePath="buildTransitive\Moonlight.Frontend.targets" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
</MoonlightCssClassDir>
|
</MoonlightCssClassDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="CopyContents" BeforeTargets="Build">
|
<Target Name="Moonlight_CopyContents" BeforeTargets="Build">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Files Include="$(MSBuildThisFileDirectory)..\Styles\**\*" />
|
<Files Include="$(MSBuildThisFileDirectory)..\Styles\**\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ namespace Moonlight.Frontend;
|
|||||||
|
|
||||||
public abstract class MoonlightPlugin : IPluginModule
|
public abstract class MoonlightPlugin : IPluginModule
|
||||||
{
|
{
|
||||||
|
protected MoonlightPlugin[] Plugins { get; private set; }
|
||||||
|
|
||||||
|
public void Initialize(MoonlightPlugin[] plugins)
|
||||||
|
{
|
||||||
|
Plugins = plugins;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void PreBuild(WebAssemblyHostBuilder builder){}
|
public virtual void PreBuild(WebAssemblyHostBuilder builder){}
|
||||||
public virtual void PostBuild(WebAssemblyHost application){}
|
public virtual void PostBuild(WebAssemblyHost application){}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ using System.Net.Http.Json;
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.VisualBasic;
|
||||||
|
using Moonlight.Shared.Http;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Auth;
|
using Moonlight.Shared.Http.Responses.Admin.Auth;
|
||||||
|
|
||||||
namespace Moonlight.Frontend.Services;
|
namespace Moonlight.Frontend.Services;
|
||||||
@@ -23,7 +25,7 @@ public class RemoteAuthProvider : AuthenticationStateProvider
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var claimResponses = await HttpClient.GetFromJsonAsync<ClaimDto[]>(
|
var claimResponses = await HttpClient.GetFromJsonAsync<ClaimDto[]>(
|
||||||
"api/auth/claims", Constants.SerializerOptions
|
"api/auth/claims", SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
var claims = claimResponses!.Select(claim => new Claim(claim.Type, claim.Value));
|
var claims = claimResponses!.Select(claim => new Claim(claim.Type, claim.Value));
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
|
||||||
using SimplePlugin.Abstractions;
|
|
||||||
|
|
||||||
namespace Moonlight.Frontend.Startup;
|
|
||||||
|
|
||||||
public interface IAppStartup : IPluginModule
|
|
||||||
{
|
|
||||||
public void PreBuild(WebAssemblyHostBuilder builder);
|
|
||||||
public void PostBuild(WebAssemblyHost application);
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using LucideBlazor;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -6,6 +7,7 @@ using Moonlight.Frontend.Implementations;
|
|||||||
using Moonlight.Frontend.Interfaces;
|
using Moonlight.Frontend.Interfaces;
|
||||||
using Moonlight.Frontend.Services;
|
using Moonlight.Frontend.Services;
|
||||||
using Moonlight.Frontend.UI;
|
using Moonlight.Frontend.UI;
|
||||||
|
using Moonlight.Frontend.UI.Admin.Settings;
|
||||||
using ShadcnBlazor;
|
using ShadcnBlazor;
|
||||||
using ShadcnBlazor.Extras;
|
using ShadcnBlazor.Extras;
|
||||||
|
|
||||||
@@ -31,5 +33,14 @@ public partial class Startup
|
|||||||
{
|
{
|
||||||
options.Assemblies.Add(typeof(Startup).Assembly);
|
options.Assemblies.Add(typeof(Startup).Assembly);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.Configure<SystemSettingsOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Add<TextCursorInputIcon, WhiteLabelingSetting>(
|
||||||
|
"White Labeling",
|
||||||
|
"Settings for white labeling your moonlight instance",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,17 @@ public static class StartupHandler
|
|||||||
|
|
||||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
|
||||||
|
// Setting up context
|
||||||
|
foreach (var plugin in plugins)
|
||||||
|
plugin.Initialize(plugins);
|
||||||
|
|
||||||
|
// Stage 1: Pre Build
|
||||||
foreach (var plugin in plugins)
|
foreach (var plugin in plugins)
|
||||||
plugin.PreBuild(builder);
|
plugin.PreBuild(builder);
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Stage 2: Post Build
|
||||||
foreach(var plugin in plugins)
|
foreach(var plugin in plugins)
|
||||||
plugin.PostBuild(app);
|
plugin.PostBuild(app);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.UI.Admin.Components
|
@using Moonlight.Frontend.UI.Admin.Components
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
|
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
|
||||||
@using Moonlight.Shared.Http.Responses
|
@using Moonlight.Shared.Http.Responses
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
var response = await HttpClient.PostAsJsonAsync(
|
var response = await HttpClient.PostAsJsonAsync(
|
||||||
"/api/admin/apiKeys",
|
"/api/admin/apiKeys",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.UI.Admin.Components
|
@using Moonlight.Frontend.UI.Admin.Components
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Roles
|
@using Moonlight.Shared.Http.Requests.Admin.Roles
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@using ShadcnBlazor.Extras.Forms
|
@using ShadcnBlazor.Extras.Forms
|
||||||
@@ -76,7 +77,7 @@
|
|||||||
var response = await HttpClient.PostAsJsonAsync(
|
var response = await HttpClient.PostAsJsonAsync(
|
||||||
"api/admin/roles",
|
"api/admin/roles",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Users
|
@using Moonlight.Shared.Http.Requests.Admin.Users
|
||||||
@using Moonlight.Shared.Http.Responses
|
@using Moonlight.Shared.Http.Responses
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@@ -65,7 +66,7 @@
|
|||||||
var response = await HttpClient.PostAsJsonAsync(
|
var response = await HttpClient.PostAsJsonAsync(
|
||||||
"/api/admin/users",
|
"/api/admin/users",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.Mappers
|
@using Moonlight.Frontend.Mappers
|
||||||
@using Moonlight.Frontend.UI.Admin.Components
|
@using Moonlight.Frontend.UI.Admin.Components
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
|
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
var response = await HttpClient.PatchAsJsonAsync(
|
var response = await HttpClient.PatchAsJsonAsync(
|
||||||
$"/api/admin/apiKeys/{Key.Id}",
|
$"/api/admin/apiKeys/{Key.Id}",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ else
|
|||||||
await HttpClient.PostAsJsonAsync("api/admin/ch/version", new SetVersionDto()
|
await HttpClient.PostAsJsonAsync("api/admin/ch/version", new SetVersionDto()
|
||||||
{
|
{
|
||||||
Version = Version
|
Version = Version
|
||||||
}, SerializationContext.TunedOptions);
|
}, SerializationContext.Default.Options);
|
||||||
|
|
||||||
// Starting rebuild task
|
// Starting rebuild task
|
||||||
CurrentStep = 2;
|
CurrentStep = 2;
|
||||||
@@ -136,7 +136,7 @@ else
|
|||||||
request.Content = JsonContent.Create(
|
request.Content = JsonContent.Create(
|
||||||
new RequestRebuildDto(NoBuildCache),
|
new RequestRebuildDto(NoBuildCache),
|
||||||
null,
|
null,
|
||||||
SerializationContext.TunedOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
var response = await HttpClient.SendAsync(
|
var response = await HttpClient.SendAsync(
|
||||||
@@ -160,7 +160,7 @@ else
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var data = line.Trim("data: ");
|
var data = line.Trim("data: ");
|
||||||
var deserializedData = JsonSerializer.Deserialize<RebuildEventDto>(data, Constants.SerializerOptions);
|
var deserializedData = JsonSerializer.Deserialize<RebuildEventDto>(data, SerializationContext.Default.Options);
|
||||||
|
|
||||||
switch (deserializedData.Type)
|
switch (deserializedData.Type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.Mappers
|
@using Moonlight.Frontend.Mappers
|
||||||
@using Moonlight.Frontend.UI.Admin.Components
|
@using Moonlight.Frontend.UI.Admin.Components
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Roles
|
@using Moonlight.Shared.Http.Requests.Admin.Roles
|
||||||
@using Moonlight.Shared.Http.Responses.Admin
|
@using Moonlight.Shared.Http.Responses.Admin
|
||||||
@using ShadcnBlazor.Dialogs
|
@using ShadcnBlazor.Dialogs
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
var response = await HttpClient.PatchAsJsonAsync(
|
var response = await HttpClient.PatchAsJsonAsync(
|
||||||
$"api/admin/roles/{Role.Id}",
|
$"api/admin/roles/{Role.Id}",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
@using LucideBlazor
|
||||||
|
@using Moonlight.Frontend.Helpers
|
||||||
|
@using Moonlight.Frontend.Services
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
|
@using Moonlight.Shared.Http.Requests.Admin.Settings
|
||||||
|
@using Moonlight.Shared.Http.Responses.Admin.Settings
|
||||||
|
@using ShadcnBlazor.Extras.Common
|
||||||
|
@using ShadcnBlazor.Extras.Forms
|
||||||
|
@using ShadcnBlazor.Extras.Toasts
|
||||||
|
@using ShadcnBlazor.Fields
|
||||||
|
@using ShadcnBlazor.Inputs
|
||||||
|
|
||||||
|
@inject HttpClient HttpClient
|
||||||
|
@inject ToastService ToastService
|
||||||
|
@inject FrontendService FrontendService
|
||||||
|
|
||||||
|
<LazyLoader Load="LoadAsync">
|
||||||
|
<EnhancedEditForm Model="Request" OnValidSubmit="OnValidSubmit">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
|
||||||
|
<FieldSet>
|
||||||
|
<FormValidationSummary />
|
||||||
|
|
||||||
|
<FieldGroup>
|
||||||
|
<Field>
|
||||||
|
<FieldLabel>Name</FieldLabel>
|
||||||
|
<TextInputField @bind-Value="Request.Name"/>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</FieldSet>
|
||||||
|
|
||||||
|
<SubmitButton ClassName="mt-3">
|
||||||
|
<SaveIcon/>
|
||||||
|
Save
|
||||||
|
</SubmitButton>
|
||||||
|
</EnhancedEditForm>
|
||||||
|
</LazyLoader>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private SetWhiteLabelingDto Request;
|
||||||
|
|
||||||
|
private async Task LoadAsync(LazyLoader _)
|
||||||
|
{
|
||||||
|
var dto = await HttpClient.GetFromJsonAsync<WhiteLabelingDto>(
|
||||||
|
"api/admin/system/settings/whiteLabeling",
|
||||||
|
SerializationContext.Default.Options
|
||||||
|
);
|
||||||
|
|
||||||
|
Request = new SetWhiteLabelingDto()
|
||||||
|
{
|
||||||
|
Name = dto!.Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> OnValidSubmit(EditContext editContext, ValidationMessageStore validationMessageStore)
|
||||||
|
{
|
||||||
|
var response = await HttpClient.PostAsJsonAsync(
|
||||||
|
"api/admin/system/settings/whiteLabeling",
|
||||||
|
Request,
|
||||||
|
SerializationContext.Default.Options
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
await FrontendService.ReloadAsync();
|
||||||
|
await ToastService.SuccessAsync("Setting", "Successfully updated white labeling settings");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
@page "/admin"
|
@page "/admin"
|
||||||
@using LucideBlazor
|
@using LucideBlazor
|
||||||
@using Moonlight.Frontend.UI.Admin.Modals
|
@using Moonlight.Frontend.UI.Admin.Modals
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Responses.Admin
|
@using Moonlight.Shared.Http.Responses.Admin
|
||||||
@using ShadcnBlazor.Buttons
|
@using ShadcnBlazor.Buttons
|
||||||
@using ShadcnBlazor.Cards
|
@using ShadcnBlazor.Cards
|
||||||
@@ -155,7 +156,7 @@
|
|||||||
if(!firstRender)
|
if(!firstRender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InfoResponse = await HttpClient.GetFromJsonAsync<SystemInfoDto>("api/admin/system/info", Constants.SerializerOptions);
|
InfoResponse = await HttpClient.GetFromJsonAsync<SystemInfoDto>("api/admin/system/info", SerializationContext.Default.Options);
|
||||||
IsInfoLoading = false;
|
IsInfoLoading = false;
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Moonlight.Frontend.UI.Admin.Modals
|
@using Moonlight.Frontend.UI.Admin.Modals
|
||||||
@using Moonlight.Shared
|
@using Moonlight.Shared
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests
|
@using Moonlight.Shared.Http.Requests
|
||||||
@using Moonlight.Shared.Http.Responses
|
@using Moonlight.Shared.Http.Responses
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
||||||
@@ -126,7 +127,7 @@
|
|||||||
|
|
||||||
var response = await HttpClient.GetFromJsonAsync<PagedData<ApiKeyDto>>(
|
var response = await HttpClient.GetFromJsonAsync<PagedData<ApiKeyDto>>(
|
||||||
$"api/admin/apiKeys{query}&filterOptions={filterOptions}",
|
$"api/admin/apiKeys{query}&filterOptions={filterOptions}",
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
return new DataGridResponse<ApiKeyDto>(response!.Data, response.TotalLength);
|
return new DataGridResponse<ApiKeyDto>(response!.Data, response.TotalLength);
|
||||||
|
|||||||
@@ -4,18 +4,14 @@
|
|||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Moonlight.Shared
|
@using Moonlight.Shared
|
||||||
@using ShadcnBlazor.Buttons
|
|
||||||
@using ShadcnBlazor.Cards
|
|
||||||
@using ShadcnBlazor.Inputs
|
|
||||||
@using ShadcnBlazor.Tab
|
@using ShadcnBlazor.Tab
|
||||||
@using ShadcnBlazor.Labels
|
|
||||||
|
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject IAuthorizationService AuthorizationService
|
@inject IAuthorizationService AuthorizationService
|
||||||
|
|
||||||
<Tabs DefaultValue="@(Tab ?? "settings")" OnValueChanged="OnTabChanged">
|
<Tabs DefaultValue="@(Tab ?? "settings")" OnValueChanged="OnTabChanged">
|
||||||
<TabsList ClassName="inline-flex w-full lg:w-fit justify-start overflow-x-auto overflow-y-hidden">
|
<TabsList ClassName="inline-flex w-full lg:w-fit justify-start overflow-x-auto overflow-y-hidden">
|
||||||
<TabsTrigger Value="settings">
|
<TabsTrigger Value="settings" Disabled="@(!SettingsResult.Succeeded)">
|
||||||
<CogIcon />
|
<CogIcon />
|
||||||
Settings
|
Settings
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -27,7 +23,7 @@
|
|||||||
<KeyIcon/>
|
<KeyIcon/>
|
||||||
API & API Keys
|
API & API Keys
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger Value="diagnose">
|
<TabsTrigger Value="diagnose" Disabled="@(!DiagnoseResult.Succeeded)">
|
||||||
<HeartPulseIcon/>
|
<HeartPulseIcon/>
|
||||||
Diagnose
|
Diagnose
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -36,19 +32,18 @@
|
|||||||
Instance
|
Instance
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent Value="settings">
|
@if (SettingsResult.Succeeded)
|
||||||
<Card ClassName="mt-5">
|
{
|
||||||
<CardFooter ClassName="justify-end">
|
<TabsContent Value="settings">
|
||||||
<Button>
|
<Settings />
|
||||||
<SaveIcon />
|
</TabsContent>
|
||||||
Save changes
|
}
|
||||||
</Button>
|
@if (DiagnoseResult.Succeeded)
|
||||||
</CardFooter>
|
{
|
||||||
</Card>
|
<TabsContent Value="diagnose">
|
||||||
</TabsContent>
|
<Diagnose />
|
||||||
<TabsContent Value="diagnose">
|
</TabsContent>
|
||||||
<Diagnose />
|
}
|
||||||
</TabsContent>
|
|
||||||
@if (ApiKeyAccess.Succeeded)
|
@if (ApiKeyAccess.Succeeded)
|
||||||
{
|
{
|
||||||
<TabsContent Value="apiKeys">
|
<TabsContent Value="apiKeys">
|
||||||
@@ -81,6 +76,8 @@
|
|||||||
private AuthorizationResult ThemesAccess;
|
private AuthorizationResult ThemesAccess;
|
||||||
private AuthorizationResult InstanceResult;
|
private AuthorizationResult InstanceResult;
|
||||||
private AuthorizationResult VersionsResult;
|
private AuthorizationResult VersionsResult;
|
||||||
|
private AuthorizationResult SettingsResult;
|
||||||
|
private AuthorizationResult DiagnoseResult;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -90,6 +87,8 @@
|
|||||||
ThemesAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Themes.View);
|
ThemesAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Themes.View);
|
||||||
InstanceResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Versions);
|
InstanceResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Versions);
|
||||||
VersionsResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Instance);
|
VersionsResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Instance);
|
||||||
|
SettingsResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Settings);
|
||||||
|
DiagnoseResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Diagnose);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTabChanged(string name)
|
private void OnTabChanged(string name)
|
||||||
|
|||||||
52
Moonlight.Frontend/UI/Admin/Views/Sys/Settings.razor
Normal file
52
Moonlight.Frontend/UI/Admin/Views/Sys/Settings.razor
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
@using Microsoft.Extensions.Options
|
||||||
|
@using Moonlight.Frontend.Configuration
|
||||||
|
@using ShadcnBlazor.Cards
|
||||||
|
@using ShadcnBlazor.Sidebars
|
||||||
|
|
||||||
|
@inject IOptions<SystemSettingsOptions> Options
|
||||||
|
|
||||||
|
<div class="mt-5 flex flex-col md:flex-row gap-5">
|
||||||
|
<Card ClassName="flex py-2 grow-0 min-w-56 max-h-[65vh] md:min-h-[65vh]">
|
||||||
|
<CardContent ClassName="px-2 flex flex-col gap-y-1 h-full max-h-[65vh] overflow-y-auto scrollbar-thin">
|
||||||
|
@foreach (var menuPage in Pages)
|
||||||
|
{
|
||||||
|
<SidebarMenuButton @onclick="() => Navigate(menuPage)" IsActive="@(CurrentPage == menuPage)" ClassName="overflow-visible">
|
||||||
|
<DynamicComponent Type="@menuPage.IconComponentType" />
|
||||||
|
<span>@menuPage.Name</span>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
@if (CurrentPage != null)
|
||||||
|
{
|
||||||
|
<Card ClassName="flex grow">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>@CurrentPage.Name</CardTitle>
|
||||||
|
<CardDescription>@CurrentPage.Description</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<DynamicComponent Type="@CurrentPage.ComponentType" />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private SystemSettingsPage[] Pages;
|
||||||
|
private SystemSettingsPage? CurrentPage;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Pages = Options
|
||||||
|
.Value
|
||||||
|
.Components
|
||||||
|
.OrderBy(x => x.Order)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
CurrentPage = Pages.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Navigate(SystemSettingsPage page)
|
||||||
|
=> CurrentPage = page;
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
@using LucideBlazor
|
@using LucideBlazor
|
||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.Services
|
@using Moonlight.Frontend.Services
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Themes
|
@using Moonlight.Shared.Http.Requests.Admin.Themes
|
||||||
@using ShadcnBlazor.Buttons
|
@using ShadcnBlazor.Buttons
|
||||||
@using ShadcnBlazor.Cards
|
@using ShadcnBlazor.Cards
|
||||||
@@ -122,7 +123,7 @@
|
|||||||
var response = await HttpClient.PostAsJsonAsync(
|
var response = await HttpClient.PostAsJsonAsync(
|
||||||
"/api/admin/themes",
|
"/api/admin/themes",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Moonlight.Shared
|
@using Moonlight.Shared
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests
|
@using Moonlight.Shared.Http.Requests
|
||||||
@using Moonlight.Shared.Http.Responses
|
@using Moonlight.Shared.Http.Responses
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Themes
|
@using Moonlight.Shared.Http.Responses.Admin.Themes
|
||||||
@@ -136,7 +137,7 @@
|
|||||||
|
|
||||||
var response = await HttpClient.GetFromJsonAsync<PagedData<ThemeDto>>(
|
var response = await HttpClient.GetFromJsonAsync<PagedData<ThemeDto>>(
|
||||||
$"api/admin/themes{query}&filterOptions={filterOptions}",
|
$"api/admin/themes{query}&filterOptions={filterOptions}",
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
return new DataGridResponse<ThemeDto>(response!.Data, response.TotalLength);
|
return new DataGridResponse<ThemeDto>(response!.Data, response.TotalLength);
|
||||||
@@ -182,7 +183,7 @@
|
|||||||
|
|
||||||
var importedTheme = await response
|
var importedTheme = await response
|
||||||
.Content
|
.Content
|
||||||
.ReadFromJsonAsync<ThemeDto>(Constants.SerializerOptions);
|
.ReadFromJsonAsync<ThemeDto>(SerializationContext.Default.Options);
|
||||||
|
|
||||||
if (importedTheme == null)
|
if (importedTheme == null)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
@using Moonlight.Frontend.Helpers
|
@using Moonlight.Frontend.Helpers
|
||||||
@using Moonlight.Frontend.Mappers
|
@using Moonlight.Frontend.Mappers
|
||||||
@using Moonlight.Frontend.Services
|
@using Moonlight.Frontend.Services
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Themes
|
@using Moonlight.Shared.Http.Requests.Admin.Themes
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Themes
|
@using Moonlight.Shared.Http.Responses.Admin.Themes
|
||||||
@using ShadcnBlazor.Buttons
|
@using ShadcnBlazor.Buttons
|
||||||
@@ -136,7 +137,7 @@
|
|||||||
var response = await HttpClient.PatchAsJsonAsync(
|
var response = await HttpClient.PatchAsJsonAsync(
|
||||||
$"/api/admin/themes/{Theme.Id}",
|
$"/api/admin/themes/{Theme.Id}",
|
||||||
Request,
|
Request,
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Moonlight.Frontend.UI.Admin.Modals
|
@using Moonlight.Frontend.UI.Admin.Modals
|
||||||
@using Moonlight.Shared
|
@using Moonlight.Shared
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using Moonlight.Shared.Http.Requests
|
@using Moonlight.Shared.Http.Requests
|
||||||
@using Moonlight.Shared.Http.Responses
|
@using Moonlight.Shared.Http.Responses
|
||||||
@using Moonlight.Shared.Http.Responses.Admin
|
@using Moonlight.Shared.Http.Responses.Admin
|
||||||
@@ -123,7 +124,7 @@
|
|||||||
|
|
||||||
var response = await HttpClient.GetFromJsonAsync<PagedData<RoleDto>>(
|
var response = await HttpClient.GetFromJsonAsync<PagedData<RoleDto>>(
|
||||||
$"api/admin/roles{query}&filterOptions={filterOptions}",
|
$"api/admin/roles{query}&filterOptions={filterOptions}",
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
return new DataGridResponse<RoleDto>(response!.Data, response.TotalLength);
|
return new DataGridResponse<RoleDto>(response!.Data, response.TotalLength);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Moonlight.Frontend.UI.Admin.Modals
|
@using Moonlight.Frontend.UI.Admin.Modals
|
||||||
@using Moonlight.Shared
|
@using Moonlight.Shared
|
||||||
|
@using Moonlight.Shared.Http
|
||||||
@using ShadcnBlazor.Buttons
|
@using ShadcnBlazor.Buttons
|
||||||
@using ShadcnBlazor.DataGrids
|
@using ShadcnBlazor.DataGrids
|
||||||
@using ShadcnBlazor.Dropdowns
|
@using ShadcnBlazor.Dropdowns
|
||||||
@@ -121,7 +122,7 @@
|
|||||||
|
|
||||||
var response = await HttpClient.GetFromJsonAsync<PagedData<UserDto>>(
|
var response = await HttpClient.GetFromJsonAsync<PagedData<UserDto>>(
|
||||||
$"api/admin/users{query}&filterOptions={filterOptions}",
|
$"api/admin/users{query}&filterOptions={filterOptions}",
|
||||||
Constants.SerializerOptions
|
SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
return new DataGridResponse<UserDto>(response!.Data, response.TotalLength);
|
return new DataGridResponse<UserDto>(response!.Data, response.TotalLength);
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
@using ShadcnBlazor.Emptys
|
@using ShadcnBlazor.Emptys
|
||||||
@using Moonlight.Frontend.UI.Shared.Components.Auth
|
@using Moonlight.Frontend.UI.Shared.Components.Auth
|
||||||
@using Moonlight.Frontend.UI.Shared.Partials
|
@using Moonlight.Frontend.UI.Shared.Partials
|
||||||
|
@using ShadcnBlazor.Extras.AlertDialogs
|
||||||
|
@using ShadcnBlazor.Extras.Dialogs
|
||||||
|
@using ShadcnBlazor.Extras.Toasts
|
||||||
|
@using ShadcnBlazor.Portals
|
||||||
|
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject IOptions<NavigationAssemblyOptions> NavigationOptions
|
@inject IOptions<NavigationAssemblyOptions> NavigationOptions
|
||||||
@@ -17,15 +21,23 @@
|
|||||||
<ChildContent>
|
<ChildContent>
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="Assemblies" NotFoundPage="typeof(NotFound)">
|
<LayoutMiddleware>
|
||||||
<Found Context="routeData">
|
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="Assemblies" NotFoundPage="typeof(NotFound)">
|
||||||
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
|
<Found Context="routeData">
|
||||||
<NotAuthorized Context="authRouteViewContext">
|
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
|
||||||
<AccessDenied/>
|
<NotAuthorized Context="authRouteViewContext">
|
||||||
</NotAuthorized>
|
<AccessDenied/>
|
||||||
</AuthorizeRouteView>
|
</NotAuthorized>
|
||||||
</Found>
|
</AuthorizeRouteView>
|
||||||
</Router>
|
</Found>
|
||||||
|
</Router>
|
||||||
|
</LayoutMiddleware>
|
||||||
|
|
||||||
|
<ToastLauncher/>
|
||||||
|
<DialogLauncher/>
|
||||||
|
<AlertDialogLauncher/>
|
||||||
|
|
||||||
|
<PortalOutlet />
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
<Authorizing>
|
<Authorizing>
|
||||||
<Authenticating/>
|
<Authenticating/>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@using Moonlight.Shared.Http.Responses.Admin.Auth
|
@using Moonlight.Shared.Http
|
||||||
|
@using Moonlight.Shared.Http.Responses.Admin.Auth
|
||||||
@using ShadcnBlazor.Cards
|
@using ShadcnBlazor.Cards
|
||||||
@using ShadcnBlazor.Spinners
|
@using ShadcnBlazor.Spinners
|
||||||
@using ShadcnBlazor.Buttons
|
@using ShadcnBlazor.Buttons
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var schemes = await HttpClient.GetFromJsonAsync<SchemeDto[]>(
|
var schemes = await HttpClient.GetFromJsonAsync<SchemeDto[]>(
|
||||||
"api/auth", Constants.SerializerOptions
|
"api/auth", SerializationContext.Default.Options
|
||||||
);
|
);
|
||||||
|
|
||||||
if (schemes == null)
|
if (schemes == null)
|
||||||
|
|||||||
34
Moonlight.Frontend/UI/Shared/LayoutMiddleware.razor
Normal file
34
Moonlight.Frontend/UI/Shared/LayoutMiddleware.razor
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
@using Microsoft.Extensions.Options
|
||||||
|
@using Moonlight.Frontend.Configuration
|
||||||
|
@using Moonlight.Frontend.Interfaces
|
||||||
|
|
||||||
|
@inject IOptions<LayoutMiddlewareOptions> Options
|
||||||
|
|
||||||
|
@Chain
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
private RenderFragment Chain;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Chain = ChildContent;
|
||||||
|
|
||||||
|
foreach (var component in Options.Value.Components)
|
||||||
|
{
|
||||||
|
// Capture current values
|
||||||
|
var currentChain = Chain;
|
||||||
|
var currentComponent = component;
|
||||||
|
|
||||||
|
Chain = builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent(0, currentComponent);
|
||||||
|
builder.SetKey(component);
|
||||||
|
builder.AddComponentParameter(1, nameof(LayoutMiddlewareBase.ChildContent), currentChain);
|
||||||
|
builder.CloseComponent();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,53 @@
|
|||||||
@using ShadcnBlazor.Extras.AlertDialogs
|
@using Microsoft.Extensions.Options
|
||||||
|
@using Moonlight.Frontend.Configuration
|
||||||
@using ShadcnBlazor.Extras.Alerts
|
@using ShadcnBlazor.Extras.Alerts
|
||||||
@using ShadcnBlazor.Extras.Dialogs
|
|
||||||
@using ShadcnBlazor.Extras.Toasts
|
|
||||||
@using ShadcnBlazor.Portals
|
|
||||||
@using ShadcnBlazor.Sidebars
|
@using ShadcnBlazor.Sidebars
|
||||||
|
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
@inject IOptions<LayoutPageOptions> LayoutPageOptions
|
||||||
|
|
||||||
<SidebarProvider DefaultOpen="true">
|
<SidebarProvider DefaultOpen="true">
|
||||||
<AppSidebar/>
|
<AppSidebar/>
|
||||||
|
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<AppHeader/>
|
<AppHeader/>
|
||||||
|
|
||||||
|
@foreach (var headerComponent in HeaderComponents)
|
||||||
|
{
|
||||||
|
<DynamicComponent Type="headerComponent" />
|
||||||
|
}
|
||||||
|
|
||||||
<div class="mx-8 my-8 max-w-full">
|
<div class="mx-8 my-8 max-w-full">
|
||||||
<AlertLauncher/>
|
<AlertLauncher/>
|
||||||
|
|
||||||
@Body
|
@Body
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ToastLauncher/>
|
@foreach (var footerComponent in FooterComponents)
|
||||||
<DialogLauncher/>
|
{
|
||||||
<AlertDialogLauncher/>
|
<DynamicComponent Type="footerComponent" />
|
||||||
|
}
|
||||||
<PortalOutlet />
|
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private Type[] HeaderComponents;
|
||||||
|
private Type[] FooterComponents;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
HeaderComponents = LayoutPageOptions.Value.Components
|
||||||
|
.Where(x => x.Slot == LayoutPageSlot.Header)
|
||||||
|
.OrderBy(x => x.Order)
|
||||||
|
.Select(x => x.ComponentType)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
FooterComponents = LayoutPageOptions.Value.Components
|
||||||
|
.Where(x => x.Slot == LayoutPageSlot.Footer)
|
||||||
|
.OrderBy(x => x.Order)
|
||||||
|
.Select(x => x.ComponentType)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Moonlight.Shared.Http.Requests.Admin.Settings;
|
||||||
|
|
||||||
|
public class SetWhiteLabelingDto
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Shared.Http.Responses.Admin.Settings;
|
||||||
|
|
||||||
|
public class WhiteLabelingDto
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
@@ -4,12 +4,14 @@ using Moonlight.Shared.Http.Events;
|
|||||||
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
|
using Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Roles;
|
using Moonlight.Shared.Http.Requests.Admin.Roles;
|
||||||
|
using Moonlight.Shared.Http.Requests.Admin.Settings;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Themes;
|
using Moonlight.Shared.Http.Requests.Admin.Themes;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Users;
|
using Moonlight.Shared.Http.Requests.Admin.Users;
|
||||||
using Moonlight.Shared.Http.Responses;
|
using Moonlight.Shared.Http.Responses;
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
using Moonlight.Shared.Http.Responses.Admin;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Auth;
|
using Moonlight.Shared.Http.Responses.Admin.Auth;
|
||||||
|
using Moonlight.Shared.Http.Responses.Admin.Settings;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Themes;
|
using Moonlight.Shared.Http.Responses.Admin.Themes;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Users;
|
using Moonlight.Shared.Http.Responses.Admin.Users;
|
||||||
|
|
||||||
@@ -58,21 +60,13 @@ namespace Moonlight.Shared.Http;
|
|||||||
//Misc
|
//Misc
|
||||||
[JsonSerializable(typeof(VersionDto))]
|
[JsonSerializable(typeof(VersionDto))]
|
||||||
[JsonSerializable(typeof(ProblemDetails))]
|
[JsonSerializable(typeof(ProblemDetails))]
|
||||||
|
|
||||||
|
// Settings - White Labeling
|
||||||
|
[JsonSerializable(typeof(WhiteLabelingDto))]
|
||||||
|
[JsonSerializable(typeof(SetWhiteLabelingDto))]
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
|
||||||
public partial class SerializationContext : JsonSerializerContext
|
public partial class SerializationContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
private static JsonSerializerOptions? InternalTunedOptions;
|
|
||||||
|
|
||||||
public static JsonSerializerOptions TunedOptions
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (InternalTunedOptions != null)
|
|
||||||
return InternalTunedOptions;
|
|
||||||
|
|
||||||
InternalTunedOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
|
||||||
InternalTunedOptions.TypeInfoResolverChain.Add(Default);
|
|
||||||
|
|
||||||
return InternalTunedOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -55,5 +55,6 @@ public static class Permissions
|
|||||||
public const string Diagnose = $"{Prefix}{Section}.{nameof(Diagnose)}";
|
public const string Diagnose = $"{Prefix}{Section}.{nameof(Diagnose)}";
|
||||||
public const string Versions = $"{Prefix}{Section}.{nameof(Versions)}";
|
public const string Versions = $"{Prefix}{Section}.{nameof(Versions)}";
|
||||||
public const string Instance = $"{Prefix}{Section}.{nameof(Instance)}";
|
public const string Instance = $"{Prefix}{Section}.{nameof(Instance)}";
|
||||||
|
public const string Settings = $"{Prefix}{Section}.{nameof(Settings)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user