Refactored project to module structure

This commit is contained in:
2026-03-12 22:50:15 +01:00
parent 93de9c5d00
commit 1257e8b950
219 changed files with 1231 additions and 1259 deletions

View File

@@ -1,11 +1,10 @@
@using LucideBlazor
@using Moonlight.Shared.Http.Requests.Seup
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Spinners
@using Moonlight.Shared.Admin.Setup
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Inputs
@using ShadcnBlazor.Labels
@using ShadcnBlazor.Spinners
@inject HttpClient HttpClient
@inject NavigationManager Navigation
@@ -18,13 +17,14 @@
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<img alt="Moonlight Logo" class="size-34" src="/_content/Moonlight.Frontend/logo.svg" />
<img alt="Moonlight Logo" class="size-34" src="/_content/Moonlight.Frontend/logo.svg"/>
</div>
<div class="space-y-2 text-center">
<h2 class="text-2xl font-bold">Welcome to Moonlight Panel</h2>
<p class="text-muted-foreground">
You successfully installed moonlight. Now you are ready to perform some initial steps to complete your installation
You successfully installed moonlight. Now you are ready to perform some initial steps to
complete your installation
</p>
</div>
}
@@ -32,17 +32,19 @@
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<UserIcon ClassName="size-34 text-blue-500" />
<UserIcon ClassName="size-34 text-blue-500"/>
</div>
<div class="space-y-2 text-center">
<h2 class="text-2xl font-bold">Admin Account Creation</h2>
<p class="text-muted-foreground">
To continue please fill in the account details of the user you want to use as the initial administrator account.
If you use an external OIDC provider, these details need to match with your desired OIDC account
To continue please fill in the account details of the user you want to use as the initial
administrator account.
If you use an external OIDC provider, these details need to match with your desired OIDC
account
</p>
</div>
<div class="grid grid-cols-1 gap-5">
<div class="grid gap-2">
<Label for="username">Username</Label>
@@ -73,7 +75,7 @@
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<RocketIcon ClassName="size-34 text-blue-500" />
<RocketIcon ClassName="size-34 text-blue-500"/>
</div>
<div class="space-y-2 text-center">
@@ -105,7 +107,7 @@
@if (CurrentStep > 0)
{
<Button @onclick="() => Navigate(-1)" Variant="ButtonVariant.Outline">
<ChevronLeftIcon />
<ChevronLeftIcon/>
Back
</Button>
}
@@ -120,7 +122,7 @@
{
<Button @onclick="ApplyAsync">
Finish
<RocketIcon />
<RocketIcon/>
</Button>
}
</div>
@@ -140,16 +142,19 @@
{
private bool IsLoaded;
private int CurrentStep = 0;
private int StepCount = 3;
private int CurrentStep;
private readonly int StepCount = 3;
private ApplySetupDto SetupDto = new();
private readonly ApplySetupDto SetupDto = new();
private void Navigate(int diff) => CurrentStep += diff;
private void Navigate(int diff)
{
CurrentStep += diff;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(!firstRender)
if (!firstRender)
return;
var response = await HttpClient.GetAsync("api/admin/setup");

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
using Moonlight.Shared.Admin.Sys.ApiKeys;
using Riok.Mapperly.Abstractions;
namespace Moonlight.Frontend.Mappers;
namespace Moonlight.Frontend.Admin.Sys.ApiKeys;
[Mapper]
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]

View File

@@ -1,8 +1,7 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.UI.Admin.Components
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
@using Moonlight.Shared.Http.Responses
@using Moonlight.Frontend.Admin.Users.Shared
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared
@using Moonlight.Shared.Admin.Sys.ApiKeys
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@@ -26,7 +25,7 @@
<FieldGroup>
<DataAnnotationsValidator/>
<FormValidationSummary/>
<FieldSet>
<Field>
<FieldLabel for="keyName">Name</FieldLabel>
@@ -34,11 +33,12 @@
</Field>
<Field>
<FieldLabel for="keyDescription">Description</FieldLabel>
<TextareaInputField @bind-Value="Request.Description" id="keyDescription" placeholder="What this key is for"/>
<TextareaInputField @bind-Value="Request.Description" id="keyDescription"
placeholder="What this key is for"/>
</Field>
<Field>
<FieldLabel for="keyValidUntil">Valid until</FieldLabel>
<DateTimeInputField @bind-Value="Request.ValidUntil" id="keyValidUntil" />
<DateTimeInputField @bind-Value="Request.ValidUntil" id="keyValidUntil"/>
</Field>
<Field>
<FieldLabel>Permissions</FieldLabel>
@@ -59,11 +59,11 @@
private CreateApiKeyDto Request;
private List<string> Permissions = new();
private readonly List<string> Permissions = new();
protected override void OnInitialized()
{
Request = new()
Request = new CreateApiKeyDto
{
Permissions = [],
ValidUntil = DateTimeOffset.UtcNow
@@ -74,13 +74,13 @@
{
Request.Permissions = Permissions.ToArray();
Request.ValidUntil = Request.ValidUntil.ToUniversalTime();
var response = await HttpClient.PostAsJsonAsync(
"/api/admin/apiKeys",
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);

View File

@@ -1,19 +1,18 @@
@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.UI.Admin.Modals
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
@using Moonlight.Shared.Admin.Sys.ApiKeys
@using Moonlight.Shared.Shared
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.DataGrids
@using ShadcnBlazor.Dropdowns
@using ShadcnBlazor.Extras.AlertDialogs
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Tabels
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Tabels
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject ToastService ToastService
@inject DialogService DialogService
@@ -49,15 +48,17 @@
</CellTemplate>
</TemplateColumn>
<PropertyColumn IsFilterable="true"
Identifier="@nameof(ApiKeyDto.Description)" Field="k => k.Description" HeadClassName="hidden lg:table-cell" CellClassName="hidden lg:table-cell" />
<TemplateColumn Identifier="@nameof(ApiKeyDto.ValidUntil)" Title="Valid until" HeadClassName="hidden lg:table-cell">
Identifier="@nameof(ApiKeyDto.Description)" Field="k => k.Description"
HeadClassName="hidden lg:table-cell" CellClassName="hidden lg:table-cell"/>
<TemplateColumn Identifier="@nameof(ApiKeyDto.ValidUntil)" Title="Valid until"
HeadClassName="hidden lg:table-cell">
<CellTemplate>
<TableCell ClassName="hidden lg:table-cell">
@{
var diff = context.ValidUntil - DateTimeOffset.UtcNow;
var text = string.Format(diff.TotalSeconds < 0 ? "Expired since {0}" : "Expires in {0}", Formatter.FormatDuration(diff));
}
<span>
@text
</span>
@@ -78,7 +79,8 @@
</Slot>
</DropdownMenuTrigger>
<DropdownMenuContent SideOffset="2">
<DropdownMenuItem OnClick="() => EditAsync(context)" Disabled="@(!EditAccess.Succeeded)">
<DropdownMenuItem OnClick="() => EditAsync(context)"
Disabled="@(!EditAccess.Succeeded)">
Edit
<DropdownMenuShortcut>
<PenIcon/>
@@ -104,9 +106,9 @@
@code
{
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private DataGrid<ApiKeyDto> Grid;
private AuthorizationResult EditAccess;
private AuthorizationResult DeleteAccess;
private AuthorizationResult CreateAccess;
@@ -114,7 +116,7 @@
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
EditAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.ApiKeys.Edit);
DeleteAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.ApiKeys.Delete);
CreateAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.ApiKeys.Create);
@@ -135,13 +137,7 @@
private async Task CreateAsync()
{
await DialogService.LaunchAsync<CreateApiKeyDialog>(parameters =>
{
parameters[nameof(CreateApiKeyDialog.OnSubmit)] = async () =>
{
await Grid.RefreshAsync();
};
});
await DialogService.LaunchAsync<CreateApiKeyDialog>(parameters => { parameters[nameof(CreateApiKeyDialog.OnSubmit)] = async () => { await Grid.RefreshAsync(); }; });
}
private async Task EditAsync(ApiKeyDto key)
@@ -149,10 +145,7 @@
await DialogService.LaunchAsync<UpdateApiKeyDialog>(parameters =>
{
parameters[nameof(UpdateApiKeyDialog.Key)] = key;
parameters[nameof(UpdateApiKeyDialog.OnSubmit)] = async () =>
{
await Grid.RefreshAsync();
};
parameters[nameof(UpdateApiKeyDialog.OnSubmit)] = async () => { await Grid.RefreshAsync(); };
});
}

View File

@@ -1,9 +1,7 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.Mappers
@using Moonlight.Frontend.UI.Admin.Components
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.ApiKeys
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
@using Moonlight.Frontend.Admin.Users.Shared
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared
@using Moonlight.Shared.Admin.Sys.ApiKeys
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@@ -34,11 +32,12 @@
</Field>
<Field>
<FieldLabel for="keyDescription">Description</FieldLabel>
<TextareaInputField @bind-Value="Request.Description" id="keyDescription" placeholder="What this key is for"/>
<TextareaInputField @bind-Value="Request.Description" id="keyDescription"
placeholder="What this key is for"/>
</Field>
<Field>
<FieldLabel for="keyValidUntil">Valid until</FieldLabel>
<DateTimeInputField @bind-Value="Request.ValidUntil" id="keyValidUntil" />
<DateTimeInputField @bind-Value="Request.ValidUntil" id="keyValidUntil"/>
</Field>
<Field>
<FieldLabel>Permissions</FieldLabel>
@@ -71,13 +70,13 @@
{
Request.Permissions = Permissions.ToArray();
Request.ValidUntil = Request.ValidUntil.ToUniversalTime();
var response = await HttpClient.PatchAsJsonAsync(
$"/api/admin/apiKeys/{Key.Id}",
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);
@@ -88,7 +87,7 @@
"API Key update",
$"Successfully updated API key {Request.Name}"
);
await OnSubmit.Invoke();
await CloseAsync();

View File

@@ -2,7 +2,7 @@
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Shared
@using Moonlight.Shared.Http.Responses.Admin
@using Moonlight.Shared.Admin.Sys.Diagnose
@using ShadcnBlazor.Accordions
@using ShadcnBlazor.Alerts
@using ShadcnBlazor.Buttons
@@ -10,7 +10,6 @@
@using ShadcnBlazor.Emptys
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Spinners
@inject HttpClient HttpClient
@inject IAuthorizationService AuthorizationService
@@ -223,15 +222,15 @@
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private AuthorizationResult AccessResult;
private bool IsLoading = false;
private bool HasDiagnosed = false;
private bool IsLoading;
private bool HasDiagnosed;
private DiagnoseResultDto[] Entries;
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
AccessResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Diagnose);
}

View File

@@ -1,16 +1,15 @@
@using LucideBlazor
@using Moonlight.Frontend.UI.Admin.Modals
@using Moonlight.Shared.Http.Responses.Admin
@using Moonlight.Shared.Admin.Sys.ContainerHelper
@using Moonlight.Shared.Admin.Sys.Versions
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Emptys
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Extras.AlertDialogs
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Selects
@using ShadcnBlazor.Switches
@inject HttpClient HttpClient
@inject DialogService DialogService
@inject AlertDialogService AlertDialogService
@@ -43,10 +42,10 @@
if (version.IsDevelopment)
displayName += " (dev)";
if (version.IsPreRelease)
displayName += " (beta)";
<SelectItem Value="@version.Identifier">
@displayName
</SelectItem>
@@ -137,7 +136,7 @@
if (currentVersion != null)
SelectedVersion = currentVersion.Identifier;
Versions = (await HttpClient.GetFromJsonAsync<VersionDto[]>("api/admin/versions"))!;
}
@@ -149,7 +148,7 @@
parameters[nameof(UpdateInstanceModal.Version)] = SelectedVersion;
parameters[nameof(UpdateInstanceModal.NoBuildCache)] = NoBuildCache;
},
onConfigure: model =>
model =>
{
model.ShowCloseButton = false;
model.ClassName = "sm:max-w-4xl!";

View File

@@ -1,14 +1,12 @@
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@using System.Text.Json
@using LucideBlazor
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Events
@using Moonlight.Shared.Http.Requests.Admin.ContainerHelper
@using Moonlight.Shared.Admin.Sys.ContainerHelper
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Progresses
@using ShadcnBlazor.Spinners
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@@ -121,7 +119,7 @@ else
Progress = 20;
await InvokeAsync(StateHasChanged);
await HttpClient.PostAsJsonAsync("api/admin/ch/version", new SetVersionDto()
await HttpClient.PostAsJsonAsync("api/admin/ch/version", new SetVersionDto
{
Version = Version
}, SerializationContext.Default.Options);

View File

@@ -1,8 +1,9 @@
@page "/admin/system"
@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.Admin.Sys.HelperContainer
@using Moonlight.Frontend.Admin.Sys.Settings
@using Moonlight.Shared
@using ShadcnBlazor.Tab
@@ -12,7 +13,7 @@
<Tabs DefaultValue="@(Tab ?? "settings")" OnValueChanged="OnTabChanged">
<TabsList ClassName="inline-flex w-full lg:w-fit justify-start overflow-x-auto overflow-y-hidden">
<TabsTrigger Value="settings" Disabled="@(!SettingsResult.Succeeded)">
<CogIcon />
<CogIcon/>
Settings
</TabsTrigger>
<TabsTrigger Value="themes" Disabled="@(!ThemesAccess.Succeeded)">
@@ -35,31 +36,31 @@
@if (SettingsResult.Succeeded)
{
<TabsContent Value="settings">
<Settings />
<Settings/>
</TabsContent>
}
@if (DiagnoseResult.Succeeded)
{
<TabsContent Value="diagnose">
<Diagnose />
<Diagnose/>
</TabsContent>
}
@if (ApiKeyAccess.Succeeded)
{
<TabsContent Value="apiKeys">
<ApiKeys />
<Moonlight.Frontend.Admin.Sys.ApiKeys.Index/>
</TabsContent>
}
@if (ThemesAccess.Succeeded)
{
<TabsContent Value="themes">
<Moonlight.Frontend.UI.Admin.Views.Sys.Themes.Index />
<Moonlight.Frontend.Admin.Sys.Themes.Index/>
</TabsContent>
}
@if (InstanceResult.Succeeded && VersionsResult.Succeeded)
{
<TabsContent Value="instance">
<Instance />
<Instance/>
</TabsContent>
}
</Tabs>
@@ -69,7 +70,7 @@
[SupplyParameterFromQuery(Name = "tab")]
[Parameter]
public string? Tab { get; set; }
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private AuthorizationResult ApiKeyAccess;
@@ -78,7 +79,7 @@
private AuthorizationResult VersionsResult;
private AuthorizationResult SettingsResult;
private AuthorizationResult DiagnoseResult;
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;

View File

@@ -1,12 +1,12 @@
@page "/admin"
@using LucideBlazor
@using Moonlight.Frontend.UI.Admin.Modals
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Responses.Admin
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared.Admin.Sys
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Spinners
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@inject DialogService DialogService
@@ -30,12 +30,12 @@
<CardDescription>CPU Usage</CardDescription>
<CardTitle ClassName="text-lg">@Math.Round(InfoResponse.CpuUsage, 2)%</CardTitle>
<CardAction>
<CpuIcon ClassName="size-6 text-muted-foreground" />
<CpuIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
</CardHeader>
}
</Card>
<Card ClassName="col-span-1">
@if (IsInfoLoading || InfoResponse == null)
{
@@ -49,12 +49,12 @@
<CardDescription>Memory Usage</CardDescription>
<CardTitle ClassName="text-lg">@Formatter.FormatSize(InfoResponse.MemoryUsage)</CardTitle>
<CardAction>
<MemoryStickIcon ClassName="size-6 text-muted-foreground" />
<MemoryStickIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
</CardHeader>
}
</Card>
<Card ClassName="col-span-1">
@if (IsInfoLoading || InfoResponse == null)
{
@@ -68,12 +68,12 @@
<CardDescription>Operating System</CardDescription>
<CardTitle ClassName="text-lg">@InfoResponse.OperatingSystem</CardTitle>
<CardAction>
<ComputerIcon ClassName="size-6 text-muted-foreground" />
<ComputerIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
</CardHeader>
}
</Card>
<Card ClassName="col-span-1">
@if (IsInfoLoading || InfoResponse == null)
{
@@ -87,12 +87,12 @@
<CardDescription>Uptime</CardDescription>
<CardTitle ClassName="text-lg">@Formatter.FormatDuration(InfoResponse.Uptime)</CardTitle>
<CardAction>
<ClockIcon ClassName="size-6 text-muted-foreground" />
<ClockIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
</CardHeader>
}
</Card>
<Card ClassName="col-span-1">
@if (IsInfoLoading || InfoResponse == null)
{
@@ -106,12 +106,12 @@
<CardDescription>Version</CardDescription>
<CardTitle ClassName="text-lg">@InfoResponse.VersionName</CardTitle>
<CardAction>
<RocketIcon ClassName="size-6 text-muted-foreground" />
<RocketIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
</CardHeader>
}
</Card>
<Card ClassName="col-span-1">
@if (IsInfoLoading || InfoResponse == null)
{
@@ -127,7 +127,7 @@
{
<CardTitle ClassName="text-lg text-green-500">Up-to-date</CardTitle>
<CardAction>
<RefreshCwIcon ClassName="size-6 text-muted-foreground" />
<RefreshCwIcon ClassName="size-6 text-muted-foreground"/>
</CardAction>
}
else
@@ -153,7 +153,7 @@
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(!firstRender)
if (!firstRender)
return;
InfoResponse = await HttpClient.GetFromJsonAsync<SystemInfoDto>("api/admin/system/info", SerializationContext.Default.Options);

View File

@@ -1,8 +1,6 @@
@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">
@@ -10,8 +8,9 @@
<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" />
<SidebarMenuButton @onclick="() => Navigate(menuPage)" IsActive="@(CurrentPage == menuPage)"
ClassName="overflow-visible">
<DynamicComponent Type="@menuPage.IconComponentType"/>
<span>@menuPage.Name</span>
</SidebarMenuButton>
}
@@ -25,7 +24,7 @@
<CardDescription>@CurrentPage.Description</CardDescription>
</CardHeader>
<CardContent>
<DynamicComponent Type="@CurrentPage.ComponentType" />
<DynamicComponent Type="@CurrentPage.ComponentType"/>
</CardContent>
</Card>
}
@@ -48,5 +47,7 @@
}
private void Navigate(SystemSettingsPage page)
=> CurrentPage = page;
{
CurrentPage = page;
}
}

View File

@@ -1,29 +1,39 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
namespace Moonlight.Frontend.Configuration;
namespace Moonlight.Frontend.Admin.Sys.Settings;
public class SystemSettingsOptions
{
private readonly List<SystemSettingsPage> InnerComponents = new();
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,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
TComponent>(string name, string description,
int order)
where TIcon : ComponentBase where TComponent : ComponentBase
=> Add(name, description, order, typeof(TIcon), typeof(TComponent));
{
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 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));
{
Remove(typeof(TComponent));
}
public void Remove([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType)
=> InnerComponents.RemoveAll(x => x.ComponentType == componentType);
{
InnerComponents.RemoveAll(x => x.ComponentType == componentType);
}
}
public record SystemSettingsPage(

View File

@@ -1,14 +1,13 @@
@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 Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Frontend.Shared.Frontend
@using Moonlight.Shared.Admin.Sys.Settings
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@inject ToastService ToastService
@@ -16,11 +15,11 @@
<LazyLoader Load="LoadAsync">
<EnhancedEditForm Model="Request" OnValidSubmit="OnValidSubmit">
<DataAnnotationsValidator />
<DataAnnotationsValidator/>
<FieldSet>
<FormValidationSummary />
<FormValidationSummary/>
<FieldGroup>
<Field>
<FieldLabel>Name</FieldLabel>
@@ -46,8 +45,8 @@
"api/admin/system/settings/whiteLabeling",
SerializationContext.Default.Options
);
Request = new SetWhiteLabelingDto()
Request = new SetWhiteLabelingDto
{
Name = dto!.Name
};
@@ -65,7 +64,7 @@
{
await FrontendService.ReloadAsync();
await ToastService.SuccessAsync("Setting", "Successfully updated white labeling settings");
return true;
}

View File

@@ -1,12 +1,10 @@
@page "/admin/system/themes/create"
@using Microsoft.AspNetCore.Authorization
@using Moonlight.Shared
@using LucideBlazor
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.Services
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.Themes
@using Microsoft.AspNetCore.Authorization
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Frontend.Shared.Frontend
@using Moonlight.Shared
@using Moonlight.Shared.Admin.Sys.Themes
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Extras.Editors
@@ -15,6 +13,7 @@
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using ShadcnBlazor.Switches
@using SerializationContext = Moonlight.Shared.SerializationContext
@attribute [Authorize(Policy = Permissions.Themes.Create)]
@@ -109,7 +108,7 @@
@code
{
private CreateThemeDto Request = new()
private readonly CreateThemeDto Request = new()
{
CssContent = "/* Define your css here */"
};
@@ -125,7 +124,7 @@
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);

View File

@@ -2,16 +2,15 @@
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Shared
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin.Themes
@using Moonlight.Shared.Admin.Sys.Themes
@using Moonlight.Shared.Shared
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.DataGrids
@using ShadcnBlazor.Dropdowns
@using ShadcnBlazor.Extras.AlertDialogs
@using ShadcnBlazor.Tabels
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Tabels
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject ToastService ToastService
@inject NavigationManager Navigation
@@ -143,11 +142,20 @@
return new DataGridResponse<ThemeDto>(response!.Data, response.TotalLength);
}
private void Create() => Navigation.NavigateTo("/admin/system/themes/create");
private void Create()
{
Navigation.NavigateTo("/admin/system/themes/create");
}
private void Edit(ThemeDto theme) => Navigation.NavigateTo($"/admin/system/themes/{theme.Id}");
private void Edit(ThemeDto theme)
{
Navigation.NavigateTo($"/admin/system/themes/{theme.Id}");
}
private void Download(ThemeDto theme) => Navigation.NavigateTo($"api/admin/themes/{theme.Id}/export", true);
private void Download(ThemeDto theme)
{
Navigation.NavigateTo($"api/admin/themes/{theme.Id}/export", true);
}
private async Task DeleteAsync(ThemeDto theme)
{

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Moonlight.Shared.Http.Requests.Admin.Themes;
using Moonlight.Shared.Http.Responses.Admin.Themes;
using Moonlight.Shared.Admin.Sys.Themes;
using Riok.Mapperly.Abstractions;
namespace Moonlight.Frontend.Mappers;
namespace Moonlight.Frontend.Admin.Sys.Themes;
[Mapper]
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]

View File

@@ -1,14 +1,10 @@
@page "/admin/system/themes/{Id:int}"
@using Microsoft.AspNetCore.Authorization
@using Moonlight.Shared
@using LucideBlazor
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.Mappers
@using Moonlight.Frontend.Services
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.Themes
@using Moonlight.Shared.Http.Responses.Admin.Themes
@using Microsoft.AspNetCore.Authorization
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Frontend.Shared.Frontend
@using Moonlight.Shared
@using Moonlight.Shared.Admin.Sys.Themes
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Extras.Common
@@ -18,6 +14,7 @@
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using ShadcnBlazor.Switches
@using SerializationContext = Moonlight.Shared.SerializationContext
@attribute [Authorize(Policy = Permissions.Themes.Edit)]
@@ -139,7 +136,7 @@
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);

View File

@@ -1,6 +1,8 @@
@page "/admin/users"
@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Moonlight.Frontend.Admin.Users.Roles
@using Moonlight.Frontend.Admin.Users.Users
@using Moonlight.Shared
@using ShadcnBlazor.Tab
@@ -11,19 +13,19 @@
<Tabs DefaultValue="@(Tab ?? "users")" OnValueChanged="OnTabChanged">
<TabsList ClassName="inline-flex w-full lg:w-fit justify-start overflow-x-auto overflow-y-hidden">
<TabsTrigger Value="users">
<UserRoundIcon />
<UserRoundIcon/>
Users
</TabsTrigger>
<TabsTrigger Value="roles">
<UsersRoundIcon />
<UsersRoundIcon/>
Roles
</TabsTrigger>
</TabsList>
<TabsContent Value="users">
<Users />
<Users/>
</TabsContent>
<TabsContent Value="roles">
<Roles />
<Roles/>
</TabsContent>
</Tabs>

View File

@@ -1,12 +1,12 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.UI.Admin.Components
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.Roles
@using Moonlight.Frontend.Admin.Users.Shared
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared.Admin.Users.Roles
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using SerializationContext = Moonlight.Shared.SerializationContext
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@@ -62,24 +62,24 @@
protected override void OnInitialized()
{
Request = new()
Request = new CreateRoleDto
{
Permissions = []
};
Permissions = new();
Permissions = new List<string>();
}
private async Task<bool> OnSubmitAsync(EditContext editContext, ValidationMessageStore validationMessageStore)
{
Request.Permissions = Permissions.ToArray();
var response = await HttpClient.PostAsJsonAsync(
"api/admin/roles",
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);

View File

@@ -1,7 +1,7 @@
@using LucideBlazor
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin
@using Moonlight.Shared.Http.Responses.Admin.Users
@using Moonlight.Shared.Admin.Users.Roles
@using Moonlight.Shared.Admin.Users.Users
@using Moonlight.Shared.Shared
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.DataGrids
@using ShadcnBlazor.Dialogs
@@ -9,7 +9,6 @@
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Labels
@using ShadcnBlazor.Tabels
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@inject HttpClient HttpClient
@@ -50,7 +49,8 @@
<CellTemplate>
<TableCell>
<div class="flex justify-end me-1.5">
<WButton OnClick="_ => RemoveAsync(context)" Variant="ButtonVariant.Destructive" Size="ButtonSize.Icon">
<WButton OnClick="_ => RemoveAsync(context)" Variant="ButtonVariant.Destructive"
Size="ButtonSize.Icon">
<TrashIcon/>
</WButton>
</div>

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Moonlight.Shared.Http.Requests.Admin.Roles;
using Moonlight.Shared.Http.Responses.Admin;
using Moonlight.Shared.Admin.Users.Roles;
using Riok.Mapperly.Abstractions;
namespace Moonlight.Frontend.Mappers;
namespace Moonlight.Frontend.Admin.Users.Roles;
[Mapper]
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]

View File

@@ -1,19 +1,17 @@
@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.UI.Admin.Modals
@using Moonlight.Shared
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin
@using ShadcnBlazor.DataGrids
@using Moonlight.Shared.Admin.Users.Roles
@using Moonlight.Shared.Shared
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.DataGrids
@using ShadcnBlazor.Dropdowns
@using ShadcnBlazor.Extras.AlertDialogs
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Tabels
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@inject DialogService DialogService
@@ -67,13 +65,15 @@
</Slot>
</DropdownMenuTrigger>
<DropdownMenuContent SideOffset="2">
<DropdownMenuItem OnClick="() => MembersAsync(context)" Disabled="@(!MembersAccess.Succeeded)">
<DropdownMenuItem OnClick="() => MembersAsync(context)"
Disabled="@(!MembersAccess.Succeeded)">
Members
<DropdownMenuShortcut>
<UsersRoundIcon/>
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem OnClick="() => EditAsync(context)" Disabled="@(!EditAccess.Succeeded)">
<DropdownMenuItem OnClick="() => EditAsync(context)"
Disabled="@(!EditAccess.Succeeded)">
Edit
<DropdownMenuShortcut>
<PenIcon/>
@@ -99,7 +99,7 @@
@code
{
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private DataGrid<RoleDto> Grid;
private AuthorizationResult MembersAccess;
@@ -110,7 +110,7 @@
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
MembersAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Roles.Members);
EditAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Roles.Edit);
DeleteAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Roles.Delete);
@@ -132,13 +132,7 @@
private async Task CreateAsync()
{
await DialogService.LaunchAsync<CreateRoleDialog>(parameters =>
{
parameters[nameof(CreateRoleDialog.OnSubmit)] = async Task () =>
{
await Grid.RefreshAsync();
};
});
await DialogService.LaunchAsync<CreateRoleDialog>(parameters => { parameters[nameof(CreateRoleDialog.OnSubmit)] = async Task () => { await Grid.RefreshAsync(); }; });
}
private async Task EditAsync(RoleDto role)
@@ -146,10 +140,7 @@
await DialogService.LaunchAsync<UpdateRoleDialog>(parameters =>
{
parameters[nameof(UpdateRoleDialog.Role)] = role;
parameters[nameof(UpdateRoleDialog.OnSubmit)] = async Task () =>
{
await Grid.RefreshAsync();
};
parameters[nameof(UpdateRoleDialog.OnSubmit)] = async Task () => { await Grid.RefreshAsync(); };
});
}
@@ -160,7 +151,7 @@
await ToastService.ErrorAsync("Permission denied", "You dont have the required permission to manage members");
return;
}
await DialogService.LaunchAsync<ManageRoleMembersDialog>(parameters => { parameters[nameof(ManageRoleMembersDialog.Role)] = role; }, model => { model.ClassName = "sm:max-w-xl"; });
}
@@ -173,7 +164,7 @@
{
var response = await HttpClient.DeleteAsync($"api/admin/roles/{role.Id}");
response.EnsureSuccessStatusCode();
await ToastService.SuccessAsync("User deletion", $"Successfully deleted role {role.Name}");
await Grid.RefreshAsync();

View File

@@ -1,14 +1,12 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.Mappers
@using Moonlight.Frontend.UI.Admin.Components
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.Roles
@using Moonlight.Shared.Http.Responses.Admin
@using Moonlight.Frontend.Admin.Users.Shared
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared.Admin.Users.Roles
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using SerializationContext = Moonlight.Shared.SerializationContext
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@@ -72,13 +70,13 @@
private async Task<bool> OnSubmitAsync(EditContext editContext, ValidationMessageStore validationMessageStore)
{
Request.Permissions = Permissions.ToArray();
var response = await HttpClient.PatchAsJsonAsync(
$"api/admin/roles/{Role.Id}",
Request,
SerializationContext.Default.Options
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);
@@ -86,7 +84,7 @@
}
await ToastService.SuccessAsync("Role update", $"Role {Request.Name} has been successfully updated");
await OnSubmit.Invoke();
await CloseAsync();

View File

@@ -1,17 +1,15 @@
using System.Diagnostics.CodeAnalysis;
namespace Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Admin.Users.Shared;
public class Permission
{
public string Identifier { get; init; }
public string Name { get; init; }
public string Description { get; init; }
public Permission(string identifier, string name, string description)
{
Identifier = identifier;
Name = name;
Description = description;
}
public string Identifier { get; init; }
public string Name { get; init; }
public string Description { get; init; }
}

View File

@@ -1,21 +1,22 @@
using System.Diagnostics.CodeAnalysis;
namespace Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Admin.Users.Shared;
public record PermissionCategory
{
public string Name { get; init; }
// Used to prevent the IL-Trimming from removing this type as its dynamically assigned a type, and we
// need it to work properly
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public Type Icon { get; init; }
public Permission[] Permissions { get; init; }
public PermissionCategory(string name, Type icon, Permission[] permissions)
{
Name = name;
Icon = icon;
Permissions = permissions;
}
public string Name { get; init; }
// Used to prevent the IL-Trimming from removing this type as its dynamically assigned a type, and we
// need it to work properly
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public Type Icon { get; init; }
public Permission[] Permissions { get; init; }
}

View File

@@ -1,10 +1,8 @@
@using Moonlight.Frontend.Interfaces
@using Moonlight.Frontend.Models
@using ShadcnBlazor.Extras.Common
@using Moonlight.Frontend.Infrastructure.Hooks
@using ShadcnBlazor.Accordions
@using ShadcnBlazor.Checkboxes
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Labels
@inject IEnumerable<IPermissionProvider> Providers
<LazyLoader Load="LoadAsync">

View File

@@ -1,12 +1,11 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Requests.Admin.Users
@using Moonlight.Shared.Http.Responses
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared.Admin.Users.Users
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@using SerializationContext = Moonlight.Shared.SerializationContext
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@@ -26,7 +25,7 @@
<FieldGroup>
<FormValidationSummary/>
<DataAnnotationsValidator />
<DataAnnotationsValidator/>
<FieldSet>
<Field>
@@ -58,7 +57,7 @@
protected override void OnInitialized()
{
Request = new();
Request = new CreateUserDto();
}
private async Task<bool> OnSubmitAsync(EditContext editContext, ValidationMessageStore validationMessageStore)

View File

@@ -1,14 +1,10 @@
@using Moonlight.Frontend.Helpers
@using Moonlight.Frontend.Mappers
@using Moonlight.Shared.Http.Requests.Admin.Users
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin.Users
@using Moonlight.Frontend.Infrastructure.Helpers
@using Moonlight.Shared.Admin.Users.Users
@using ShadcnBlazor.Dialogs
@using ShadcnBlazor.Extras.Forms
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Fields
@using ShadcnBlazor.Inputs
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
@inject HttpClient HttpClient
@@ -68,7 +64,7 @@
$"/api/admin/users/{User.Id}",
Request
);
if (!response.IsSuccessStatusCode)
{
await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Request, validationMessageStore);
@@ -79,7 +75,7 @@
"User update",
$"Successfully updated user {Request.Username}"
);
await OnCompleted.Invoke();
await CloseAsync();

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Moonlight.Shared.Http.Requests.Admin.Users;
using Moonlight.Shared.Http.Responses.Admin.Users;
using Moonlight.Shared.Admin.Users.Users;
using Riok.Mapperly.Abstractions;
namespace Moonlight.Frontend.Mappers;
namespace Moonlight.Frontend.Admin.Users.Users;
[Mapper]
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]

View File

@@ -1,19 +1,17 @@
@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.UI.Admin.Modals
@using Moonlight.Shared
@using Moonlight.Shared.Http
@using Moonlight.Shared.Admin.Users.Users
@using Moonlight.Shared.Shared
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.DataGrids
@using ShadcnBlazor.Dropdowns
@using ShadcnBlazor.Extras.AlertDialogs
@using ShadcnBlazor.Extras.Dialogs
@using ShadcnBlazor.Extras.Toasts
@using ShadcnBlazor.Tabels
@using Moonlight.Shared.Http.Requests
@using Moonlight.Shared.Http.Responses
@using Moonlight.Shared.Http.Responses.Admin.Users
@using ShadcnBlazor.Extras.Dialogs
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@inject AlertDialogService AlertDialogService
@@ -65,13 +63,15 @@
</Slot>
</DropdownMenuTrigger>
<DropdownMenuContent SideOffset="2">
<DropdownMenuItem OnClick="() => LogoutAsync(context)" Disabled="@(!LogoutAccess.Succeeded)">
<DropdownMenuItem OnClick="() => LogoutAsync(context)"
Disabled="@(!LogoutAccess.Succeeded)">
Logout
<DropdownMenuShortcut>
<LogOutIcon/>
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem OnClick="() => EditAsync(context)" Disabled="@(!EditAccess.Succeeded)">
<DropdownMenuItem OnClick="() => EditAsync(context)"
Disabled="@(!EditAccess.Succeeded)">
Edit
<DropdownMenuShortcut>
<PenIcon/>
@@ -97,9 +97,9 @@
@code
{
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private DataGrid<UserDto> Grid;
private AuthorizationResult LogoutAccess;
private AuthorizationResult EditAccess;
private AuthorizationResult DeleteAccess;
@@ -108,7 +108,7 @@
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
LogoutAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Users.Logout);
EditAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Users.Edit);
DeleteAccess = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.Users.Delete);
@@ -130,13 +130,7 @@
private async Task CreateAsync()
{
await DialogService.LaunchAsync<CreateUserDialog>(parameters =>
{
parameters[nameof(CreateUserDialog.OnCompleted)] = async () =>
{
await Grid.RefreshAsync();
};
});
await DialogService.LaunchAsync<CreateUserDialog>(parameters => { parameters[nameof(CreateUserDialog.OnCompleted)] = async () => { await Grid.RefreshAsync(); }; });
}
private async Task EditAsync(UserDto user)
@@ -144,10 +138,7 @@
await DialogService.LaunchAsync<UpdateUserDialog>(parameters =>
{
parameters[nameof(UpdateUserDialog.User)] = user;
parameters[nameof(UpdateUserDialog.OnCompleted)] = async () =>
{
await Grid.RefreshAsync();
};
parameters[nameof(UpdateUserDialog.OnCompleted)] = async () => { await Grid.RefreshAsync(); };
});
}

View File

@@ -1,48 +0,0 @@
using LucideBlazor;
using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Models;
using Moonlight.Shared;
namespace Moonlight.Frontend.Implementations;
public sealed class PermissionProvider : IPermissionProvider
{
public Task<PermissionCategory[]> GetPermissionsAsync()
{
return Task.FromResult<PermissionCategory[]>([
new PermissionCategory("Users", typeof(UserRoundIcon), [
new Permission(Permissions.Users.Create, "Create", "Create new users"),
new Permission(Permissions.Users.View, "View", "View all users"),
new Permission(Permissions.Users.Edit, "Edit", "Edit user details"),
new Permission(Permissions.Users.Delete, "Delete", "Delete user accounts"),
new Permission(Permissions.Users.Logout, "Logout", "Logout user accounts"),
]),
new PermissionCategory("Roles", typeof(UsersRoundIcon), [
new Permission(Permissions.Roles.Create, "Create", "Create new roles"),
new Permission(Permissions.Roles.View, "View", "View all roles"),
new Permission(Permissions.Roles.Edit, "Edit", "Edit role details"),
new Permission(Permissions.Roles.Delete, "Delete", "Delete role accounts"),
new Permission(Permissions.Roles.Members, "Members", "Manage role members"),
]),
new PermissionCategory("System", typeof(CogIcon), [
new Permission(Permissions.System.Info, "Info", "View system info"),
new Permission(Permissions.System.Diagnose, "Diagnose", "Run diagnostics"),
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.Settings, "Settings", "Change settings of the instance"),
]),
new PermissionCategory("API Keys", typeof(KeyIcon), [
new Permission(Permissions.ApiKeys.Create, "Create", "Create new API keys"),
new Permission(Permissions.ApiKeys.View, "View", "View all API keys"),
new Permission(Permissions.ApiKeys.Edit, "Edit", "Edit API key details"),
new Permission(Permissions.ApiKeys.Delete, "Delete", "Delete API keys"),
]),
new PermissionCategory("Themes", typeof(PaintRollerIcon), [
new Permission(Permissions.Themes.Create, "Create", "Create new theme"),
new Permission(Permissions.Themes.View, "View", "View all themes"),
new Permission(Permissions.Themes.Edit, "Edit", "Edit themes"),
new Permission(Permissions.Themes.Delete, "Delete", "Delete themes"),
]),
]);
}
}

View File

@@ -1,25 +1,26 @@
using System.Diagnostics.CodeAnalysis;
using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Infrastructure.Hooks;
namespace Moonlight.Frontend.Configuration;
namespace Moonlight.Frontend.Infrastructure.Configuration;
public class LayoutMiddlewareOptions
{
public IReadOnlyList<Type> Components => InnerComponents;
private readonly List<Type> InnerComponents = new();
public IReadOnlyList<Type> Components => InnerComponents;
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
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
public void Remove<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()
where T : LayoutMiddlewareBase
{
InnerComponents.Remove(typeof(T));
}

View File

@@ -1,27 +1,34 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
namespace Moonlight.Frontend.Configuration;
namespace Moonlight.Frontend.Infrastructure.Configuration;
public class LayoutPageOptions
{
public IReadOnlyList<LayoutPageComponent> Components => InnerComponents;
private readonly List<LayoutPageComponent> InnerComponents = new();
public IReadOnlyList<LayoutPageComponent> Components => InnerComponents;
public void Add<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(LayoutPageSlot slot, int order)
where T : ComponentBase
=> Add(typeof(T), slot, order);
{
Add(typeof(T), slot, order);
}
public void Add(Type componentType, LayoutPageSlot slot, int order)
=> InnerComponents.Add(new LayoutPageComponent(componentType, order, slot));
{
InnerComponents.Add(new LayoutPageComponent(componentType, order, slot));
}
public void Remove<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()
where T : ComponentBase
=> Remove(typeof(T));
{
Remove(typeof(T));
}
public void Remove(Type componentType)
=> InnerComponents.RemoveAll(x => x.ComponentType == componentType);
{
InnerComponents.RemoveAll(x => x.ComponentType == componentType);
}
}
public record LayoutPageComponent(Type ComponentType, int Order, LayoutPageSlot Slot);

View File

@@ -1,6 +1,6 @@
using System.Reflection;
namespace Moonlight.Frontend.Configuration;
namespace Moonlight.Frontend.Infrastructure.Configuration;
public class NavigationAssemblyOptions
{

View File

@@ -1,44 +1,44 @@
namespace Moonlight.Frontend;
namespace Moonlight.Frontend.Infrastructure.Helpers;
public static class Formatter
{
public static string FormatSize(long bytes, double conversionStep = 1024)
{
if (bytes == 0) return "0 B";
string[] units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
var unitIndex = 0;
double size = bytes;
while (size >= conversionStep && unitIndex < units.Length - 1)
{
size /= conversionStep;
unitIndex++;
}
var decimals = unitIndex == 0 ? 0 : 2;
return $"{Math.Round(size, decimals)} {units[unitIndex]}";
}
public static string FormatDuration(TimeSpan timeSpan)
{
var abs = timeSpan.Duration(); // Handle negative timespans
if (abs.TotalSeconds < 1)
return $"{abs.TotalMilliseconds:F0}ms";
if (abs.TotalMinutes < 1)
return $"{abs.TotalSeconds:F1}s";
if (abs.TotalHours < 1)
return $"{abs.Minutes}m {abs.Seconds}s";
if (abs.TotalDays < 1)
return $"{abs.Hours}h {abs.Minutes}m";
if (abs.TotalDays < 365)
return $"{abs.Days}d {abs.Hours}h";
var years = (int)(abs.TotalDays / 365);
var days = abs.Days % 365;
return days > 0 ? $"{years}y {days}d" : $"{years}y";

View File

@@ -1,30 +1,29 @@
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components.Forms;
using Moonlight.Shared.Http.Responses;
using Moonlight.Shared.Shared;
namespace Moonlight.Frontend.Helpers;
namespace Moonlight.Frontend.Infrastructure.Helpers;
public static class ProblemDetailsHelper
{
public static async Task HandleProblemDetailsAsync(HttpResponseMessage response, object model, ValidationMessageStore validationMessageStore)
public static async Task HandleProblemDetailsAsync(HttpResponseMessage response, object model,
ValidationMessageStore validationMessageStore)
{
var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>();
if (problemDetails == null)
{
response.EnsureSuccessStatusCode(); // Trigger exception when unable to parse
}
else
{
if(!string.IsNullOrEmpty(problemDetails.Detail))
if (!string.IsNullOrEmpty(problemDetails.Detail))
validationMessageStore.Add(new FieldIdentifier(model, string.Empty), problemDetails.Detail);
if (problemDetails.Errors != null)
{
foreach (var error in problemDetails.Errors)
{
foreach (var message in error.Value)
validationMessageStore.Add(new FieldIdentifier(model, error.Key), message);
}
}
foreach (var message in error.Value)
validationMessageStore.Add(new FieldIdentifier(model, error.Key), message);
}
}
}

View File

@@ -1,6 +1,6 @@
using Moonlight.Frontend.Models;
using Moonlight.Frontend.Admin.Users.Shared;
namespace Moonlight.Frontend.Interfaces;
namespace Moonlight.Frontend.Infrastructure.Hooks;
public interface IPermissionProvider
{

View File

@@ -0,0 +1,8 @@
using Moonlight.Frontend.Infrastructure.Models;
namespace Moonlight.Frontend.Infrastructure.Hooks;
public interface ISidebarProvider
{
public Task<SidebarItem[]> GetItemsAsync();
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.Frontend.Interfaces;
namespace Moonlight.Frontend.Infrastructure.Hooks;
public abstract class LayoutMiddlewareBase : ComponentBase
{

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Moonlight.Shared;
namespace Moonlight.Frontend.Implementations;
namespace Moonlight.Frontend.Infrastructure.Implementations;
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{

View File

@@ -2,22 +2,22 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Moonlight.Shared;
namespace Moonlight.Frontend.Implementations;
namespace Moonlight.Frontend.Infrastructure.Implementations;
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
{
private readonly DefaultAuthorizationPolicyProvider FallbackProvider;
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
{
FallbackProvider = new DefaultAuthorizationPolicyProvider(options);
}
public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
if (!policyName.StartsWith(Permissions.Prefix, StringComparison.OrdinalIgnoreCase))
return await FallbackProvider.GetPolicyAsync(policyName);
var policy = new AuthorizationPolicyBuilder();
policy.AddRequirements(new PermissionRequirement(policyName));
@@ -25,18 +25,22 @@ public class PermissionPolicyProvider : IAuthorizationPolicyProvider
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
=> FallbackProvider.GetDefaultPolicyAsync();
{
return FallbackProvider.GetDefaultPolicyAsync();
}
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
=> FallbackProvider.GetFallbackPolicyAsync();
{
return FallbackProvider.GetFallbackPolicyAsync();
}
}
public class PermissionRequirement : IAuthorizationRequirement
{
public string Identifier { get; }
public PermissionRequirement(string identifier)
{
Identifier = identifier;
}
public string Identifier { get; }
}

View File

@@ -0,0 +1,49 @@
using LucideBlazor;
using Moonlight.Frontend.Admin.Users.Shared;
using Moonlight.Frontend.Infrastructure.Hooks;
using Moonlight.Shared;
namespace Moonlight.Frontend.Infrastructure.Implementations;
public sealed class PermissionProvider : IPermissionProvider
{
public Task<PermissionCategory[]> GetPermissionsAsync()
{
return Task.FromResult<PermissionCategory[]>([
new PermissionCategory("Users", typeof(UserRoundIcon), [
new Permission(Permissions.Users.Create, "Create", "Create new users"),
new Permission(Permissions.Users.View, "View", "View all users"),
new Permission(Permissions.Users.Edit, "Edit", "Edit user details"),
new Permission(Permissions.Users.Delete, "Delete", "Delete user accounts"),
new Permission(Permissions.Users.Logout, "Logout", "Logout user accounts")
]),
new PermissionCategory("Roles", typeof(UsersRoundIcon), [
new Permission(Permissions.Roles.Create, "Create", "Create new roles"),
new Permission(Permissions.Roles.View, "View", "View all roles"),
new Permission(Permissions.Roles.Edit, "Edit", "Edit role details"),
new Permission(Permissions.Roles.Delete, "Delete", "Delete role accounts"),
new Permission(Permissions.Roles.Members, "Members", "Manage role members")
]),
new PermissionCategory("System", typeof(CogIcon), [
new Permission(Permissions.System.Info, "Info", "View system info"),
new Permission(Permissions.System.Diagnose, "Diagnose", "Run diagnostics"),
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.Settings, "Settings", "Change settings of the instance")
]),
new PermissionCategory("API Keys", typeof(KeyIcon), [
new Permission(Permissions.ApiKeys.Create, "Create", "Create new API keys"),
new Permission(Permissions.ApiKeys.View, "View", "View all API keys"),
new Permission(Permissions.ApiKeys.Edit, "Edit", "Edit API key details"),
new Permission(Permissions.ApiKeys.Delete, "Delete", "Delete API keys")
]),
new PermissionCategory("Themes", typeof(PaintRollerIcon), [
new Permission(Permissions.Themes.Create, "Create", "Create new theme"),
new Permission(Permissions.Themes.View, "View", "View all themes"),
new Permission(Permissions.Themes.Edit, "Edit", "Edit themes"),
new Permission(Permissions.Themes.Delete, "Delete", "Delete themes")
])
]);
}
}

View File

@@ -1,16 +1,16 @@
using LucideBlazor;
using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Models;
using Moonlight.Frontend.Infrastructure.Hooks;
using Moonlight.Frontend.Infrastructure.Models;
using Moonlight.Shared;
namespace Moonlight.Frontend.Implementations;
namespace Moonlight.Frontend.Infrastructure.Implementations;
public sealed class SidebarProvider : ISidebarProvider
{
public Task<SidebarItem[]> GetItemsAsync()
{
return Task.FromResult<SidebarItem[]>([
new()
new SidebarItem
{
Name = "Overview",
IconType = typeof(LayoutDashboardIcon),
@@ -18,7 +18,7 @@ public sealed class SidebarProvider : ISidebarProvider
IsExactPath = true,
Order = 0
},
new()
new SidebarItem
{
Name = "Overview",
IconType = typeof(LayoutDashboardIcon),
@@ -28,7 +28,7 @@ public sealed class SidebarProvider : ISidebarProvider
Order = 0,
Policy = Permissions.System.Info
},
new()
new SidebarItem
{
Name = "Users",
IconType = typeof(UsersRoundIcon),
@@ -38,7 +38,7 @@ public sealed class SidebarProvider : ISidebarProvider
Order = 10,
Policy = Permissions.Users.View
},
new()
new SidebarItem
{
Name = "System",
IconType = typeof(SettingsIcon),

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
namespace Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Infrastructure.Models;
public record SidebarItem
{

View File

@@ -3,17 +3,13 @@
@using LucideBlazor
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.Extensions.Options
@using Moonlight.Frontend.Configuration
@using Moonlight.Frontend.UI.Shared
@using Moonlight.Frontend.UI.Shared.Components
@using Moonlight.Frontend.Infrastructure.Configuration
@using Moonlight.Frontend.Shared.Auth
@using ShadcnBlazor.Emptys
@using Moonlight.Frontend.UI.Shared.Components.Auth
@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 IOptions<NavigationAssemblyOptions> NavigationOptions
@@ -22,7 +18,8 @@
<AuthorizeView>
<ChildContent>
<LayoutMiddleware>
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="Assemblies" NotFoundPage="typeof(NotFound)">
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="Assemblies"
NotFoundPage="typeof(NotFound)">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
<NotAuthorized Context="authRouteViewContext">
@@ -32,12 +29,12 @@
</Found>
</Router>
</LayoutMiddleware>
<ToastLauncher/>
<DialogLauncher/>
<AlertDialogLauncher/>
<PortalOutlet />
<PortalOutlet/>
</ChildContent>
<Authorizing>
<Authenticating/>
@@ -53,7 +50,7 @@
if (uri.LocalPath.StartsWith("/setup"))
{
<Setup />
<Setup/>
}
else
{

View File

@@ -1,5 +1,4 @@
@using ShadcnBlazor.Sidebars
<header
class="flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)"
style="--header-height: calc(var(--spacing) * 12)">

View File

@@ -1,16 +1,15 @@
@using System.Text.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.Interfaces
@using Moonlight.Frontend.Models
@using Moonlight.Frontend.Services
@using ShadcnBlazor.Sidebars
@inject NavigationManager Navigation
@inject NavigationManager Navigation
@inject IAuthorizationService AuthorizationService
@inject FrontendService FrontendService
@inject IEnumerable<ISidebarProvider> Providers
@using System.Text.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.Infrastructure.Hooks
@using Moonlight.Frontend.Infrastructure.Models
@using Moonlight.Frontend.Shared.Frontend
@using ShadcnBlazor.Sidebars
@implements IDisposable
@{
@@ -75,32 +74,32 @@
@code
{
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private readonly List<SidebarItem> Items = new();
private FrontendConfiguration? FrontendConfiguration;
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
foreach (var provider in Providers)
{
var items = await provider.GetItemsAsync();
foreach (var item in items)
{
if (!string.IsNullOrWhiteSpace(item.Policy))
{
var result = await AuthorizationService.AuthorizeAsync(authState.User, item.Policy);
if(!result.Succeeded)
if (!result.Succeeded)
continue;
}
Items.Add(item);
}
}
Navigation.LocationChanged += OnLocationChanged;
FrontendConfiguration = await FrontendService.GetConfigurationAsync();

View File

@@ -1,7 +1,6 @@
@using Microsoft.Extensions.Options
@using Moonlight.Frontend.Configuration
@using Moonlight.Frontend.Interfaces
@using Moonlight.Frontend.Infrastructure.Configuration
@using Moonlight.Frontend.Infrastructure.Hooks
@inject IOptions<LayoutMiddlewareOptions> Options
@Chain
@@ -9,7 +8,7 @@
@code
{
[Parameter] public RenderFragment ChildContent { get; set; }
private RenderFragment Chain;
protected override void OnInitialized()
@@ -21,7 +20,7 @@
// Capture current values
var currentChain = Chain;
var currentComponent = component;
Chain = builder =>
{
builder.OpenComponent(0, currentComponent);

View File

@@ -1,8 +1,7 @@
@using Microsoft.Extensions.Options
@using Moonlight.Frontend.Configuration
@using Moonlight.Frontend.Infrastructure.Configuration
@using ShadcnBlazor.Extras.Alerts
@using ShadcnBlazor.Sidebars
@inherits LayoutComponentBase
@inject IOptions<LayoutPageOptions> LayoutPageOptions
@@ -15,18 +14,18 @@
@foreach (var headerComponent in HeaderComponents)
{
<DynamicComponent Type="headerComponent" />
<DynamicComponent Type="headerComponent"/>
}
<div class="mx-8 my-8 max-w-full">
<AlertLauncher/>
@Body
</div>
@foreach (var footerComponent in FooterComponents)
{
<DynamicComponent Type="footerComponent" />
<DynamicComponent Type="footerComponent"/>
}
</SidebarInset>
</SidebarProvider>
@@ -35,7 +34,7 @@
{
private Type[] HeaderComponents;
private Type[] FooterComponents;
protected override void OnInitialized()
{
HeaderComponents = LayoutPageOptions.Value.Components
@@ -43,7 +42,7 @@
.OrderBy(x => x.Order)
.Select(x => x.ComponentType)
.ToArray();
FooterComponents = LayoutPageOptions.Value.Components
.Where(x => x.Slot == LayoutPageSlot.Footer)
.OrderBy(x => x.Order)

View File

@@ -5,7 +5,6 @@
@using ShadcnBlazor.Dropdowns
@using ShadcnBlazor.Interop
@using ShadcnBlazor.Sidebars
@inject NavigationManager Navigation
<SidebarMenu>
@@ -72,5 +71,8 @@
Email = authState.User.FindFirst(ClaimTypes.Email)?.Value ?? "N/A";
}
private void Logout() => Navigation.NavigateTo("/api/auth/logout", true);
private void Logout()
{
Navigation.NavigateTo("/api/auth/logout", true);
}
}

View File

@@ -1,8 +1,7 @@
@page "/notfound"
@using LucideBlazor
@using ShadcnBlazor.Emptys
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Emptys
<Empty>
<EmptyHeader>

View File

@@ -3,16 +3,15 @@ using System.Net.Http.Json;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic;
using Moonlight.Shared.Http;
using Moonlight.Shared.Http.Responses.Admin.Auth;
using Moonlight.Shared.Shared.Auth;
using SerializationContext = Moonlight.Shared.SerializationContext;
namespace Moonlight.Frontend.Services;
namespace Moonlight.Frontend.Infrastructure.Services;
public class RemoteAuthProvider : AuthenticationStateProvider
{
private readonly ILogger<RemoteAuthProvider> Logger;
private readonly HttpClient HttpClient;
private readonly ILogger<RemoteAuthProvider> Logger;
public RemoteAuthProvider(ILogger<RemoteAuthProvider> logger, HttpClient httpClient)
{

View File

@@ -1,8 +0,0 @@
using Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Interfaces;
public interface ISidebarProvider
{
public Task<SidebarItem[]> GetItemsAsync();
}

View File

@@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace Moonlight.Frontend.Models;
public class FrontendConfiguration
{
[JsonPropertyName("name")]
public string Name { get; set; }
}

View File

@@ -24,18 +24,47 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.3"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.3"/>
<PackageReference Include="Riok.Mapperly" Version="4.3.1"/>
<PackageReference Include="ShadcnBlazor" Version="1.0.14" />
<PackageReference Include="ShadcnBlazor.Extras" Version="1.0.14" />
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2" />
<PackageReference Include="ShadcnBlazor" Version="1.0.14"/>
<PackageReference Include="ShadcnBlazor.Extras" Version="1.0.14"/>
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj"/>
</ItemGroup>
<ItemGroup>
<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="buildTransitive\Moonlight.Frontend.targets" />
<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="buildTransitive\Moonlight.Frontend.targets"/>
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="UI\Shared\Components\Auth\AccessDenied.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Components\Auth\Authenticating.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Components\Auth\Authentication.razor"/>
<UpToDateCheckInput Remove="Admin\Diagnose\Index.razor"/>
<UpToDateCheckInput Remove="Admin\Themes\Create.razor"/>
<UpToDateCheckInput Remove="Admin\Themes\Index.razor"/>
<UpToDateCheckInput Remove="Admin\Themes\Update.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Partials\AppHeader.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Partials\AppSidebar.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Partials\MainLayout.razor"/>
<UpToDateCheckInput Remove="UI\Shared\Partials\NavUser.razor"/>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Admin\Sys\HelperContainer\Instance.razor"/>
<AdditionalFiles Include="Admin\Sys\Settings\Settings.razor"/>
<AdditionalFiles Include="Admin\Sys\Themes\Create.razor"/>
<AdditionalFiles Include="Admin\Sys\Themes\Index.razor"/>
<AdditionalFiles Include="Admin\Sys\Themes\Update.razor"/>
<AdditionalFiles Include="Infrastructure\Partials\AppHeader.razor"/>
<AdditionalFiles Include="Infrastructure\Partials\AppSidebar.razor"/>
<AdditionalFiles Include="Infrastructure\Partials\MainLayout.razor"/>
<AdditionalFiles Include="Infrastructure\Partials\NavUser.razor"/>
<AdditionalFiles Include="Shared\Auth\AccessDenied.razor"/>
<AdditionalFiles Include="Shared\Auth\Authenticating.razor"/>
<AdditionalFiles Include="Shared\Auth\Authentication.razor"/>
</ItemGroup>
</Project>

View File

@@ -7,9 +7,9 @@
<Target Name="Moonlight_CopyContents" BeforeTargets="Build">
<ItemGroup>
<Files Include="$(MSBuildThisFileDirectory)..\Styles\**\*" />
<Files Include="$(MSBuildThisFileDirectory)..\Styles\**\*"/>
</ItemGroup>
<Copy SourceFiles="@(Files)" DestinationFolder="$(MoonlightCssClassDir)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(Files)" DestinationFolder="$(MoonlightCssClassDir)" SkipUnchangedFiles="true"/>
</Target>
</Project>

View File

@@ -11,7 +11,12 @@ public abstract class MoonlightPlugin : IPluginModule
{
Plugins = plugins;
}
public virtual void PreBuild(WebAssemblyHostBuilder builder){}
public virtual void PostBuild(WebAssemblyHost application){}
public virtual void PreBuild(WebAssemblyHostBuilder builder)
{
}
public virtual void PostBuild(WebAssemblyHost application)
{
}
}

View File

@@ -1,6 +1,5 @@
@using LucideBlazor
@using ShadcnBlazor.Emptys
<div class="m-10 flex items-center justify-center">
<Empty>
<EmptyHeader>

View File

@@ -1,6 +1,5 @@
@using LucideBlazor
@using ShadcnBlazor.Emptys
<div class="h-screen w-full flex items-center justify-center">
<Empty>
<EmptyHeader>

View File

@@ -1,8 +1,8 @@
@using Moonlight.Shared.Http
@using Moonlight.Shared.Http.Responses.Admin.Auth
@using Moonlight.Shared.Shared.Auth
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Spinners
@using ShadcnBlazor.Buttons
@using SerializationContext = Moonlight.Shared.SerializationContext
@inject HttpClient HttpClient
@inject NavigationManager Navigation

View File

@@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
namespace Moonlight.Frontend.Shared.Frontend;
public class FrontendConfiguration
{
[JsonPropertyName("name")] public string Name { get; set; }
}

View File

@@ -1,7 +1,6 @@
using Microsoft.JSInterop;
using Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Services;
namespace Moonlight.Frontend.Shared.Frontend;
public class FrontendService
{
@@ -11,7 +10,7 @@ public class FrontendService
{
JsRuntime = jsRuntime;
}
public async Task<FrontendConfiguration> GetConfigurationAsync()
{
return await JsRuntime.InvokeAsync<FrontendConfiguration>("frontendConfig.getConfiguration");

View File

@@ -2,9 +2,9 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Moonlight.Frontend.Implementations;
using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Services;
using Moonlight.Frontend.Infrastructure.Hooks;
using Moonlight.Frontend.Infrastructure.Implementations;
using Moonlight.Frontend.Infrastructure.Services;
namespace Moonlight.Frontend.Startup;
@@ -17,7 +17,7 @@ public partial class Startup
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddSingleton<IPermissionProvider, PermissionProvider>();
builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
}

View File

@@ -2,12 +2,12 @@ using LucideBlazor;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Moonlight.Frontend.Configuration;
using Moonlight.Frontend.Implementations;
using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Services;
using Moonlight.Frontend.UI;
using Moonlight.Frontend.UI.Admin.Settings;
using Moonlight.Frontend.Admin.Sys.Settings;
using Moonlight.Frontend.Infrastructure.Configuration;
using Moonlight.Frontend.Infrastructure.Hooks;
using Moonlight.Frontend.Infrastructure.Implementations;
using Moonlight.Frontend.Infrastructure.Partials;
using Moonlight.Frontend.Shared.Frontend;
using ShadcnBlazor;
using ShadcnBlazor.Extras;

View File

@@ -14,6 +14,5 @@ public partial class Startup : MoonlightPlugin
public override void PostBuild(WebAssemblyHost application)
{
}
}

View File

@@ -7,7 +7,7 @@ public static class StartupHandler
public static async Task RunAsync(string[] args, MoonlightPlugin[] plugins)
{
Console.WriteLine($"Starting with: {string.Join(", ", plugins.Select(x => x.GetType().FullName))}");
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// Setting up context
@@ -21,7 +21,7 @@ public static class StartupHandler
var app = builder.Build();
// Stage 2: Post Build
foreach(var plugin in plugins)
foreach (var plugin in plugins)
plugin.PostBuild(app);
await app.RunAsync();

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="301px" viewBox="0 0 256 301" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<svg width="256px" height="301px" viewBox="0 0 256 301" version="1.1" xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid">
<defs>
<linearGradient x1="2.17771739%" y1="34.7938955%" x2="92.7221942%" y2="91.3419405%" id="linearGradient-1">
<stop stop-color="#41A7EF" offset="0%"></stop>
@@ -9,6 +10,7 @@
</linearGradient>
</defs>
<g>
<path d="M124.183681,101.699 C124.183681,66.515 136.256681,34.152 156.486681,8.525 C159.197681,5.092 156.787681,0.069 152.412681,0.012 C151.775681,0.004 151.136681,0 150.497681,0 C67.6206813,0 0.390681343,66.99 0.00168134279,149.775 C-0.386318657,232.369 66.4286813,300.195 149.019681,300.988 C189.884681,301.381 227.036681,285.484 254.376681,259.395 C257.519681,256.396 255.841681,251.082 251.548681,250.42 C179.413681,239.291 124.183681,176.949 124.183681,101.699" fill="url(#linearGradient-1)"></path>
<path d="M124.183681,101.699 C124.183681,66.515 136.256681,34.152 156.486681,8.525 C159.197681,5.092 156.787681,0.069 152.412681,0.012 C151.775681,0.004 151.136681,0 150.497681,0 C67.6206813,0 0.390681343,66.99 0.00168134279,149.775 C-0.386318657,232.369 66.4286813,300.195 149.019681,300.988 C189.884681,301.381 227.036681,285.484 254.376681,259.395 C257.519681,256.396 255.841681,251.082 251.548681,250.42 C179.413681,239.291 124.183681,176.949 124.183681,101.699"
fill="url(#linearGradient-1)"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB