From 8b1865ae0f24a60ed0bd064b827b7b360b80334f Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:58:02 +0200 Subject: [PATCH 01/46] Remove reading of 'developmentSettings.json' at boot --- src/Modix/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index a54b600c..9b8aa53c 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -36,7 +36,6 @@ public static int Main(string[] args) var configBuilder = builder.Configuration .AddEnvironmentVariables("MODIX_") - .AddJsonFile("developmentSettings.json", optional: true, reloadOnChange: false) .AddKeyPerFile("/run/secrets", true); if (builder.Environment.IsDevelopment()) From 7163a2f2e99522be243668a1d4384b51400afe9b Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:38:25 +0200 Subject: [PATCH 02/46] Migrate to new blazor conventions --- src/Modix.Web/App.razor | 120 ++++++++++++---------------- src/Modix.Web/Modix.Web.csproj | 1 + src/Modix.Web/Pages/_Host.cshtml | 47 ----------- src/Modix.Web/Routes.razor | 83 +++++++++++++++++++ src/Modix.Web/Setup.cs | 36 ++++++--- src/Modix.Web/Shared/MiniUser.razor | 4 +- 6 files changed, 160 insertions(+), 131 deletions(-) delete mode 100644 src/Modix.Web/Pages/_Host.cshtml create mode 100644 src/Modix.Web/Routes.razor diff --git a/src/Modix.Web/App.razor b/src/Modix.Web/App.razor index a3e8b765..126c8365 100644 --- a/src/Modix.Web/App.razor +++ b/src/Modix.Web/App.razor @@ -5,80 +5,58 @@ @using System.Security.Claims; @using Modix.Services.Core; - - - - - - Sorry, you don't have access to that page. - - - Please wait... - - - - - - Not found - - Sorry, there's nothing at this address. - - - - + + + + + + + + + + + + + + + + + + + + +
+ @if(Env.IsDevelopment()) + { + + An unhandled exception has occurred. See browser dev tools for details. + + } + else + { + + An error has occurred. This application may no longer respond until reloaded. + + } +
+ + + + + + + @code { - [Parameter] - public string? SelectedGuild { get; set; } - - [Parameter] - public string? ShowInfractionState { get; set; } - - [Parameter] - public string? ShowDeletedInfractions { get; set; } - - [Parameter] - public string? ShowInactivePromotions { get; set; } - - [Parameter] - public string? UseDarkMode { get; set; } - - [Inject] - public SessionState SessionState { get; set; } = null!; - - [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; [Inject] - public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; - - [Inject] - public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (!authState.User.Identity?.IsAuthenticated ?? false) - return; - - var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; - - _ = ulong.TryParse(userId, out var userSnowflake); - _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); - _ = bool.TryParse(ShowInfractionState, out var showInfractionState); - _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); - _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); - _ = bool.TryParse(UseDarkMode, out var useDarkMode); - - SessionState.CurrentUserId = userSnowflake; - SessionState.SelectedGuild = selectedGuildId; - SessionState.ShowInfractionState = showInfractionState; - SessionState.ShowDeletedInfractions = showDeletedInfractions; - SessionState.ShowInactivePromotions = showInactivePromotions; - SessionState.UseDarkMode = useDarkMode; - - var currentUser = DiscordHelper.GetCurrentUser(); + public required IWebHostEnvironment Env { get; set; } - await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); - } + [CascadingParameter] + public required HttpContext Context { get; set; } } \ No newline at end of file diff --git a/src/Modix.Web/Modix.Web.csproj b/src/Modix.Web/Modix.Web.csproj index e14aa282..f52ceea4 100644 --- a/src/Modix.Web/Modix.Web.csproj +++ b/src/Modix.Web/Modix.Web.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Modix.Web/Pages/_Host.cshtml b/src/Modix.Web/Pages/_Host.cshtml deleted file mode 100644 index 14ebc7c0..00000000 --- a/src/Modix.Web/Pages/_Host.cshtml +++ /dev/null @@ -1,47 +0,0 @@ -@page "/" -@using Microsoft.AspNetCore.Components.Web -@using Modix.Web.Models; -@namespace Modix.Web.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - - - - - - - - - - - - - - - - - - - - -
- - An error has occurred. This application may no longer respond until reloaded. - - - An unhandled exception has occurred. See browser dev tools for details. - - Reload - 🗙 -
- - - - - - diff --git a/src/Modix.Web/Routes.razor b/src/Modix.Web/Routes.razor new file mode 100644 index 00000000..fc544590 --- /dev/null +++ b/src/Modix.Web/Routes.razor @@ -0,0 +1,83 @@ +@using Modix.Web.Models +@using Modix.Web.Services +@using MudBlazor +@using System.Security.Claims + + + + @*TODO: AdditionalAssemblies="new [] { typeof(Client._Imports).Assembly) }"*@ + + + + + + + Sorry, you don't have access to that page. + + + Please wait... + + + + + + + + + +@code { + + [Parameter] + public string? SelectedGuild { get; set; } + + [Parameter] + public string? ShowInfractionState { get; set; } + + [Parameter] + public string? ShowDeletedInfractions { get; set; } + + [Parameter] + public string? ShowInactivePromotions { get; set; } + + [Parameter] + public string? UseDarkMode { get; set; } + + [Inject] + public SessionState SessionState { get; set; } = null!; + + [Inject] + public DiscordHelper DiscordHelper { get; set; } = null!; + + [Inject] + public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; + + [Inject] + public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity?.IsAuthenticated is not true) + return; + + var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; + + _ = ulong.TryParse(userId, out var userSnowflake); + _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); + _ = bool.TryParse(ShowInfractionState, out var showInfractionState); + _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); + _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); + _ = bool.TryParse(UseDarkMode, out var useDarkMode); + + SessionState.CurrentUserId = userSnowflake; + SessionState.SelectedGuild = selectedGuildId; + SessionState.ShowInfractionState = showInfractionState; + SessionState.ShowDeletedInfractions = showDeletedInfractions; + SessionState.ShowInactivePromotions = showInactivePromotions; + SessionState.UseDarkMode = useDarkMode; + + var currentUser = DiscordHelper.GetCurrentUser(); + + await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); + } +} \ No newline at end of file diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 2a619ac9..f2b9c965 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -13,7 +13,11 @@ public static class Setup { public static WebApplication ConfigureBlazorApplication(this WebApplication app) { - if (!app.Environment.IsDevelopment()) + if (app.Environment.IsDevelopment()) + { + app.UseWebAssemblyDebugging(); + } + else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. @@ -26,6 +30,8 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) app.UseRouting(); + app.UseAntiforgery(); + app.UseRequestLocalization("en-US"); app.UseMiddleware(); app.UseAuthorization(); @@ -33,22 +39,30 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) app.MapGet("/login", async (context) => await context.ChallengeAsync(DiscordAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); app.MapGet("/logout", async (context) => await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); - app.MapBlazorHub(); - app.MapFallbackToPage("/_Host"); + app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + //.AddInteractiveWebAssemblyRenderMode(); + //TODO + //.AddAdditionalAssemblies(typeof(Client._Imports).Assembly); return app; } public static IServiceCollection ConfigureBlazorServices(this IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddMudServices(); - services.AddMudMarkdownServices(); - - services.AddRazorPages(); - services.AddServerSideBlazor(); + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddMudServices() + .AddMudMarkdownServices(); + + services.AddCascadingAuthenticationState(); + + services + .AddRazorComponents() + .AddInteractiveServerComponents(); + //.AddInteractiveWebAssemblyComponents(); return services; } diff --git a/src/Modix.Web/Shared/MiniUser.razor b/src/Modix.Web/Shared/MiniUser.razor index 73d1bc30..551a69ef 100644 --- a/src/Modix.Web/Shared/MiniUser.razor +++ b/src/Modix.Web/Shared/MiniUser.razor @@ -72,14 +72,14 @@ return; var authState = await AuthenticationState; - if (!authState.User.Identity?.IsAuthenticated ?? false) + if (authState.User.Identity?.IsAuthenticated is not true) return; var avatarHash = authState.User.FindFirst(x => x.Type == DiscordAuthenticationConstants.Claims.AvatarHash)?.Value; var user = DiscordHelper.GetCurrentUser(); AvatarUrl = $"https://cdn.discordapp.com/avatars/{user!.Id}/{avatarHash}.png"; - Username = authState.User.Identity?.Name; + Username = authState.User.Identity.Name; GuildOptions = DiscordHelper.GetGuildOptions(); SelectedGuild = user.Guild; From 81b24d2f30211ff5505d91ca6f6c863992f42d3c Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:48:06 +0200 Subject: [PATCH 03/46] Add WASM client project --- Modix.Web.Wasm/Modix.Web.Wasm.csproj | 14 +++++ Modix.Web.Wasm/Pages/Counter.razor | 19 +++++++ Modix.Web.Wasm/Pages/Weather.razor | 57 +++++++++++++++++++ Modix.Web.Wasm/Program.cs | 14 +++++ Modix.Web.Wasm/Properties/launchSettings.json | 41 +++++++++++++ Modix.Web.Wasm/_Imports.razor | 9 +++ .../wwwroot/sample-data/weather.json | 27 +++++++++ Modix.sln | 14 +++++ src/Modix.Web/App.razor | 6 +- src/Modix.Web/Modix.Web.csproj | 1 + src/Modix.Web/Routes.razor | 4 +- src/Modix.Web/Setup.cs | 11 ++-- 12 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 Modix.Web.Wasm/Modix.Web.Wasm.csproj create mode 100644 Modix.Web.Wasm/Pages/Counter.razor create mode 100644 Modix.Web.Wasm/Pages/Weather.razor create mode 100644 Modix.Web.Wasm/Program.cs create mode 100644 Modix.Web.Wasm/Properties/launchSettings.json create mode 100644 Modix.Web.Wasm/_Imports.razor create mode 100644 Modix.Web.Wasm/wwwroot/sample-data/weather.json diff --git a/Modix.Web.Wasm/Modix.Web.Wasm.csproj b/Modix.Web.Wasm/Modix.Web.Wasm.csproj new file mode 100644 index 00000000..d1f1ae2b --- /dev/null +++ b/Modix.Web.Wasm/Modix.Web.Wasm.csproj @@ -0,0 +1,14 @@ + + + + enable + enable + true + Default + + + + + + + diff --git a/Modix.Web.Wasm/Pages/Counter.razor b/Modix.Web.Wasm/Pages/Counter.razor new file mode 100644 index 00000000..64f3e557 --- /dev/null +++ b/Modix.Web.Wasm/Pages/Counter.razor @@ -0,0 +1,19 @@ +@page "/counter" +@rendermode RenderMode.InteractiveAuto + +Counter + +

Counter

+ +

Current count: @currentCount

+ + + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/Modix.Web.Wasm/Pages/Weather.razor b/Modix.Web.Wasm/Pages/Weather.razor new file mode 100644 index 00000000..8a083fc2 --- /dev/null +++ b/Modix.Web.Wasm/Pages/Weather.razor @@ -0,0 +1,57 @@ +@page "/weather" +@inject HttpClient Http + +Weather + +

Weather

+ +

This component demonstrates fetching data from the server.

+ +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + + + + + + @foreach (var forecast in forecasts) + { + + + + + + + } + +
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
+} + +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() + { + forecasts = await Http.GetFromJsonAsync("sample-data/weather.json"); + } + + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public string? Summary { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/Modix.Web.Wasm/Program.cs b/Modix.Web.Wasm/Program.cs new file mode 100644 index 00000000..885d5637 --- /dev/null +++ b/Modix.Web.Wasm/Program.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace Modix.Web.Wasm; +public class Program +{ + public static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + + builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + + await builder.Build().RunAsync(); + } +} diff --git a/Modix.Web.Wasm/Properties/launchSettings.json b/Modix.Web.Wasm/Properties/launchSettings.json new file mode 100644 index 00000000..0bd25631 --- /dev/null +++ b/Modix.Web.Wasm/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:7032", + "sslPort": 44347 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5066", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7153;http://localhost:5066", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Modix.Web.Wasm/_Imports.razor b/Modix.Web.Wasm/_Imports.razor new file mode 100644 index 00000000..81b332c1 --- /dev/null +++ b/Modix.Web.Wasm/_Imports.razor @@ -0,0 +1,9 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using Modix.Web.Wasm diff --git a/Modix.Web.Wasm/wwwroot/sample-data/weather.json b/Modix.Web.Wasm/wwwroot/sample-data/weather.json new file mode 100644 index 00000000..b7459733 --- /dev/null +++ b/Modix.Web.Wasm/wwwroot/sample-data/weather.json @@ -0,0 +1,27 @@ +[ + { + "date": "2022-01-06", + "temperatureC": 1, + "summary": "Freezing" + }, + { + "date": "2022-01-07", + "temperatureC": 14, + "summary": "Bracing" + }, + { + "date": "2022-01-08", + "temperatureC": -13, + "summary": "Freezing" + }, + { + "date": "2022-01-09", + "temperatureC": -16, + "summary": "Balmy" + }, + { + "date": "2022-01-10", + "temperatureC": -2, + "summary": "Chilly" + } +] diff --git a/Modix.sln b/Modix.sln index e9b8da10..e1838e8d 100644 --- a/Modix.sln +++ b/Modix.sln @@ -64,6 +64,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web", "src\Modix.Web\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E74ADFBD-55F3-4E28-885B-2270670294EF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modix.Web.Wasm", "Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -218,6 +220,18 @@ Global {2280A9D0-358E-4668-8855-6832725C740A}.Release|x64.Build.0 = Release|Any CPU {2280A9D0-358E-4668-8855-6832725C740A}.Release|x86.ActiveCfg = Release|Any CPU {2280A9D0-358E-4668-8855-6832725C740A}.Release|x86.Build.0 = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|x64.ActiveCfg = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|x64.Build.0 = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|x86.ActiveCfg = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Debug|x86.Build.0 = Debug|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|Any CPU.Build.0 = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x64.ActiveCfg = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x64.Build.0 = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x86.ActiveCfg = Release|Any CPU + {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Modix.Web/App.razor b/src/Modix.Web/App.razor index 126c8365..250d6fa6 100644 --- a/src/Modix.Web/App.razor +++ b/src/Modix.Web/App.razor @@ -19,11 +19,11 @@ - + - } - + diff --git a/src/Modix.Web/Modix.Web.csproj b/src/Modix.Web/Modix.Web.csproj index f52ceea4..bc691848 100644 --- a/src/Modix.Web/Modix.Web.csproj +++ b/src/Modix.Web/Modix.Web.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Modix.Web/Routes.razor b/src/Modix.Web/Routes.razor index fc544590..7e98be06 100644 --- a/src/Modix.Web/Routes.razor +++ b/src/Modix.Web/Routes.razor @@ -5,10 +5,8 @@ - @*TODO: AdditionalAssemblies="new [] { typeof(Client._Imports).Assembly) }"*@ - - + diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index f2b9c965..5c16ef42 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -40,10 +40,9 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) app.MapGet("/logout", async (context) => await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); app.MapRazorComponents() - .AddInteractiveServerRenderMode(); - //.AddInteractiveWebAssemblyRenderMode(); - //TODO - //.AddAdditionalAssemblies(typeof(Client._Imports).Assembly); + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(Wasm._Imports).Assembly); return app; } @@ -61,8 +60,8 @@ public static IServiceCollection ConfigureBlazorServices(this IServiceCollection services .AddRazorComponents() - .AddInteractiveServerComponents(); - //.AddInteractiveWebAssemblyComponents(); + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); return services; } From 3d8d968c71ff0c35759ac185a625455f539157ca Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:55:50 +0200 Subject: [PATCH 04/46] Add Modix.Web.Shared project Move Routes/MainLayout component to Wasm project to enable Auto rendering mode --- .../Models/SessionState.cs | 0 Modix.Web.Shared/Modix.Web.Shared.csproj | 9 ++ Modix.Web.Wasm/Modix.Web.Wasm.csproj | 11 +++ Modix.Web.Wasm/Routes.razor | 88 +++++++++++++++++++ Modix.Web.Wasm/Shared/MainLayout.razor | 26 ++++++ Modix.sln | 14 +++ src/Modix.Web/App.razor | 5 +- src/Modix.Web/Modix.Web.csproj | 1 + src/Modix.Web/Routes.razor | 81 ----------------- src/Modix.Web/Setup.cs | 1 + 10 files changed, 153 insertions(+), 83 deletions(-) rename {src/Modix.Web => Modix.Web.Shared}/Models/SessionState.cs (100%) create mode 100644 Modix.Web.Shared/Modix.Web.Shared.csproj create mode 100644 Modix.Web.Wasm/Routes.razor create mode 100644 Modix.Web.Wasm/Shared/MainLayout.razor delete mode 100644 src/Modix.Web/Routes.razor diff --git a/src/Modix.Web/Models/SessionState.cs b/Modix.Web.Shared/Models/SessionState.cs similarity index 100% rename from src/Modix.Web/Models/SessionState.cs rename to Modix.Web.Shared/Models/SessionState.cs diff --git a/Modix.Web.Shared/Modix.Web.Shared.csproj b/Modix.Web.Shared/Modix.Web.Shared.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/Modix.Web.Shared/Modix.Web.Shared.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Modix.Web.Wasm/Modix.Web.Wasm.csproj b/Modix.Web.Wasm/Modix.Web.Wasm.csproj index d1f1ae2b..92f71171 100644 --- a/Modix.Web.Wasm/Modix.Web.Wasm.csproj +++ b/Modix.Web.Wasm/Modix.Web.Wasm.csproj @@ -9,6 +9,17 @@ + + + + + + + + + + + diff --git a/Modix.Web.Wasm/Routes.razor b/Modix.Web.Wasm/Routes.razor new file mode 100644 index 00000000..e03826b6 --- /dev/null +++ b/Modix.Web.Wasm/Routes.razor @@ -0,0 +1,88 @@ +@* @using Microsoft.AspNetCore.Components.Authorization *@ +@* @using Modix.Web.Models +@using Modix.Web.Services *@ +@using Modix.Web.Models +@using Modix.Web.Wasm.Shared +@using MudBlazor +@using System.Security.Claims + +@* TODO *@ +@* *@ + + + + + + + @* TODO *@ + @* + + Sorry, you don't have access to that page. + + + Please wait... + + *@ + + + + + +@* *@ + +@code { + + [Parameter] + public string? SelectedGuild { get; set; } + + [Parameter] + public string? ShowInfractionState { get; set; } + + [Parameter] + public string? ShowDeletedInfractions { get; set; } + + [Parameter] + public string? ShowInactivePromotions { get; set; } + + [Parameter] + public string? UseDarkMode { get; set; } + + [CascadingParameter] + public SessionState SessionState { get; set; } = null!; + + // [Inject] + // public DiscordHelper DiscordHelper { get; set; } = null!; + + // [Inject] + // public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; + + // [Inject] + // public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; + + // protected override async Task OnInitializedAsync() + // { + // var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + // if (authState.User.Identity?.IsAuthenticated is not true) + // return; + + // var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; + + // _ = ulong.TryParse(userId, out var userSnowflake); + // _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); + // _ = bool.TryParse(ShowInfractionState, out var showInfractionState); + // _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); + // _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); + // _ = bool.TryParse(UseDarkMode, out var useDarkMode); + + // SessionState.CurrentUserId = userSnowflake; + // SessionState.SelectedGuild = selectedGuildId; + // SessionState.ShowInfractionState = showInfractionState; + // SessionState.ShowDeletedInfractions = showDeletedInfractions; + // SessionState.ShowInactivePromotions = showInactivePromotions; + // SessionState.UseDarkMode = useDarkMode; + + // var currentUser = DiscordHelper.GetCurrentUser(); + + // await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); + // } +} \ No newline at end of file diff --git a/Modix.Web.Wasm/Shared/MainLayout.razor b/Modix.Web.Wasm/Shared/MainLayout.razor new file mode 100644 index 00000000..eb2ea92d --- /dev/null +++ b/Modix.Web.Wasm/Shared/MainLayout.razor @@ -0,0 +1,26 @@ +@using Modix.Web.Models +@using MudBlazor +@inherits LayoutComponentBase + +@* + + *@ + +@* TODO *@ +@* *@ + + + @* *@ + + + @Body + + + +@* *@ + +@code { + + [CascadingParameter] + public required SessionState SessionState { get; set; } +} diff --git a/Modix.sln b/Modix.sln index e1838e8d..6f78e9c4 100644 --- a/Modix.sln +++ b/Modix.sln @@ -66,6 +66,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E74ADFBD-5 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modix.Web.Wasm", "Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modix.Web.Shared", "Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -232,6 +234,18 @@ Global {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x64.Build.0 = Release|Any CPU {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x86.ActiveCfg = Release|Any CPU {F0DFE404-B6A9-495C-A41C-92FD25138619}.Release|x86.Build.0 = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|x64.ActiveCfg = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|x64.Build.0 = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|x86.ActiveCfg = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Debug|x86.Build.0 = Debug|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|Any CPU.Build.0 = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|x64.ActiveCfg = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|x64.Build.0 = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|x86.ActiveCfg = Release|Any CPU + {A603AE22-F588-466E-A7B1-298989E76890}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Modix.Web/App.razor b/src/Modix.Web/App.razor index 250d6fa6..f8dd74b2 100644 --- a/src/Modix.Web/App.razor +++ b/src/Modix.Web/App.razor @@ -1,6 +1,7 @@ @using Discord.WebSocket; @using Modix.Web.Models; @using Modix.Web.Services; +@using Modix.Web.Wasm @using MudBlazor @using System.Security.Claims; @using Modix.Services.Core; @@ -19,11 +20,11 @@ - + - + diff --git a/src/Modix.Web/Routes.razor b/src/Modix.Web/Routes.razor deleted file mode 100644 index 7e98be06..00000000 --- a/src/Modix.Web/Routes.razor +++ /dev/null @@ -1,81 +0,0 @@ -@using Modix.Web.Models -@using Modix.Web.Services -@using MudBlazor -@using System.Security.Claims - - - - - - - - - Sorry, you don't have access to that page. - - - Please wait... - - - - - - - - - -@code { - - [Parameter] - public string? SelectedGuild { get; set; } - - [Parameter] - public string? ShowInfractionState { get; set; } - - [Parameter] - public string? ShowDeletedInfractions { get; set; } - - [Parameter] - public string? ShowInactivePromotions { get; set; } - - [Parameter] - public string? UseDarkMode { get; set; } - - [Inject] - public SessionState SessionState { get; set; } = null!; - - [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; - - [Inject] - public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; - - [Inject] - public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity?.IsAuthenticated is not true) - return; - - var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; - - _ = ulong.TryParse(userId, out var userSnowflake); - _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); - _ = bool.TryParse(ShowInfractionState, out var showInfractionState); - _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); - _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); - _ = bool.TryParse(UseDarkMode, out var useDarkMode); - - SessionState.CurrentUserId = userSnowflake; - SessionState.SelectedGuild = selectedGuildId; - SessionState.ShowInfractionState = showInfractionState; - SessionState.ShowDeletedInfractions = showDeletedInfractions; - SessionState.ShowInactivePromotions = showInactivePromotions; - SessionState.UseDarkMode = useDarkMode; - - var currentUser = DiscordHelper.GetCurrentUser(); - - await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); - } -} \ No newline at end of file diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 5c16ef42..a3c98f72 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -53,6 +53,7 @@ public static IServiceCollection ConfigureBlazorServices(this IServiceCollection .AddScoped() .AddScoped() .AddScoped() + .AddCascadingValue(sp => sp.GetRequiredService()) .AddMudServices() .AddMudMarkdownServices(); From c1cf8d6d90b3618c3e5d4dccac0a3099e542d974 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:56:33 +0200 Subject: [PATCH 05/46] Parse selected guild closer to the actual usage in ClaimsMiddleware --- src/Modix.Web/Security/ClaimsMiddleware.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Modix.Web/Security/ClaimsMiddleware.cs b/src/Modix.Web/Security/ClaimsMiddleware.cs index 20b89069..ada0a226 100644 --- a/src/Modix.Web/Security/ClaimsMiddleware.cs +++ b/src/Modix.Web/Security/ClaimsMiddleware.cs @@ -16,15 +16,15 @@ public async Task InvokeAsync(HttpContext context, IAuthorizationService authori return; } - var selectedGuild = context.Request.Cookies[CookieConstants.SelectedGuild]; - _ = ulong.TryParse(selectedGuild, out var selectedGuildId); - if (context.User.Identity is not ClaimsIdentity claimsIdentity) { await next(context); return; } + var selectedGuild = context.Request.Cookies[CookieConstants.SelectedGuild]; + _ = ulong.TryParse(selectedGuild, out var selectedGuildId); + var currentGuild = discordClient.GetGuild(selectedGuildId) ?? discordClient.Guilds.First(); var currentUser = currentGuild.GetUser(userSnowflake); From 145ffc114485d7dfb1aef42d49218e8ab694c2f0 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:57:16 +0200 Subject: [PATCH 06/46] Read data from API --- Modix.Web.Wasm/Pages/Weather.razor | 8 +++- .../wwwroot/sample-data/weather.json | 27 ------------ src/Modix.Web/Controllers/ValuesController.cs | 44 +++++++++++++++++++ src/Modix.Web/Setup.cs | 2 + 4 files changed, 52 insertions(+), 29 deletions(-) delete mode 100644 Modix.Web.Wasm/wwwroot/sample-data/weather.json create mode 100644 src/Modix.Web/Controllers/ValuesController.cs diff --git a/Modix.Web.Wasm/Pages/Weather.razor b/Modix.Web.Wasm/Pages/Weather.razor index 8a083fc2..d31785ab 100644 --- a/Modix.Web.Wasm/Pages/Weather.razor +++ b/Modix.Web.Wasm/Pages/Weather.razor @@ -39,9 +39,13 @@ else @code { private WeatherForecast[]? forecasts; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - forecasts = await Http.GetFromJsonAsync("sample-data/weather.json"); + if (!firstRender) + return; + + forecasts = await Http.GetFromJsonAsync("/api/values/GetData"); + StateHasChanged(); } public class WeatherForecast diff --git a/Modix.Web.Wasm/wwwroot/sample-data/weather.json b/Modix.Web.Wasm/wwwroot/sample-data/weather.json deleted file mode 100644 index b7459733..00000000 --- a/Modix.Web.Wasm/wwwroot/sample-data/weather.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "date": "2022-01-06", - "temperatureC": 1, - "summary": "Freezing" - }, - { - "date": "2022-01-07", - "temperatureC": 14, - "summary": "Bracing" - }, - { - "date": "2022-01-08", - "temperatureC": -13, - "summary": "Freezing" - }, - { - "date": "2022-01-09", - "temperatureC": -16, - "summary": "Balmy" - }, - { - "date": "2022-01-10", - "temperatureC": -2, - "summary": "Chilly" - } -] diff --git a/src/Modix.Web/Controllers/ValuesController.cs b/src/Modix.Web/Controllers/ValuesController.cs new file mode 100644 index 00000000..64876486 --- /dev/null +++ b/src/Modix.Web/Controllers/ValuesController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Modix.Web.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class ValuesController : ControllerBase +{ + [HttpGet("getdata")] + public IActionResult GetData() + { + var e = """ + [ + { + "date": "2022-01-06", + "temperatureC": 1, + "summary": "Freezing" + }, + { + "date": "2022-01-07", + "temperatureC": 14, + "summary": "Bracing" + }, + { + "date": "2022-01-08", + "temperatureC": -13, + "summary": "Freezing" + }, + { + "date": "2022-01-09", + "temperatureC": -16, + "summary": "Balmy" + }, + { + "date": "2022-01-10", + "temperatureC": -2, + "summary": "Chilly" + } + ] + """; + return Ok(e); + } + +} diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index a3c98f72..5736c38c 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -36,6 +36,8 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) app.UseMiddleware(); app.UseAuthorization(); + app.MapControllers(); + app.MapGet("/login", async (context) => await context.ChallengeAsync(DiscordAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); app.MapGet("/logout", async (context) => await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); From 328b3a9fc081dc97a6c3589842230ab97a862977 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Fri, 28 Jun 2024 22:08:03 +0200 Subject: [PATCH 07/46] Reimplement the MudBlazor stuff in Wasm MainLayout --- Modix.Web.Wasm/Shared/MainLayout.razor | 24 ++++++++++++-- src/Modix.Web/Shared/MainLayout.razor | 45 -------------------------- 2 files changed, 22 insertions(+), 47 deletions(-) delete mode 100644 src/Modix.Web/Shared/MainLayout.razor diff --git a/Modix.Web.Wasm/Shared/MainLayout.razor b/Modix.Web.Wasm/Shared/MainLayout.razor index eb2ea92d..9cd36585 100644 --- a/Modix.Web.Wasm/Shared/MainLayout.razor +++ b/Modix.Web.Wasm/Shared/MainLayout.razor @@ -2,9 +2,9 @@ @using MudBlazor @inherits LayoutComponentBase -@* + - *@ + @* TODO *@ @* *@ @@ -23,4 +23,24 @@ [CascadingParameter] public required SessionState SessionState { get; set; } + + private MudTheme _theme = new() + { + Palette = new PaletteLight() + { + Primary = new("#803788"), + Surface = new("#fafafa") + }, + PaletteDark = new PaletteDark() + { + Primary = new("#803788"), + }, + Typography = new() + { + Default = new() + { + LetterSpacing = "0" + } + } + }; } diff --git a/src/Modix.Web/Shared/MainLayout.razor b/src/Modix.Web/Shared/MainLayout.razor deleted file mode 100644 index a7d8ba2c..00000000 --- a/src/Modix.Web/Shared/MainLayout.razor +++ /dev/null @@ -1,45 +0,0 @@ -@using Modix.Data.Models.Core; -@using Modix.Web.Models -@using MudBlazor -@inherits LayoutComponentBase - - - - - - - - - - - - @Body - - - - - -@code { - [Inject] - public required SessionState SessionState { get; set; } - - private MudTheme _theme = new() - { - Palette = new PaletteLight() - { - Primary = new("#803788"), - Surface = new("#fafafa") - }, - PaletteDark = new PaletteDark() - { - Primary = new("#803788"), - }, - Typography = new() - { - Default = new() - { - LetterSpacing = "0" - } - } - }; -} From ec2f29d2aaf9efeff9524d71002baf7887df564e Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 13:29:12 +0200 Subject: [PATCH 08/46] WIP possibly working auth --- Modix.Web.Shared/Models/DiscordUser.cs | 14 +++++ Modix.Web.Wasm/Modix.Web.Wasm.csproj | 3 +- Modix.Web.Wasm/Program.cs | 9 ++- Modix.Web.Wasm/Routes.razor | 47 ++++++++------- .../PersistentAuthenticationStateProvider.cs | 29 +++++++++ Modix.sln | 12 ++-- src/Modix.Web/Modix.Web.csproj | 2 +- .../PersistingAuthenticationStateProvider.cs | 60 +++++++++++++++++++ src/Modix.Web/Setup.cs | 2 + src/Modix/Program.cs | 2 +- 10 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 Modix.Web.Shared/Models/DiscordUser.cs create mode 100644 Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs create mode 100644 src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs diff --git a/Modix.Web.Shared/Models/DiscordUser.cs b/Modix.Web.Shared/Models/DiscordUser.cs new file mode 100644 index 00000000..3ed361d7 --- /dev/null +++ b/Modix.Web.Shared/Models/DiscordUser.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Modix.Web.Shared.Models; + +public class DiscordUser +{ + public required ulong UserId { get; init; } + public required string Name { get; init; } + public required string AvatarHash { get; init; } +} diff --git a/Modix.Web.Wasm/Modix.Web.Wasm.csproj b/Modix.Web.Wasm/Modix.Web.Wasm.csproj index 92f71171..6f2d9662 100644 --- a/Modix.Web.Wasm/Modix.Web.Wasm.csproj +++ b/Modix.Web.Wasm/Modix.Web.Wasm.csproj @@ -8,7 +8,8 @@ - + + diff --git a/Modix.Web.Wasm/Program.cs b/Modix.Web.Wasm/Program.cs index 885d5637..3e070d30 100644 --- a/Modix.Web.Wasm/Program.cs +++ b/Modix.Web.Wasm/Program.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Modix.Web.Wasm.Security; namespace Modix.Web.Wasm; public class Program @@ -9,6 +11,11 @@ public static async Task Main(string[] args) builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + builder.Services + .AddAuthorizationCore() + .AddCascadingAuthenticationState() + .AddSingleton(); + await builder.Build().RunAsync(); } } diff --git a/Modix.Web.Wasm/Routes.razor b/Modix.Web.Wasm/Routes.razor index e03826b6..904471bf 100644 --- a/Modix.Web.Wasm/Routes.razor +++ b/Modix.Web.Wasm/Routes.razor @@ -1,6 +1,7 @@ @* @using Microsoft.AspNetCore.Components.Authorization *@ @* @using Modix.Web.Models @using Modix.Web.Services *@ +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models @using Modix.Web.Wasm.Shared @using MudBlazor @@ -53,36 +54,36 @@ // [Inject] // public DiscordHelper DiscordHelper { get; set; } = null!; - // [Inject] - // public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; + [Inject] + public AuthenticationStateProvider AuthenticationStateProvider { get; set; } = null!; // [Inject] // public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; - // protected override async Task OnInitializedAsync() - // { - // var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - // if (authState.User.Identity?.IsAuthenticated is not true) - // return; + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity?.IsAuthenticated is not true) + return; - // var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; + var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; - // _ = ulong.TryParse(userId, out var userSnowflake); - // _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); - // _ = bool.TryParse(ShowInfractionState, out var showInfractionState); - // _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); - // _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); - // _ = bool.TryParse(UseDarkMode, out var useDarkMode); + _ = ulong.TryParse(userId, out var userSnowflake); + _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); + _ = bool.TryParse(ShowInfractionState, out var showInfractionState); + _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); + _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); + _ = bool.TryParse(UseDarkMode, out var useDarkMode); - // SessionState.CurrentUserId = userSnowflake; - // SessionState.SelectedGuild = selectedGuildId; - // SessionState.ShowInfractionState = showInfractionState; - // SessionState.ShowDeletedInfractions = showDeletedInfractions; - // SessionState.ShowInactivePromotions = showInactivePromotions; - // SessionState.UseDarkMode = useDarkMode; + SessionState.CurrentUserId = userSnowflake; + SessionState.SelectedGuild = selectedGuildId; + SessionState.ShowInfractionState = showInfractionState; + SessionState.ShowDeletedInfractions = showDeletedInfractions; + SessionState.ShowInactivePromotions = showInactivePromotions; + SessionState.UseDarkMode = useDarkMode; - // var currentUser = DiscordHelper.GetCurrentUser(); + // var currentUser = DiscordHelper.GetCurrentUser(); - // await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); - // } + // await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); + } } \ No newline at end of file diff --git a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs new file mode 100644 index 00000000..ee361e94 --- /dev/null +++ b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Components; +using System.Security.Claims; +using Microsoft.AspNetCore.Components.Authorization; +using Modix.Web.Shared.Models; + +namespace Modix.Web.Wasm.Security; + +public class PersistentAuthenticationStateProvider : AuthenticationStateProvider +{ + private static readonly Task DefaultUnauthenticatedTask = Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); + + private readonly Task _authenticationStateTask = DefaultUnauthenticatedTask; + + public PersistentAuthenticationStateProvider(PersistentComponentState state) + { + if (!state.TryTakeFromJson(nameof(DiscordUser), out var userInfo) || userInfo is null) + return; + + Claim[] claims = [ + new Claim(ClaimTypes.NameIdentifier, userInfo.UserId.ToString()), + new Claim(ClaimTypes.Name, userInfo.Name) ]; + + _authenticationStateTask = Task.FromResult( + new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, + authenticationType: nameof(PersistentAuthenticationStateProvider))))); + } + + public override Task GetAuthenticationStateAsync() => _authenticationStateTask; +} diff --git a/Modix.sln b/Modix.sln index 6f78e9c4..f19aa0f2 100644 --- a/Modix.sln +++ b/Modix.sln @@ -64,9 +64,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web", "src\Modix.Web\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E74ADFBD-55F3-4E28-885B-2270670294EF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modix.Web.Wasm", "Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Wasm", "Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modix.Web.Shared", "Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Shared", "Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -251,12 +251,12 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {092916B2-32C6-4ED9-A4EA-DF31D2F91A8F} = {23DA774D-7AE9-48C1-A261-F27D15A07858} - {9A28A475-067B-4CBD-94BC-CA31C0D1555A} = {E74ADFBD-55F3-4E28-885B-2270670294EF} - {4F9BDC85-B8B2-4AC5-99BC-1F2F0CF80016} = {E74ADFBD-55F3-4E28-885B-2270670294EF} + {E8C7267F-5BC3-4A13-B5EF-60DC1B0E919F} = {E74ADFBD-55F3-4E28-885B-2270670294EF} {D9BFC7C7-10B2-4D61-99EA-79805074CCD7} = {E74ADFBD-55F3-4E28-885B-2270670294EF} + {4F9BDC85-B8B2-4AC5-99BC-1F2F0CF80016} = {E74ADFBD-55F3-4E28-885B-2270670294EF} {E9D29AA9-1B7D-4A90-839A-2E8665A8714F} = {E74ADFBD-55F3-4E28-885B-2270670294EF} - {E8C7267F-5BC3-4A13-B5EF-60DC1B0E919F} = {E74ADFBD-55F3-4E28-885B-2270670294EF} + {092916B2-32C6-4ED9-A4EA-DF31D2F91A8F} = {23DA774D-7AE9-48C1-A261-F27D15A07858} + {9A28A475-067B-4CBD-94BC-CA31C0D1555A} = {E74ADFBD-55F3-4E28-885B-2270670294EF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36BDDB86-FBAB-45BF-AA22-DD4509504772} diff --git a/src/Modix.Web/Modix.Web.csproj b/src/Modix.Web/Modix.Web.csproj index 18adbede..d56dafda 100644 --- a/src/Modix.Web/Modix.Web.csproj +++ b/src/Modix.Web/Modix.Web.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs b/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs new file mode 100644 index 00000000..15c26686 --- /dev/null +++ b/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs @@ -0,0 +1,60 @@ +using System.Diagnostics; +using System.Security.Claims; +using AspNet.Security.OAuth.Discord; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Components.Web; +using Modix.Web.Shared.Models; + +namespace Modix.Web.Security; + +public class PersistingAuthenticationStateProvider : ServerAuthenticationStateProvider, IDisposable +{ + private readonly PersistentComponentState _state; + private readonly PersistingComponentStateSubscription _subscription; + private Task? _authenticationStateTask; + + public PersistingAuthenticationStateProvider(PersistentComponentState persistentComponentState) + { + _state = persistentComponentState; + _subscription = _state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveAuto); + + AuthenticationStateChanged += OnAuthenticationStateChanged; + } + + private void OnAuthenticationStateChanged(Task task) + { + _authenticationStateTask = task; + } + + private async Task OnPersistingAsync() + { + if(_authenticationStateTask is null) + throw new UnreachableException($"Authentication state not set in {nameof(OnPersistingAsync)}()."); + + var authState = await _authenticationStateTask; + var user = authState.User; + if (user.Identity?.IsAuthenticated is not true) + return; + + var userId = user.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; + var avatarHash = user.FindFirst(DiscordAuthenticationConstants.Claims.AvatarHash); + if (avatarHash is null || user.Identity.Name is null || !ulong.TryParse(userId, out var userSnowflake)) + return; + + _state.PersistAsJson(nameof(DiscordUser), new DiscordUser + { + UserId = userSnowflake, + Name = user.Identity.Name, + AvatarHash = avatarHash.Value, + }); + } + + public void Dispose() + { + _authenticationStateTask?.Dispose(); + AuthenticationStateChanged -= OnAuthenticationStateChanged; + _subscription.Dispose(); + } +} diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 5736c38c..9f89e26f 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -1,6 +1,7 @@ using AspNet.Security.OAuth.Discord; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Components.Authorization; using Modix.Web.Models; using Modix.Web.Security; using Modix.Web.Services; @@ -59,6 +60,7 @@ public static IServiceCollection ConfigureBlazorServices(this IServiceCollection .AddMudServices() .AddMudMarkdownServices(); + services.AddScoped(); services.AddCascadingAuthenticationState(); services diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index 9b8aa53c..9f6f68b1 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -120,7 +120,7 @@ private static void ConfigureServices(WebApplicationBuilder builder, IConfigurat builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { - options.LoginPath = "/api/unauthorized"; + options.LoginPath = "/login"; //options.LogoutPath = "/logout"; options.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0); }) From ef9d0e3e8afdf3d1c80d477fc2acf30b58ad7305 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:08:06 +0200 Subject: [PATCH 09/46] Flow claims to wasm client as well --- Modix.Web.Shared/Models/DiscordUser.cs | 9 ++------- .../Security/PersistentAuthenticationStateProvider.cs | 9 ++++++--- .../Security/PersistingAuthenticationStateProvider.cs | 1 + 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Modix.Web.Shared/Models/DiscordUser.cs b/Modix.Web.Shared/Models/DiscordUser.cs index 3ed361d7..c9cd8176 100644 --- a/Modix.Web.Shared/Models/DiscordUser.cs +++ b/Modix.Web.Shared/Models/DiscordUser.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Modix.Web.Shared.Models; +namespace Modix.Web.Shared.Models; public class DiscordUser { public required ulong UserId { get; init; } public required string Name { get; init; } public required string AvatarHash { get; init; } + public required IEnumerable Claims { get; set; } } diff --git a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs index ee361e94..aedd4a61 100644 --- a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs +++ b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs @@ -20,9 +20,12 @@ public PersistentAuthenticationStateProvider(PersistentComponentState state) new Claim(ClaimTypes.NameIdentifier, userInfo.UserId.ToString()), new Claim(ClaimTypes.Name, userInfo.Name) ]; - _authenticationStateTask = Task.FromResult( - new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, - authenticationType: nameof(PersistentAuthenticationStateProvider))))); + var roles = userInfo.Claims.Select(role => new Claim(ClaimTypes.Role, role)); + + var claimsIdentity = new ClaimsIdentity([..claims, ..roles], authenticationType: nameof(PersistentAuthenticationStateProvider)); + var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); + var authState = new AuthenticationState(claimsPrincipal); + _authenticationStateTask = Task.FromResult(authState); } public override Task GetAuthenticationStateAsync() => _authenticationStateTask; diff --git a/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs b/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs index 15c26686..9ad9b5d7 100644 --- a/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs +++ b/src/Modix.Web/Security/PersistingAuthenticationStateProvider.cs @@ -48,6 +48,7 @@ private async Task OnPersistingAsync() UserId = userSnowflake, Name = user.Identity.Name, AvatarHash = avatarHash.Value, + Claims = user.Claims.Where(x => x.Type == ClaimTypes.Role).Select(x => x.Value) }); } From a6e89817b77de482b568cccf95dc3c792e8b83f2 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:19:39 +0200 Subject: [PATCH 10/46] Move navbar related components over to the wasm project --- .../Components}/MiniUser.razor | 40 ++++++++++--------- .../Components}/NavMenu.razor | 15 +++---- .../Components}/NavMenuLinks.razor | 18 +++++---- .../Pages/Index.razor | 0 Modix.Web.Wasm/Program.cs | 11 +++++ Modix.Web.Wasm/Shared/MainLayout.razor | 3 +- 6 files changed, 53 insertions(+), 34 deletions(-) rename {src/Modix.Web/Shared => Modix.Web.Wasm/Components}/MiniUser.razor (65%) rename {src/Modix.Web/Shared => Modix.Web.Wasm/Components}/NavMenu.razor (85%) rename {src/Modix.Web/Shared => Modix.Web.Wasm/Components}/NavMenuLinks.razor (71%) rename {src/Modix.Web => Modix.Web.Wasm}/Pages/Index.razor (100%) diff --git a/src/Modix.Web/Shared/MiniUser.razor b/Modix.Web.Wasm/Components/MiniUser.razor similarity index 65% rename from src/Modix.Web/Shared/MiniUser.razor rename to Modix.Web.Wasm/Components/MiniUser.razor index 551a69ef..03e9ec18 100644 --- a/src/Modix.Web/Shared/MiniUser.razor +++ b/Modix.Web.Wasm/Components/MiniUser.razor @@ -1,7 +1,8 @@ -@using AspNet.Security.OAuth.Discord; -@using Discord.WebSocket; +@* @using AspNet.Security.OAuth.Discord; *@ +@* @using Discord.WebSocket; *@ +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@using Modix.Web.Services; +@* @using Modix.Web.Services; *@ @using MudBlazor
@@ -24,18 +25,19 @@
- + @* *@ +
- @foreach (var guildOption in GuildOptions) +@* @foreach (var guildOption in GuildOptions) { @guildOption.Name - } + } *@
@@ -51,17 +53,17 @@ [CascadingParameter] public Task? AuthenticationState { get; set; } = null!; - [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + // [Inject] + // public DiscordHelper DiscordHelper { get; set; } = null!; private string? AvatarUrl { get; set; } private string? Username { get; set; } - private IEnumerable GuildOptions { get; set; } = Array.Empty(); - private SocketGuild? SelectedGuild { get; set; } + // private IEnumerable GuildOptions { get; set; } = Array.Empty(); + // private SocketGuild? SelectedGuild { get; set; } - [Inject] - public CookieService CookieService { get; set; } = null!; + // [Inject] + // public CookieService CookieService { get; set; } = null!; [Inject] public NavigationManager NavigationManager { get; set; } = null!; @@ -75,19 +77,19 @@ if (authState.User.Identity?.IsAuthenticated is not true) return; - var avatarHash = authState.User.FindFirst(x => x.Type == DiscordAuthenticationConstants.Claims.AvatarHash)?.Value; - var user = DiscordHelper.GetCurrentUser(); + // var avatarHash = authState.User.FindFirst(x => x.Type == DiscordAuthenticationConstants.Claims.AvatarHash)?.Value; + // var user = DiscordHelper.GetCurrentUser(); - AvatarUrl = $"https://cdn.discordapp.com/avatars/{user!.Id}/{avatarHash}.png"; - Username = authState.User.Identity.Name; + // AvatarUrl = $"https://cdn.discordapp.com/avatars/{user!.Id}/{avatarHash}.png"; + // Username = authState.User.Identity.Name; - GuildOptions = DiscordHelper.GetGuildOptions(); - SelectedGuild = user.Guild; + // GuildOptions = DiscordHelper.GetGuildOptions(); + // SelectedGuild = user.Guild; } private async Task SelectGuild(ulong guildId) { - await CookieService.SetSelectedGuildAsync(guildId); + // await CookieService.SetSelectedGuildAsync(guildId); NavigationManager.NavigateTo(NavigationManager.Uri, true); } } diff --git a/src/Modix.Web/Shared/NavMenu.razor b/Modix.Web.Wasm/Components/NavMenu.razor similarity index 85% rename from src/Modix.Web/Shared/NavMenu.razor rename to Modix.Web.Wasm/Components/NavMenu.razor index e483be3c..99387fa5 100644 --- a/src/Modix.Web/Shared/NavMenu.razor +++ b/Modix.Web.Wasm/Components/NavMenu.razor @@ -1,8 +1,9 @@ -@using AspNet.Security.OAuth.Discord; -@using Discord.WebSocket; -@using Modix.Data.Models.Core; +@* @using AspNet.Security.OAuth.Discord; +@using Discord.WebSocket; *@ +@using Microsoft.AspNetCore.Components.Authorization +@* @using Modix.Data.Models.Core; *@ @using Modix.Web.Models; -@using Modix.Web.Services; +@* @using Modix.Web.Services; *@ @using MudBlazor; @using System.Security.Claims; @@ -43,8 +44,8 @@
@code { - [Inject] - public required CookieService CookieService { get; set; } + // [Inject] + // public required CookieService CookieService { get; set; } [Parameter] public bool DarkMode { get; set; } @@ -60,6 +61,6 @@ { DarkMode = toggled; await DarkModeChanged.InvokeAsync(DarkMode); - await CookieService.SetUseDarkModeAsync(DarkMode); + // await CookieService.SetUseDarkModeAsync(DarkMode); } } diff --git a/src/Modix.Web/Shared/NavMenuLinks.razor b/Modix.Web.Wasm/Components/NavMenuLinks.razor similarity index 71% rename from src/Modix.Web/Shared/NavMenuLinks.razor rename to Modix.Web.Wasm/Components/NavMenuLinks.razor index c2484441..386ecf4a 100644 --- a/src/Modix.Web/Shared/NavMenuLinks.razor +++ b/Modix.Web.Wasm/Components/NavMenuLinks.razor @@ -1,8 +1,9 @@ -@using AspNet.Security.OAuth.Discord; -@using Discord.WebSocket; -@using Modix.Data.Models.Core; +@* @using AspNet.Security.OAuth.Discord; *@ +@* @using Discord.WebSocket; *@ +@* @using Modix.Data.Models.Core; *@ +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@using Modix.Web.Services; +@* @using Modix.Web.Services; *@ @using MudBlazor @@ -14,15 +15,18 @@ User Lookup Tags - + @* *@ + Promotions - + @* *@ + Logs - + @* *@ + Config diff --git a/src/Modix.Web/Pages/Index.razor b/Modix.Web.Wasm/Pages/Index.razor similarity index 100% rename from src/Modix.Web/Pages/Index.razor rename to Modix.Web.Wasm/Pages/Index.razor diff --git a/Modix.Web.Wasm/Program.cs b/Modix.Web.Wasm/Program.cs index 3e070d30..ade53711 100644 --- a/Modix.Web.Wasm/Program.cs +++ b/Modix.Web.Wasm/Program.cs @@ -1,6 +1,9 @@ using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Modix.Web.Models; using Modix.Web.Wasm.Security; +using MudBlazor; +using MudBlazor.Services; namespace Modix.Web.Wasm; public class Program @@ -9,6 +12,10 @@ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.Services.AddScoped(); + + builder.Services.AddCascadingValue(sp => sp.GetRequiredService()); + builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services @@ -16,6 +23,10 @@ public static async Task Main(string[] args) .AddCascadingAuthenticationState() .AddSingleton(); + builder.Services + .AddMudServices() + .AddMudMarkdownServices(); + await builder.Build().RunAsync(); } } diff --git a/Modix.Web.Wasm/Shared/MainLayout.razor b/Modix.Web.Wasm/Shared/MainLayout.razor index 9cd36585..7740b8d2 100644 --- a/Modix.Web.Wasm/Shared/MainLayout.razor +++ b/Modix.Web.Wasm/Shared/MainLayout.razor @@ -1,4 +1,5 @@ @using Modix.Web.Models +@using Modix.Web.Wasm.Components @using MudBlazor @inherits LayoutComponentBase @@ -10,7 +11,7 @@ @* *@ - @* *@ + @Body From f80d5573638c6b3cb1aeb941880831f0179ba972 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:33:40 +0200 Subject: [PATCH 11/46] Stop using CascadingAuthenticationState --- Modix.Web.Wasm/Components/MiniUser.razor | 5 +- Modix.Web.Wasm/Components/NavMenu.razor | 72 +++++++++----------- Modix.Web.Wasm/Components/NavMenuLinks.razor | 70 +++++++++---------- Modix.Web.Wasm/Routes.razor | 54 +++++++-------- Modix.Web.Wasm/Shared/MainLayout.razor | 15 ++-- 5 files changed, 96 insertions(+), 120 deletions(-) diff --git a/Modix.Web.Wasm/Components/MiniUser.razor b/Modix.Web.Wasm/Components/MiniUser.razor index 03e9ec18..9a91f48d 100644 --- a/Modix.Web.Wasm/Components/MiniUser.razor +++ b/Modix.Web.Wasm/Components/MiniUser.razor @@ -1,8 +1,5 @@ -@* @using AspNet.Security.OAuth.Discord; *@ -@* @using Discord.WebSocket; *@ -@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@* @using Modix.Web.Services; *@ @using MudBlazor
diff --git a/Modix.Web.Wasm/Components/NavMenu.razor b/Modix.Web.Wasm/Components/NavMenu.razor index 99387fa5..0b77eac6 100644 --- a/Modix.Web.Wasm/Components/NavMenu.razor +++ b/Modix.Web.Wasm/Components/NavMenu.razor @@ -1,47 +1,39 @@ -@* @using AspNet.Security.OAuth.Discord; -@using Discord.WebSocket; *@ -@using Microsoft.AspNetCore.Components.Authorization -@* @using Modix.Data.Models.Core; *@ +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@* @using Modix.Web.Services; *@ @using MudBlazor; @using System.Security.Claims; - - - -
- -
- -
- -
- - - @* The color: inherit here is needed to keep the colors consistent between link icons and light/dark theme icons - I would suggest not thinking about it too much :) *@ - - - - - -
- - - - - - - -
+ +
+ +
+ +
+ +
+ + + @* The color: inherit here is needed to keep the colors consistent between link icons and light/dark theme icons + I would suggest not thinking about it too much :) *@ + + + + + +
+ + + + + + @code { // [Inject] diff --git a/Modix.Web.Wasm/Components/NavMenuLinks.razor b/Modix.Web.Wasm/Components/NavMenuLinks.razor index 386ecf4a..bed8a9ab 100644 --- a/Modix.Web.Wasm/Components/NavMenuLinks.razor +++ b/Modix.Web.Wasm/Components/NavMenuLinks.razor @@ -1,48 +1,42 @@ -@* @using AspNet.Security.OAuth.Discord; *@ -@* @using Discord.WebSocket; *@ -@* @using Modix.Data.Models.Core; *@ -@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@* @using Modix.Web.Services; *@ @using MudBlazor - - - - Home - Stats - Commands - User Lookup - Tags + + + Home + Stats + Commands + User Lookup + Tags - @* *@ - - Promotions - + @* *@ + + Promotions + - @* *@ - - Logs - + @* *@ + + Logs + - @* *@ - - Config - + @* *@ + + Config + - - -
- Home - Commands -
- -
- Log In -
-
-
-
+ + +
+ Home + Commands +
+ +
+ Log In +
+
+ @code { + [Inject] - public ICommandHelpService CommandHelpService { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } - private IReadOnlyCollection? Modules; + private List Modules = []; - protected override void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; - var modules = CommandHelpService.GetModuleHelpData(); + using var client = HttpClientFactory.CreateClient("api"); + + var request = new HttpRequestMessage(HttpMethod.Get, "api/commands"); + request.SetBrowserResponseStreamingEnabled(true); + + using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + + var modules = response.Content.ReadFromJsonAsAsyncEnumerable(); - Modules = modules.Select(m => + await foreach (var module in modules) { - var commands = m.Commands.Select(c => new Command(c.Name, c.Summary, FormatUtilities.CollapsePlurals(c.Aliases), c.Parameters, c.IsSlashCommand)); - return new Module(m.Name, m.Summary, commands); - }).ToArray(); + if (module is null) + continue; - StateHasChanged(); + Modules.Add(module); + StateHasChanged(); + } } } diff --git a/Modix.Web.Wasm/Program.cs b/Modix.Web.Wasm/Program.cs index ade53711..5b671634 100644 --- a/Modix.Web.Wasm/Program.cs +++ b/Modix.Web.Wasm/Program.cs @@ -6,6 +6,7 @@ using MudBlazor.Services; namespace Modix.Web.Wasm; + public class Program { public static async Task Main(string[] args) @@ -16,7 +17,7 @@ public static async Task Main(string[] args) builder.Services.AddCascadingValue(sp => sp.GetRequiredService()); - builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + builder.Services.AddHttpClient("api", http => http.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); builder.Services .AddAuthorizationCore() diff --git a/src/Modix.Web/Controllers/CommandsController.cs b/src/Modix.Web/Controllers/CommandsController.cs new file mode 100644 index 00000000..856382d5 --- /dev/null +++ b/src/Modix.Web/Controllers/CommandsController.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Mvc; +using Modix.Services.CommandHelp; +using Modix.Services.Utilities; +using Modix.Web.Shared.Models.Commands; + +namespace Modix.Web.Controllers; + +[Route("~/api")] +[ApiController] +public class CommandsController : Controller +{ + private readonly ICommandHelpService _commandHelpService; + + public CommandsController(ICommandHelpService commandHelpService) + { + _commandHelpService = commandHelpService; + } + + [HttpGet("commands")] + public IEnumerable Commands() + { + IEnumerable ModulesStream() + { + var modules = _commandHelpService.GetModuleHelpData(); + + var mapped = modules.Select(m => + { + var commands = m.Commands.Select(c => + { + var parameters = c.Parameters.Select(p => new Parameter(p.Name, p.Summary, p.Options, p.Type, p.IsOptional)); + + return new Command(c.Name, c.Summary, FormatUtilities.CollapsePlurals(c.Aliases), [.. parameters], c.IsSlashCommand); + }); + + return new Module(m.Name, m.Summary, commands); + }); + + foreach (var module in mapped) + { + yield return module; + } + } + + return ModulesStream(); + } +} diff --git a/src/Modix.Web/Models/Commands/Command.cs b/src/Modix.Web/Models/Commands/Command.cs deleted file mode 100644 index 71438879..00000000 --- a/src/Modix.Web/Models/Commands/Command.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Modix.Services.CommandHelp; - -namespace Modix.Web.Models.Commands; - -public record Command(string Name, string Summary, IReadOnlyCollection Aliases, IReadOnlyCollection Parameters, bool IsSlashCommand); diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 9f89e26f..066cf8a5 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -52,6 +52,8 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) public static IServiceCollection ConfigureBlazorServices(this IServiceCollection services) { + services.AddControllers(); + services .AddScoped() .AddScoped() diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index 9f6f68b1..09b7316a 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -140,12 +140,12 @@ private static void ConfigureServices(WebApplicationBuilder builder, IConfigurat .AddModixHttpClients() .AddModix(configuration); - builder.Services.AddMvc(d => d.EnableEndpointRouting = false) - .AddNewtonsoftJson(options => - { - options.SerializerSettings.Converters.Add(new StringEnumConverter()); - options.SerializerSettings.Converters.Add(new StringULongConverter()); - }); + //builder.Services.AddMvc(d => d.EnableEndpointRouting = false) + // .AddNewtonsoftJson(options => + // { + // options.SerializerSettings.Converters.Add(new StringEnumConverter()); + // options.SerializerSettings.Converters.Add(new StringULongConverter()); + // }); } public static void ConfigureCommon(WebApplication app) From 29d30adeca2ad21f17394bbfb675d7128f577a9d Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 17:50:00 +0200 Subject: [PATCH 13/46] Remove Newtonsoft serializer --- Directory.Build.targets | 3 +-- src/Modix/Modix.csproj | 1 - src/Modix/Program.cs | 8 -------- src/Modix/StringULongConverter.cs | 25 ------------------------- 4 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 src/Modix/StringULongConverter.cs diff --git a/Directory.Build.targets b/Directory.Build.targets index e4ba440e..199826c6 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -23,8 +23,7 @@ - - + diff --git a/src/Modix/Modix.csproj b/src/Modix/Modix.csproj index cf0c3ee8..d67bfaf4 100644 --- a/src/Modix/Modix.csproj +++ b/src/Modix/Modix.csproj @@ -2,7 +2,6 @@ - diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index 09b7316a..dbe05e0a 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -21,7 +21,6 @@ using Modix.Services; using Modix.Services.Utilities; using Modix.Web; -using Newtonsoft.Json.Converters; using Serilog; using Serilog.Events; using Serilog.Formatting.Compact; @@ -139,13 +138,6 @@ private static void ConfigureServices(WebApplicationBuilder builder, IConfigurat builder.Services .AddModixHttpClients() .AddModix(configuration); - - //builder.Services.AddMvc(d => d.EnableEndpointRouting = false) - // .AddNewtonsoftJson(options => - // { - // options.SerializerSettings.Converters.Add(new StringEnumConverter()); - // options.SerializerSettings.Converters.Add(new StringULongConverter()); - // }); } public static void ConfigureCommon(WebApplication app) diff --git a/src/Modix/StringULongConverter.cs b/src/Modix/StringULongConverter.cs deleted file mode 100644 index d77f7297..00000000 --- a/src/Modix/StringULongConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace Modix -{ - public class StringULongConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(ulong); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } - - public override bool CanRead => false; - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - } -} From caa4001367eaeac7bf028d90f4a12d5c28ac5bd8 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:49:45 +0200 Subject: [PATCH 14/46] Include AvatarHash claim --- Modix.Web.Wasm/Components/MiniUser.razor | 9 ++++++--- .../Security/PersistentAuthenticationStateProvider.cs | 3 ++- src/Modix.Web/Controllers/CommandsController.cs | 2 +- src/Modix/Authentication/DiscordAuthenticationSetup.cs | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Modix.Web.Wasm/Components/MiniUser.razor b/Modix.Web.Wasm/Components/MiniUser.razor index 9a91f48d..1e780b86 100644 --- a/Modix.Web.Wasm/Components/MiniUser.razor +++ b/Modix.Web.Wasm/Components/MiniUser.razor @@ -1,6 +1,8 @@ @using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; +@using Modix.Web.Shared.Models @using MudBlazor +@using System.Security.Claims
@if (AvatarUrl is not null && Username is not null) @@ -74,11 +76,12 @@ if (authState.User.Identity?.IsAuthenticated is not true) return; - // var avatarHash = authState.User.FindFirst(x => x.Type == DiscordAuthenticationConstants.Claims.AvatarHash)?.Value; // var user = DiscordHelper.GetCurrentUser(); - // AvatarUrl = $"https://cdn.discordapp.com/avatars/{user!.Id}/{avatarHash}.png"; - // Username = authState.User.Identity.Name; + var avatarHash = authState.User.FindFirst(x => x.Type == nameof(DiscordUser.AvatarHash))?.Value; + var userId = ulong.Parse(authState.User.FindFirst(d => d.Type == ClaimTypes.NameIdentifier)?.Value); + AvatarUrl = $"https://cdn.discordapp.com/avatars/{userId}/{avatarHash}.png"; + Username = authState.User.Identity.Name; // GuildOptions = DiscordHelper.GetGuildOptions(); // SelectedGuild = user.Guild; diff --git a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs index aedd4a61..b8ffdbc7 100644 --- a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs +++ b/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs @@ -18,7 +18,8 @@ public PersistentAuthenticationStateProvider(PersistentComponentState state) Claim[] claims = [ new Claim(ClaimTypes.NameIdentifier, userInfo.UserId.ToString()), - new Claim(ClaimTypes.Name, userInfo.Name) ]; + new Claim(ClaimTypes.Name, userInfo.Name), + new Claim(nameof(DiscordUser.AvatarHash), userInfo.AvatarHash)]; var roles = userInfo.Claims.Select(role => new Claim(ClaimTypes.Role, role)); diff --git a/src/Modix.Web/Controllers/CommandsController.cs b/src/Modix.Web/Controllers/CommandsController.cs index 856382d5..ef3e9ebf 100644 --- a/src/Modix.Web/Controllers/CommandsController.cs +++ b/src/Modix.Web/Controllers/CommandsController.cs @@ -7,7 +7,7 @@ namespace Modix.Web.Controllers; [Route("~/api")] [ApiController] -public class CommandsController : Controller +public class CommandsController : ControllerBase { private readonly ICommandHelpService _commandHelpService; diff --git a/src/Modix/Authentication/DiscordAuthenticationSetup.cs b/src/Modix/Authentication/DiscordAuthenticationSetup.cs index 1cf996de..b160c36c 100644 --- a/src/Modix/Authentication/DiscordAuthenticationSetup.cs +++ b/src/Modix/Authentication/DiscordAuthenticationSetup.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Modix.Web.Shared.Models; namespace Modix.Authentication { @@ -22,7 +23,7 @@ public static AuthenticationBuilder AddDiscordAuthentication( options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always; - options.ClaimActions.MapJsonKey(claimType: "avatarHash", jsonKey: "avatar"); + options.ClaimActions.MapJsonKey(claimType: nameof(DiscordUser.AvatarHash), jsonKey: "avatar"); }); } } From 33898994115b19b52a66b08c20f1847565a3490a Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:40:07 +0200 Subject: [PATCH 15/46] Move Stats page over to the wasm project --- .../Models/Stats/GuildRoleMemberCount.cs | 3 ++ .../Models/Stats/GuildStatData.cs | 3 ++ .../Models/Stats/PerUserMessageCount.cs | 3 ++ .../Pages/Stats.razor | 28 ++++------ .../Controllers/GuildStatsController.cs | 53 +++++++++++++++++++ src/Modix.Web/Models/Stats/GuildStatData.cs | 6 --- 6 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs create mode 100644 Modix.Web.Shared/Models/Stats/GuildStatData.cs create mode 100644 Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs rename {src/Modix.Web => Modix.Web.Wasm}/Pages/Stats.razor (84%) create mode 100644 src/Modix.Web/Controllers/GuildStatsController.cs delete mode 100644 src/Modix.Web/Models/Stats/GuildStatData.cs diff --git a/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs b/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs new file mode 100644 index 00000000..9b2830d6 --- /dev/null +++ b/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Stats; + +public record GuildRoleMemberCount(string Name, int Count, string Color); diff --git a/Modix.Web.Shared/Models/Stats/GuildStatData.cs b/Modix.Web.Shared/Models/Stats/GuildStatData.cs new file mode 100644 index 00000000..78ed0f64 --- /dev/null +++ b/Modix.Web.Shared/Models/Stats/GuildStatData.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Stats; + +public record GuildStatData(string GuildName, IReadOnlyCollection GuildRoleCounts, IReadOnlyCollection TopUserMessageCounts); diff --git a/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs b/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs new file mode 100644 index 00000000..dd553d2e --- /dev/null +++ b/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Stats; + +public record PerUserMessageCount(string Username, string Discriminator, int Rank, int MessageCount, bool IsCurrentUser); diff --git a/src/Modix.Web/Pages/Stats.razor b/Modix.Web.Wasm/Pages/Stats.razor similarity index 84% rename from src/Modix.Web/Pages/Stats.razor rename to Modix.Web.Wasm/Pages/Stats.razor index 9220d27d..841a2636 100644 --- a/src/Modix.Web/Pages/Stats.razor +++ b/Modix.Web.Wasm/Pages/Stats.razor @@ -1,11 +1,10 @@ @page "/stats" -@attribute [Authorize] -@using Modix.Data.Models.Core; -@using Modix.Services.GuildStats; -@using Modix.Web.Models.Stats; -@using Modix.Web.Services; +@using Microsoft.AspNetCore.Authorization +@using Modix.Web.Shared.Models.Stats @using MudBlazor +@attribute [Authorize] + Modix - Stats @if (Data is not null) @@ -83,27 +82,22 @@ } @code { - GuildStatData Data { get; set; } = null!; - List GuildRoleCountView { get; set; } = null!; + GuildStatData? Data { get; set; } + IReadOnlyCollection? GuildRoleCountView { get; set; } [Inject] - IGuildStatService GuildStatService { get; set; } = null!; - - [Inject] - DiscordHelper DiscordHelper { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; - var currentUser = DiscordHelper.GetCurrentUser(); - - var roleCounts = await GuildStatService.GetGuildMemberDistributionAsync(currentUser!.Guild); - var messageCounts = await GuildStatService.GetTopMessageCounts(currentUser.Guild, currentUser.Id); + using var client = HttpClientFactory.CreateClient("api"); + var guildStatData = await client.GetFromJsonAsync("api/guildstats"); - Data = new GuildStatData(currentUser.Guild.Name, roleCounts, messageCounts); - GuildRoleCountView = roleCounts; + Data = guildStatData; + GuildRoleCountView = guildStatData?.GuildRoleCounts; StateHasChanged(); } diff --git a/src/Modix.Web/Controllers/GuildStatsController.cs b/src/Modix.Web/Controllers/GuildStatsController.cs new file mode 100644 index 00000000..a33232ae --- /dev/null +++ b/src/Modix.Web/Controllers/GuildStatsController.cs @@ -0,0 +1,53 @@ +using System.Security.Claims; +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Services.GuildStats; +using Modix.Web.Models; +using Modix.Web.Shared.Models.Stats; + +namespace Modix.Web.Controllers; + +[Route("~/api")] +[ApiController] +[Authorize] +public class GuildStatsController : ControllerBase +{ + private readonly IGuildStatService _guildStatService; + private readonly DiscordSocketClient _discordSocketClient; + + public GuildStatsController(IGuildStatService guildStatService, DiscordSocketClient discordSocketClient) + { + _guildStatService = guildStatService; + _discordSocketClient = discordSocketClient; + } + + [HttpGet("guildstats")] + public async Task GuildStats() + { + // TODO: Move this to a base class like ModixController? + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + + var userId = ulong.Parse(User.FindFirst(d => d.Type == ClaimTypes.NameIdentifier)?.Value); + + var roleCounts = await _guildStatService.GetGuildMemberDistributionAsync(guildToSearch); + var messageCounts = await _guildStatService.GetTopMessageCounts(guildToSearch, userId); + + var guildRoleCounts = roleCounts.Select(x => new GuildRoleMemberCount(x.Name, x.Count, x.Color)); + var topUserMessageCounts = messageCounts.Select(x => new PerUserMessageCount(x.Username, x.Discriminator, x.Rank, x.MessageCount, x.IsCurrentUser)); + + return new GuildStatData(guildToSearch.Name, [..guildRoleCounts], [..topUserMessageCounts]); + } +} diff --git a/src/Modix.Web/Models/Stats/GuildStatData.cs b/src/Modix.Web/Models/Stats/GuildStatData.cs deleted file mode 100644 index db3f0969..00000000 --- a/src/Modix.Web/Models/Stats/GuildStatData.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Modix.Data.Models.Core; -using Modix.Services.GuildStats; - -namespace Modix.Web.Models.Stats; - -public record GuildStatData(string GuildName, List GuildRoleCounts, IReadOnlyCollection TopUserMessageCounts); From ed0a9ff412e033dd9d75ac2789c2d79529277535 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:42:37 +0200 Subject: [PATCH 16/46] Return user to the page they were trying to visit before getting challenged. --- Modix.Web.Wasm/Pages/UnAuthorized.razor | 4 ++++ Modix.Web.Wasm/Routes.razor | 8 +++----- src/Modix.Web/Setup.cs | 10 +++++++++- src/Modix/Authentication/DiscordAuthenticationSetup.cs | 1 + src/Modix/Program.cs | 2 ++ 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 Modix.Web.Wasm/Pages/UnAuthorized.razor diff --git a/Modix.Web.Wasm/Pages/UnAuthorized.razor b/Modix.Web.Wasm/Pages/UnAuthorized.razor new file mode 100644 index 00000000..c3362eb0 --- /dev/null +++ b/Modix.Web.Wasm/Pages/UnAuthorized.razor @@ -0,0 +1,4 @@ +@page "/unauthorized" +@using MudBlazor + +Sorry, you don't have access to that page. \ No newline at end of file diff --git a/Modix.Web.Wasm/Routes.razor b/Modix.Web.Wasm/Routes.razor index e1190a46..d1fd8cee 100644 --- a/Modix.Web.Wasm/Routes.razor +++ b/Modix.Web.Wasm/Routes.razor @@ -2,6 +2,7 @@ @using Modix.Web.Services *@ @using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models +@using Modix.Web.Wasm.Components @using Modix.Web.Wasm.Shared @using MudBlazor @using System.Security.Claims @@ -10,17 +11,14 @@ - - - @* TODO *@ - @* + Sorry, you don't have access to that page. Please wait... - *@ + diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 066cf8a5..9390adf0 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -39,7 +39,15 @@ public static WebApplication ConfigureBlazorApplication(this WebApplication app) app.MapControllers(); - app.MapGet("/login", async (context) => await context.ChallengeAsync(DiscordAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); + app.MapGet("/login", async (context) => + { + await context.ChallengeAsync(DiscordAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties + { + RedirectUri = context.Request.Query.TryGetValue(CookieAuthenticationDefaults.ReturnUrlParameter, out var returnUrl) + ? returnUrl.ToString() + : "/" + }); + }); app.MapGet("/logout", async (context) => await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); app.MapRazorComponents() diff --git a/src/Modix/Authentication/DiscordAuthenticationSetup.cs b/src/Modix/Authentication/DiscordAuthenticationSetup.cs index b160c36c..db7d333a 100644 --- a/src/Modix/Authentication/DiscordAuthenticationSetup.cs +++ b/src/Modix/Authentication/DiscordAuthenticationSetup.cs @@ -1,6 +1,7 @@ using AspNet.Security.OAuth.Discord; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index dbe05e0a..a0baf6e6 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -120,7 +120,9 @@ private static void ConfigureServices(WebApplicationBuilder builder, IConfigurat .AddCookie(options => { options.LoginPath = "/login"; + options.AccessDeniedPath = "/unauthorized"; //options.LogoutPath = "/logout"; + options.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0); }) .AddDiscordAuthentication(); From 3bc819442181ddd633d5a657e2cfe6f367d0d713 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:23:53 +0200 Subject: [PATCH 17/46] Move UserLookup page over to the wasm project --- .../Models/Common/ChannelInformation.cs | 2 +- .../Models/Common/IAutoCompleteItem.cs | 2 +- Modix.Web.Shared/Models/Common/ModixUser.cs | 8 ++ .../Models/Common/RoleInformation.cs | 2 +- .../MessageCountPerChannelInformation.cs | 2 +- .../Models/UserLookup/UserInformation.cs | 24 ++++ .../Components/AutoComplete.razor | 1 - .../Components/AutoComplete.razor.cs | 4 +- .../Components/UserLookupField.razor | 0 .../Pages/UserLookup.razor | 89 ++++++------ .../Components/Configuration/Channels.razor | 5 +- .../Components/Configuration/Claims.razor | 3 +- .../Components/Configuration/Roles.razor | 3 +- src/Modix.Web/Components/Infractions.razor | 5 +- .../Controllers/AutocompleteController.cs | 80 +++++++++++ .../Controllers/UserInformationController.cs | 128 ++++++++++++++++++ src/Modix.Web/Models/Common/ModixUser.cs | 25 ---- .../Models/UserLookup/UserInformation.cs | 62 --------- src/Modix.Web/Pages/CreatePromotion.razor | 5 +- src/Modix.Web/Pages/Tags.razor | 2 +- src/Modix.Web/Services/DiscordHelper.cs | 27 +--- 21 files changed, 298 insertions(+), 181 deletions(-) rename {src/Modix.Web => Modix.Web.Shared}/Models/Common/ChannelInformation.cs (63%) rename {src/Modix.Web => Modix.Web.Shared}/Models/Common/IAutoCompleteItem.cs (61%) create mode 100644 Modix.Web.Shared/Models/Common/ModixUser.cs rename {src/Modix.Web => Modix.Web.Shared}/Models/Common/RoleInformation.cs (66%) rename {src/Modix.Web => Modix.Web.Shared}/Models/UserLookup/MessageCountPerChannelInformation.cs (66%) create mode 100644 Modix.Web.Shared/Models/UserLookup/UserInformation.cs rename {src/Modix.Web => Modix.Web.Wasm}/Components/AutoComplete.razor (96%) rename {src/Modix.Web => Modix.Web.Wasm}/Components/AutoComplete.razor.cs (91%) rename {src/Modix.Web => Modix.Web.Wasm}/Components/UserLookupField.razor (100%) rename {src/Modix.Web => Modix.Web.Wasm}/Pages/UserLookup.razor (68%) create mode 100644 src/Modix.Web/Controllers/AutocompleteController.cs create mode 100644 src/Modix.Web/Controllers/UserInformationController.cs delete mode 100644 src/Modix.Web/Models/Common/ModixUser.cs delete mode 100644 src/Modix.Web/Models/UserLookup/UserInformation.cs diff --git a/src/Modix.Web/Models/Common/ChannelInformation.cs b/Modix.Web.Shared/Models/Common/ChannelInformation.cs similarity index 63% rename from src/Modix.Web/Models/Common/ChannelInformation.cs rename to Modix.Web.Shared/Models/Common/ChannelInformation.cs index 871dd49c..7acfddb2 100644 --- a/src/Modix.Web/Models/Common/ChannelInformation.cs +++ b/Modix.Web.Shared/Models/Common/ChannelInformation.cs @@ -1,3 +1,3 @@ -namespace Modix.Web.Models.Common; +namespace Modix.Web.Shared.Models.Common; public record ChannelInformation(ulong Id, string Name) : IAutoCompleteItem; diff --git a/src/Modix.Web/Models/Common/IAutoCompleteItem.cs b/Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs similarity index 61% rename from src/Modix.Web/Models/Common/IAutoCompleteItem.cs rename to Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs index 6e28089c..c5c6fe79 100644 --- a/src/Modix.Web/Models/Common/IAutoCompleteItem.cs +++ b/Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs @@ -1,4 +1,4 @@ -namespace Modix.Web.Models.Common; +namespace Modix.Web.Shared.Models.Common; public interface IAutoCompleteItem { diff --git a/Modix.Web.Shared/Models/Common/ModixUser.cs b/Modix.Web.Shared/Models/Common/ModixUser.cs new file mode 100644 index 00000000..21687a6f --- /dev/null +++ b/Modix.Web.Shared/Models/Common/ModixUser.cs @@ -0,0 +1,8 @@ +namespace Modix.Web.Shared.Models.Common; + +public sealed class ModixUser : IAutoCompleteItem +{ + public string? Name { get; init; } + public ulong UserId { get; init; } + public string? AvatarUrl { get; init; } +} diff --git a/src/Modix.Web/Models/Common/RoleInformation.cs b/Modix.Web.Shared/Models/Common/RoleInformation.cs similarity index 66% rename from src/Modix.Web/Models/Common/RoleInformation.cs rename to Modix.Web.Shared/Models/Common/RoleInformation.cs index d5a36484..d0e18433 100644 --- a/src/Modix.Web/Models/Common/RoleInformation.cs +++ b/Modix.Web.Shared/Models/Common/RoleInformation.cs @@ -1,3 +1,3 @@ -namespace Modix.Web.Models.Common; +namespace Modix.Web.Shared.Models.Common; public record RoleInformation(ulong Id, string Name, string Color) : IAutoCompleteItem; diff --git a/src/Modix.Web/Models/UserLookup/MessageCountPerChannelInformation.cs b/Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs similarity index 66% rename from src/Modix.Web/Models/UserLookup/MessageCountPerChannelInformation.cs rename to Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs index af3d869c..4775edca 100644 --- a/src/Modix.Web/Models/UserLookup/MessageCountPerChannelInformation.cs +++ b/Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs @@ -1,3 +1,3 @@ -namespace Modix.Web.Models.UserLookup; +namespace Modix.Web.Shared.Models.UserLookup; public record MessageCountPerChannelInformation(string ChannelName, double Count, string Color); diff --git a/Modix.Web.Shared/Models/UserLookup/UserInformation.cs b/Modix.Web.Shared/Models/UserLookup/UserInformation.cs new file mode 100644 index 00000000..67bc0849 --- /dev/null +++ b/Modix.Web.Shared/Models/UserLookup/UserInformation.cs @@ -0,0 +1,24 @@ +using Modix.Web.Shared.Models.Common; + +namespace Modix.Web.Shared.Models.UserLookup; + +public record UserInformation( + string Id, + string? Username, + string? Nickname, + string? Discriminator, + string? AvatarUrl, + DateTimeOffset CreatedAt, + DateTimeOffset? JoinedAt, + DateTimeOffset? FirstSeen, + DateTimeOffset? LastSeen, + int Rank, + int Last7DaysMessages, + int Last30DaysMessages, + decimal AverageMessagesPerDay, + int Percentile, + IEnumerable Roles, + bool IsBanned, + string? BanReason, + bool IsGuildMember +); diff --git a/src/Modix.Web/Components/AutoComplete.razor b/Modix.Web.Wasm/Components/AutoComplete.razor similarity index 96% rename from src/Modix.Web/Components/AutoComplete.razor rename to Modix.Web.Wasm/Components/AutoComplete.razor index ae430f95..0391a458 100644 --- a/src/Modix.Web/Components/AutoComplete.razor +++ b/Modix.Web.Wasm/Components/AutoComplete.razor @@ -1,5 +1,4 @@ @using Modix.Web.Models; -@using Modix.Web.Services; @using MudBlazor @typeparam T diff --git a/src/Modix.Web/Components/AutoComplete.razor.cs b/Modix.Web.Wasm/Components/AutoComplete.razor.cs similarity index 91% rename from src/Modix.Web/Components/AutoComplete.razor.cs rename to Modix.Web.Wasm/Components/AutoComplete.razor.cs index e9d40f65..85c7015d 100644 --- a/src/Modix.Web/Components/AutoComplete.razor.cs +++ b/Modix.Web.Wasm/Components/AutoComplete.razor.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Components; -using Modix.Web.Models.Common; +using Modix.Web.Shared.Models.Common; -namespace Modix.Web.Components; +namespace Modix.Web.Wasm.Components; public partial class AutoComplete where T : IAutoCompleteItem { diff --git a/src/Modix.Web/Components/UserLookupField.razor b/Modix.Web.Wasm/Components/UserLookupField.razor similarity index 100% rename from src/Modix.Web/Components/UserLookupField.razor rename to Modix.Web.Wasm/Components/UserLookupField.razor diff --git a/src/Modix.Web/Pages/UserLookup.razor b/Modix.Web.Wasm/Pages/UserLookup.razor similarity index 68% rename from src/Modix.Web/Pages/UserLookup.razor rename to Modix.Web.Wasm/Pages/UserLookup.razor index 30c3b797..a2db2051 100644 --- a/src/Modix.Web/Pages/UserLookup.razor +++ b/Modix.Web.Wasm/Pages/UserLookup.razor @@ -1,20 +1,13 @@ @page "/userlookup" -@attribute [Authorize] -@using Discord; -@using Modix.Data.Repositories; -@using Modix.Services.Core; -@using Modix.Services.Utilities; -@using Modix.Web.Components -@using Modix.Web.Models; -@using Modix.Web.Models.UserLookup; -@using Modix.Web.Services +@using Humanizer +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.UserLookup +@using Modix.Web.Wasm.Components @using MudBlazor -@using Discord.WebSocket -@using System.Linq.Expressions; -@using MudBlazor.Charts -@using System.Globalization; -@using Humanizer; -@using Modix.Web.Models.Common; +@using System.Security.Claims +@attribute [Authorize] Modix - User Lookup @@ -25,7 +18,7 @@
@@ -89,7 +82,7 @@ - @foreach (var channel in userInformation.MessageCountsPerChannel) + @foreach (var channel in messageCountsPerChannel) { var channelColorStyle = $"border: 1px solid {channel.Color}"; @code { + MessageCountPerChannelInformation[] messageCountsPerChannel = Array.Empty(); MessageCountPerChannelInformation[] messageCountsPerChannelView = Array.Empty(); UserInformation? userInformation = null; [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; - - [Inject] - public IUserService UserService { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } - [Inject] - public IMessageRepository MessageRepository { get; set; } = null!; + [CascadingParameter] + public Task AuthState { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; - var currentUser = DiscordHelper.GetCurrentUser(); - await SelectedUserChanged(ModixUser.FromIGuildUser(currentUser!)); + //TODO: add an endpoint for this? + // var currentUser = DiscordHelper.GetCurrentUser(); + // await SelectedUserChanged(ModixUser.FromIGuildUser(currentUser!)); + + var authState = await AuthState; + var userId = authState.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + var currentUser = await AutoCompleteAsync(userId); + await SelectedUserChanged(currentUser.FirstOrDefault()); StateHasChanged(); } @@ -142,41 +140,32 @@ private void SelectedChannelsChanged(MudChip[] chips) { var channels = chips.Select(x => x.Value).Cast(); - messageCountsPerChannelView = userInformation!.MessageCountsPerChannel + messageCountsPerChannelView = messageCountsPerChannel .Where(x => channels.Contains(x.ChannelName)) .ToArray(); } - private async Task SelectedUserChanged(ModixUser user) + private async Task> AutoCompleteAsync(string query) { - if (user is null) - return; - - var currentGuild = DiscordHelper.GetUserGuild(); + using var client = HttpClientFactory.CreateClient("api"); - var ephemeralUser = await UserService.GetUserInformationAsync(currentGuild.Id, user.UserId); + var users = await client.GetFromJsonAsync($"api/autocomplete/users/{query}"); - var userRank = await MessageRepository.GetGuildUserParticipationStatistics(currentGuild.Id, user.UserId); - var messages7 = await MessageRepository.GetGuildUserMessageCountByDate(currentGuild.Id, user.UserId, TimeSpan.FromDays(7)); - var messages30 = await MessageRepository.GetGuildUserMessageCountByDate(currentGuild.Id, user.UserId, TimeSpan.FromDays(30)); - - var roles = ephemeralUser!.RoleIds - .Select(x => currentGuild.GetRole(x)) - .OrderByDescending(x => x.IsHoisted) - .ThenByDescending(x => x.Position) - .ToArray(); + return users ?? []; + } - var timespan = DateTimeOffset.UtcNow - DateTimeOffset.MinValue; - var result = await MessageRepository.GetGuildUserMessageCountByChannel(currentGuild.Id, user.UserId, timespan); - var colors = ColorUtils.GetRainbowColors(result.Count); + private async Task SelectedUserChanged(ModixUser? user) + { + if (user is null) + return; - var messageCountsPerChannel = result - .Select((x, i) => new MessageCountPerChannelInformation(x.ChannelName, x.MessageCount, colors[i++].ToString())) - .OrderByDescending(x => x.Count) - .ToList(); + using var client = HttpClientFactory.CreateClient("api"); - userInformation = UserInformation.FromEphemeralUser(ephemeralUser, userRank, messages7, messages30, roles, messageCountsPerChannel); + var userInformationTask = client.GetFromJsonAsync($"api/userinformation/{user.UserId}"); + var messageCountsPerChannelTask = client.GetFromJsonAsync($"api/userinformation/{user.UserId}/messages"); - messageCountsPerChannelView = userInformation.MessageCountsPerChannel.ToArray(); + userInformation = await userInformationTask; + messageCountsPerChannel = (await messageCountsPerChannelTask) ?? []; + messageCountsPerChannelView = messageCountsPerChannel; } } diff --git a/src/Modix.Web/Components/Configuration/Channels.razor b/src/Modix.Web/Components/Configuration/Channels.razor index f9521657..aad41f96 100644 --- a/src/Modix.Web/Components/Configuration/Channels.razor +++ b/src/Modix.Web/Components/Configuration/Channels.razor @@ -1,10 +1,11 @@ @using Modix.Data.Models.Core; -@using Modix.Web.Models.Common; +@using Modix.Services; @using Modix.Web.Models.Configuration; @using Modix.Web.Services; +@using Modix.Web.Wasm.Components @using MudBlazor @using Humanizer; -@using Modix.Services +@using Modix.Web.Shared.Models.Common; Modix - Channels Channel Designations diff --git a/src/Modix.Web/Components/Configuration/Claims.razor b/src/Modix.Web/Components/Configuration/Claims.razor index c7192a59..ddf87a3e 100644 --- a/src/Modix.Web/Components/Configuration/Claims.razor +++ b/src/Modix.Web/Components/Configuration/Claims.razor @@ -2,12 +2,11 @@ @using Modix.Data.Repositories; @using Modix.Data.Utilities; @using Modix.Models.Core; -@using Modix.Web.Models.UserLookup; @using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common @using MudBlazor @using System.Reflection; @using Humanizer; -@using Modix.Web.Models.Common; Modix - Claims diff --git a/src/Modix.Web/Components/Configuration/Roles.razor b/src/Modix.Web/Components/Configuration/Roles.razor index b7241a06..4f6b92d7 100644 --- a/src/Modix.Web/Components/Configuration/Roles.razor +++ b/src/Modix.Web/Components/Configuration/Roles.razor @@ -1,12 +1,11 @@ @using Modix.Data.Models.Core; @using Modix.Services.Core; @using Modix.Web.Models.Configuration; -@using Modix.Web.Models.UserLookup; @using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common @using MudBlazor @using Humanizer; @using System.Security.Claims; -@using Modix.Web.Models.Common; Modix - Roles Role Designations diff --git a/src/Modix.Web/Components/Infractions.razor b/src/Modix.Web/Components/Infractions.razor index 5326bb96..31b192bd 100644 --- a/src/Modix.Web/Components/Infractions.razor +++ b/src/Modix.Web/Components/Infractions.razor @@ -5,9 +5,10 @@ @using Modix.Web.Models; @using Modix.Web.Models.Infractions; @using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common +@using Modix.Web.Wasm.Components @using MudBlazor; @using System.Security.Claims; -@using Modix.Web.Models.Common; Modix - Infractions @@ -19,7 +20,7 @@ diff --git a/src/Modix.Web/Controllers/AutocompleteController.cs b/src/Modix.Web/Controllers/AutocompleteController.cs new file mode 100644 index 00000000..873a5473 --- /dev/null +++ b/src/Modix.Web/Controllers/AutocompleteController.cs @@ -0,0 +1,80 @@ +using Discord; +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Data.Utilities; +using Modix.Services.Core; +using Modix.Services.Utilities; +using Modix.Web.Models; +using Modix.Web.Shared.Models.Common; + +namespace Modix.Web.Controllers; + +[Route("~/api/autocomplete")] +[ApiController] +[Authorize] +public class AutocompleteController : ControllerBase +{ + private readonly DiscordSocketClient _discordSocketClient; + private readonly IUserService _userService; + + public AutocompleteController(DiscordSocketClient discordSocketClient, IUserService userService) + { + _discordSocketClient = discordSocketClient; + _userService = userService; + } + + + [HttpGet("users/{query}")] + public async Task> AutocompleteUsers(string query) + { + // TODO: Move this to a base class like ModixController? + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + if (guildToSearch?.Users is null) + return []; + + var result = guildToSearch.Users + .Where(d => d.Username.OrdinalContains(query) || d.Id.ToString() == query) + .Take(10) + .Select(FromIGuildUser); + + if (!result.Any() && ulong.TryParse(query, out var userId)) + { + var user = await _userService.GetUserInformationAsync(guildToSearch.Id, userId); + + if (user is not null) + { + result = [ FromNonGuildUser(user) ]; + } + } + + return result; + } + + + public static ModixUser FromIGuildUser(IGuildUser user) => new() + { + Name = user.GetDisplayName(), + UserId = user.Id, + AvatarUrl = user.GetDisplayAvatarUrl() ?? user.GetDefaultAvatarUrl() + }; + + public static ModixUser FromNonGuildUser(IUser user) => new() + { + Name = user.GetDisplayName(), + UserId = user.Id, + AvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl() + }; +} diff --git a/src/Modix.Web/Controllers/UserInformationController.cs b/src/Modix.Web/Controllers/UserInformationController.cs new file mode 100644 index 00000000..ea95b353 --- /dev/null +++ b/src/Modix.Web/Controllers/UserInformationController.cs @@ -0,0 +1,128 @@ +using Discord; +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Data.Models.Core; +using Modix.Data.Repositories; +using Modix.Services.Core; +using Modix.Services.Utilities; +using Modix.Web.Models; +using Modix.Web.Shared.Models.Common; +using Modix.Web.Shared.Models.UserLookup; + +namespace Modix.Web.Controllers; + +[Route("~/api/userinformation")] +[ApiController] +[Authorize] +public class UserInformationController : ControllerBase +{ + private readonly IUserService _userService; + private readonly IMessageRepository _messageRepository; + private readonly DiscordSocketClient _discordSocketClient; + + public UserInformationController(IUserService userService, IMessageRepository messageRepository, DiscordSocketClient discordSocketClient) + { + _userService = userService; + _messageRepository = messageRepository; + _discordSocketClient = discordSocketClient; + } + + + [HttpGet("{userIdString}")] + public async Task GetUserInformationAsync(string userIdString) + { + if (!ulong.TryParse(userIdString, out var userId)) + return null; + + // TODO: Move this to a base class like ModixController? + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + var userInformation = await _userService.GetUserInformationAsync(guildToSearch.Id, userId); + if (userInformation is null) + return null; + + var userRank = await _messageRepository.GetGuildUserParticipationStatistics(guildToSearch.Id, userId); + var messages7 = await _messageRepository.GetGuildUserMessageCountByDate(guildToSearch.Id, userId, TimeSpan.FromDays(7)); + var messages30 = await _messageRepository.GetGuildUserMessageCountByDate(guildToSearch.Id, userId, TimeSpan.FromDays(30)); + + var roles = userInformation.RoleIds + .Select(x => guildToSearch.GetRole(x)) + .OrderByDescending(x => x.IsHoisted) + .ThenByDescending(x => x.Position) + .ToArray(); + + return FromEphemeralUser(userInformation, userRank, messages7, messages30, roles); + } + + [HttpGet("{userIdString}/messages")] + public async Task GetUserMessagesPerChannelAsync(string userIdString, DateTimeOffset after = default) + { + if (!ulong.TryParse(userIdString, out var userId)) + return []; + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + var timespan = DateTimeOffset.UtcNow - after; + var result = await _messageRepository.GetGuildUserMessageCountByChannel(guildToSearch.Id, userId, timespan); + var colors = ColorUtils.GetRainbowColors(result.Count); + + return result.Select((x,i) => new MessageCountPerChannelInformation(x.ChannelName, x.MessageCount, colors[i].ToString())) + .OrderByDescending(x => x.Count) + .ToArray(); + } + + private static UserInformation FromEphemeralUser( + EphemeralUser ephemeralUser, + GuildUserParticipationStatistics userRank, + IReadOnlyList messages7, + IReadOnlyList messages30, + SocketRole[] roles) + { + return new UserInformation( + ephemeralUser.Id.ToString(), + ephemeralUser.Username, + ephemeralUser.Nickname, + ephemeralUser.Discriminator, + ephemeralUser.AvatarId != null ? ephemeralUser.GetAvatarUrl(ImageFormat.Auto, 256) : ephemeralUser.GetDefaultAvatarUrl(), + ephemeralUser.CreatedAt, + ephemeralUser.JoinedAt, + ephemeralUser.FirstSeen, + ephemeralUser.LastSeen, + userRank.Rank, + messages7.Sum(x => x.MessageCount), + messages30.Sum(x => x.MessageCount), + userRank.AveragePerDay, + userRank.Percentile, + roles + .Where(x => !x.IsEveryone) + .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())), + ephemeralUser.IsBanned, + ephemeralUser.BanReason, + ephemeralUser.GuildId != default + ); + } +} diff --git a/src/Modix.Web/Models/Common/ModixUser.cs b/src/Modix.Web/Models/Common/ModixUser.cs deleted file mode 100644 index b110ab35..00000000 --- a/src/Modix.Web/Models/Common/ModixUser.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Discord; -using Modix.Services.Utilities; - -namespace Modix.Web.Models.Common; - -public sealed class ModixUser : IAutoCompleteItem -{ - public string? Name { get; init; } - public ulong UserId { get; init; } - public string? AvatarUrl { get; init; } - - public static ModixUser FromIGuildUser(IGuildUser user) => new() - { - Name = user.GetDisplayName(), - UserId = user.Id, - AvatarUrl = user.GetDisplayAvatarUrl() ?? user.GetDefaultAvatarUrl() - }; - - public static ModixUser FromNonGuildUser(IUser user) => new() - { - Name = user.GetDisplayName(), - UserId = user.Id, - AvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl() - }; -} diff --git a/src/Modix.Web/Models/UserLookup/UserInformation.cs b/src/Modix.Web/Models/UserLookup/UserInformation.cs deleted file mode 100644 index b3bcdcea..00000000 --- a/src/Modix.Web/Models/UserLookup/UserInformation.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Discord; -using Discord.WebSocket; -using Modix.Data.Models.Core; -using Modix.Web.Models.Common; - -namespace Modix.Web.Models.UserLookup; - -public record UserInformation( - string Id, - string? Username, - string? Nickname, - string? Discriminator, - string? AvatarUrl, - DateTimeOffset CreatedAt, - DateTimeOffset? JoinedAt, - DateTimeOffset? FirstSeen, - DateTimeOffset? LastSeen, - int Rank, - int Last7DaysMessages, - int Last30DaysMessages, - decimal AverageMessagesPerDay, - int Percentile, - IEnumerable Roles, - bool IsBanned, - string? BanReason, - bool IsGuildMember, - IReadOnlyList MessageCountsPerChannel -) -{ - public static UserInformation FromEphemeralUser( - EphemeralUser ephemeralUser, - GuildUserParticipationStatistics userRank, - IReadOnlyList messages7, - IReadOnlyList messages30, - SocketRole[] roles, - List messageCountsPerChannel) - { - return new UserInformation( - ephemeralUser.Id.ToString(), - ephemeralUser.Username, - ephemeralUser.Nickname, - ephemeralUser.Discriminator, - ephemeralUser.AvatarId != null ? ephemeralUser.GetAvatarUrl(ImageFormat.Auto, 256) : ephemeralUser.GetDefaultAvatarUrl(), - ephemeralUser.CreatedAt, - ephemeralUser.JoinedAt, - ephemeralUser.FirstSeen, - ephemeralUser.LastSeen, - userRank.Rank, - messages7.Sum(x => x.MessageCount), - messages30.Sum(x => x.MessageCount), - userRank.AveragePerDay, - userRank.Percentile, - roles - .Where(x => !x.IsEveryone) - .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())), - ephemeralUser.IsBanned, - ephemeralUser.BanReason, - ephemeralUser.GuildId != default, - messageCountsPerChannel - ); - } -} diff --git a/src/Modix.Web/Pages/CreatePromotion.razor b/src/Modix.Web/Pages/CreatePromotion.razor index 8a8f5fef..4e766dd9 100644 --- a/src/Modix.Web/Pages/CreatePromotion.razor +++ b/src/Modix.Web/Pages/CreatePromotion.razor @@ -4,9 +4,10 @@ @using Modix.Services.Promotions; @using Modix.Web.Components @using Modix.Web.Models; -@using Modix.Web.Models.Common; @using Modix.Web.Models.Promotions; @using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common +@using Modix.Web.Wasm.Components @using MudBlazor Modix - Start A Campaign @@ -28,7 +29,7 @@ diff --git a/src/Modix.Web/Pages/Tags.razor b/src/Modix.Web/Pages/Tags.razor index 5bfe39af..a80f318d 100644 --- a/src/Modix.Web/Pages/Tags.razor +++ b/src/Modix.Web/Pages/Tags.razor @@ -4,9 +4,9 @@ @using Modix.Data.Models.Tags; @using Modix.Services.Tags; @using Modix.Web.Models; -@using Modix.Web.Models.Common; @using Modix.Web.Models.Tags; @using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common @using MudBlazor @using System.Globalization; diff --git a/src/Modix.Web/Services/DiscordHelper.cs b/src/Modix.Web/Services/DiscordHelper.cs index 462a8408..d80cfce2 100644 --- a/src/Modix.Web/Services/DiscordHelper.cs +++ b/src/Modix.Web/Services/DiscordHelper.cs @@ -2,7 +2,7 @@ using Discord.WebSocket; using Modix.Services.Core; using Modix.Web.Models; -using Modix.Web.Models.Common; +using Modix.Web.Shared.Models.Common; namespace Modix.Web.Services; @@ -34,31 +34,6 @@ public IEnumerable GetGuildOptions() return currentGuild.GetUser(sessionState.CurrentUserId); } - public async Task> AutoCompleteAsync(string query) - { - var userGuild = GetUserGuild(); - - if (userGuild?.Users is null) - return Array.Empty(); - - var result = userGuild.Users - .Where(d => d.Username.Contains(query, StringComparison.OrdinalIgnoreCase) || d.Id.ToString() == query) - .Take(10) - .Select(ModixUser.FromIGuildUser); - - if (!result.Any() && ulong.TryParse(query, out var userId)) - { - var user = await userService.GetUserInformationAsync(userGuild.Id, userId); - - if (user is not null) - { - result = result.Append(ModixUser.FromNonGuildUser(user)); - } - } - - return result; - } - public IEnumerable AutoCompleteRoles(string query) { if (query.StartsWith('@')) From db008047e3f23506c61fda28bedf92736d348af8 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:30:43 +0200 Subject: [PATCH 18/46] Move new projects into the src folder to conform to the new repository structure --- Modix.sln | 4 ++-- .../Modix.Web.Shared}/Models/Commands/Command.cs | 0 .../Modix.Web.Shared}/Models/Commands/Module.cs | 0 .../Modix.Web.Shared}/Models/Commands/Parameter.cs | 0 .../Modix.Web.Shared}/Models/Common/ChannelInformation.cs | 0 .../Modix.Web.Shared}/Models/Common/IAutoCompleteItem.cs | 0 .../Modix.Web.Shared}/Models/Common/ModixUser.cs | 0 .../Modix.Web.Shared}/Models/Common/RoleInformation.cs | 0 .../Modix.Web.Shared}/Models/DiscordUser.cs | 0 .../Modix.Web.Shared}/Models/SessionState.cs | 0 .../Modix.Web.Shared}/Models/Stats/GuildRoleMemberCount.cs | 0 .../Modix.Web.Shared}/Models/Stats/GuildStatData.cs | 0 .../Modix.Web.Shared}/Models/Stats/PerUserMessageCount.cs | 0 .../Models/UserLookup/MessageCountPerChannelInformation.cs | 0 .../Modix.Web.Shared}/Models/UserLookup/UserInformation.cs | 0 .../Modix.Web.Shared}/Modix.Web.Shared.csproj | 0 .../Modix.Web.Wasm}/Components/AnchorNavigation.razor | 0 .../Modix.Web.Wasm}/Components/AutoComplete.razor | 0 .../Modix.Web.Wasm}/Components/AutoComplete.razor.cs | 0 .../Modix.Web.Wasm}/Components/MiniUser.razor | 0 .../Modix.Web.Wasm}/Components/NavMenu.razor | 0 .../Modix.Web.Wasm}/Components/NavMenuLinks.razor | 0 .../Modix.Web.Wasm}/Components/UserLookupField.razor | 0 .../Modix.Web.Wasm}/Modix.Web.Wasm.csproj | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Commands.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Counter.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Index.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Stats.razor | 0 .../Modix.Web.Wasm}/Pages/UnAuthorized.razor | 0 .../Modix.Web.Wasm}/Pages/UserLookup.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Weather.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Program.cs | 0 .../Modix.Web.Wasm}/Properties/launchSettings.json | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/Routes.razor | 0 .../Security/PersistentAuthenticationStateProvider.cs | 0 .../Modix.Web.Wasm}/Shared/MainLayout.razor | 0 {Modix.Web.Wasm => src/Modix.Web.Wasm}/_Imports.razor | 0 src/Modix.Web/Modix.Web.csproj | 6 +++--- 38 files changed, 5 insertions(+), 5 deletions(-) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Commands/Command.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Commands/Module.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Commands/Parameter.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Common/ChannelInformation.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Common/IAutoCompleteItem.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Common/ModixUser.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Common/RoleInformation.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/DiscordUser.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/SessionState.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Stats/GuildRoleMemberCount.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Stats/GuildStatData.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/Stats/PerUserMessageCount.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/UserLookup/MessageCountPerChannelInformation.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Models/UserLookup/UserInformation.cs (100%) rename {Modix.Web.Shared => src/Modix.Web.Shared}/Modix.Web.Shared.csproj (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/AnchorNavigation.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/AutoComplete.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/AutoComplete.razor.cs (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/MiniUser.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/NavMenu.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/NavMenuLinks.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Components/UserLookupField.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Modix.Web.Wasm.csproj (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Commands.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Counter.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Index.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Stats.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/UnAuthorized.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/UserLookup.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Pages/Weather.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Program.cs (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Properties/launchSettings.json (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Routes.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Security/PersistentAuthenticationStateProvider.cs (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/Shared/MainLayout.razor (100%) rename {Modix.Web.Wasm => src/Modix.Web.Wasm}/_Imports.razor (100%) diff --git a/Modix.sln b/Modix.sln index f19aa0f2..ed938efc 100644 --- a/Modix.sln +++ b/Modix.sln @@ -64,9 +64,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web", "src\Modix.Web\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E74ADFBD-55F3-4E28-885B-2270670294EF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Wasm", "Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Wasm", "src\Modix.Web.Wasm\Modix.Web.Wasm.csproj", "{F0DFE404-B6A9-495C-A41C-92FD25138619}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Shared", "Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Shared", "src\Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Modix.Web.Shared/Models/Commands/Command.cs b/src/Modix.Web.Shared/Models/Commands/Command.cs similarity index 100% rename from Modix.Web.Shared/Models/Commands/Command.cs rename to src/Modix.Web.Shared/Models/Commands/Command.cs diff --git a/Modix.Web.Shared/Models/Commands/Module.cs b/src/Modix.Web.Shared/Models/Commands/Module.cs similarity index 100% rename from Modix.Web.Shared/Models/Commands/Module.cs rename to src/Modix.Web.Shared/Models/Commands/Module.cs diff --git a/Modix.Web.Shared/Models/Commands/Parameter.cs b/src/Modix.Web.Shared/Models/Commands/Parameter.cs similarity index 100% rename from Modix.Web.Shared/Models/Commands/Parameter.cs rename to src/Modix.Web.Shared/Models/Commands/Parameter.cs diff --git a/Modix.Web.Shared/Models/Common/ChannelInformation.cs b/src/Modix.Web.Shared/Models/Common/ChannelInformation.cs similarity index 100% rename from Modix.Web.Shared/Models/Common/ChannelInformation.cs rename to src/Modix.Web.Shared/Models/Common/ChannelInformation.cs diff --git a/Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs b/src/Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs similarity index 100% rename from Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs rename to src/Modix.Web.Shared/Models/Common/IAutoCompleteItem.cs diff --git a/Modix.Web.Shared/Models/Common/ModixUser.cs b/src/Modix.Web.Shared/Models/Common/ModixUser.cs similarity index 100% rename from Modix.Web.Shared/Models/Common/ModixUser.cs rename to src/Modix.Web.Shared/Models/Common/ModixUser.cs diff --git a/Modix.Web.Shared/Models/Common/RoleInformation.cs b/src/Modix.Web.Shared/Models/Common/RoleInformation.cs similarity index 100% rename from Modix.Web.Shared/Models/Common/RoleInformation.cs rename to src/Modix.Web.Shared/Models/Common/RoleInformation.cs diff --git a/Modix.Web.Shared/Models/DiscordUser.cs b/src/Modix.Web.Shared/Models/DiscordUser.cs similarity index 100% rename from Modix.Web.Shared/Models/DiscordUser.cs rename to src/Modix.Web.Shared/Models/DiscordUser.cs diff --git a/Modix.Web.Shared/Models/SessionState.cs b/src/Modix.Web.Shared/Models/SessionState.cs similarity index 100% rename from Modix.Web.Shared/Models/SessionState.cs rename to src/Modix.Web.Shared/Models/SessionState.cs diff --git a/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs b/src/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs similarity index 100% rename from Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs rename to src/Modix.Web.Shared/Models/Stats/GuildRoleMemberCount.cs diff --git a/Modix.Web.Shared/Models/Stats/GuildStatData.cs b/src/Modix.Web.Shared/Models/Stats/GuildStatData.cs similarity index 100% rename from Modix.Web.Shared/Models/Stats/GuildStatData.cs rename to src/Modix.Web.Shared/Models/Stats/GuildStatData.cs diff --git a/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs b/src/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs similarity index 100% rename from Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs rename to src/Modix.Web.Shared/Models/Stats/PerUserMessageCount.cs diff --git a/Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs b/src/Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs similarity index 100% rename from Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs rename to src/Modix.Web.Shared/Models/UserLookup/MessageCountPerChannelInformation.cs diff --git a/Modix.Web.Shared/Models/UserLookup/UserInformation.cs b/src/Modix.Web.Shared/Models/UserLookup/UserInformation.cs similarity index 100% rename from Modix.Web.Shared/Models/UserLookup/UserInformation.cs rename to src/Modix.Web.Shared/Models/UserLookup/UserInformation.cs diff --git a/Modix.Web.Shared/Modix.Web.Shared.csproj b/src/Modix.Web.Shared/Modix.Web.Shared.csproj similarity index 100% rename from Modix.Web.Shared/Modix.Web.Shared.csproj rename to src/Modix.Web.Shared/Modix.Web.Shared.csproj diff --git a/Modix.Web.Wasm/Components/AnchorNavigation.razor b/src/Modix.Web.Wasm/Components/AnchorNavigation.razor similarity index 100% rename from Modix.Web.Wasm/Components/AnchorNavigation.razor rename to src/Modix.Web.Wasm/Components/AnchorNavigation.razor diff --git a/Modix.Web.Wasm/Components/AutoComplete.razor b/src/Modix.Web.Wasm/Components/AutoComplete.razor similarity index 100% rename from Modix.Web.Wasm/Components/AutoComplete.razor rename to src/Modix.Web.Wasm/Components/AutoComplete.razor diff --git a/Modix.Web.Wasm/Components/AutoComplete.razor.cs b/src/Modix.Web.Wasm/Components/AutoComplete.razor.cs similarity index 100% rename from Modix.Web.Wasm/Components/AutoComplete.razor.cs rename to src/Modix.Web.Wasm/Components/AutoComplete.razor.cs diff --git a/Modix.Web.Wasm/Components/MiniUser.razor b/src/Modix.Web.Wasm/Components/MiniUser.razor similarity index 100% rename from Modix.Web.Wasm/Components/MiniUser.razor rename to src/Modix.Web.Wasm/Components/MiniUser.razor diff --git a/Modix.Web.Wasm/Components/NavMenu.razor b/src/Modix.Web.Wasm/Components/NavMenu.razor similarity index 100% rename from Modix.Web.Wasm/Components/NavMenu.razor rename to src/Modix.Web.Wasm/Components/NavMenu.razor diff --git a/Modix.Web.Wasm/Components/NavMenuLinks.razor b/src/Modix.Web.Wasm/Components/NavMenuLinks.razor similarity index 100% rename from Modix.Web.Wasm/Components/NavMenuLinks.razor rename to src/Modix.Web.Wasm/Components/NavMenuLinks.razor diff --git a/Modix.Web.Wasm/Components/UserLookupField.razor b/src/Modix.Web.Wasm/Components/UserLookupField.razor similarity index 100% rename from Modix.Web.Wasm/Components/UserLookupField.razor rename to src/Modix.Web.Wasm/Components/UserLookupField.razor diff --git a/Modix.Web.Wasm/Modix.Web.Wasm.csproj b/src/Modix.Web.Wasm/Modix.Web.Wasm.csproj similarity index 100% rename from Modix.Web.Wasm/Modix.Web.Wasm.csproj rename to src/Modix.Web.Wasm/Modix.Web.Wasm.csproj diff --git a/Modix.Web.Wasm/Pages/Commands.razor b/src/Modix.Web.Wasm/Pages/Commands.razor similarity index 100% rename from Modix.Web.Wasm/Pages/Commands.razor rename to src/Modix.Web.Wasm/Pages/Commands.razor diff --git a/Modix.Web.Wasm/Pages/Counter.razor b/src/Modix.Web.Wasm/Pages/Counter.razor similarity index 100% rename from Modix.Web.Wasm/Pages/Counter.razor rename to src/Modix.Web.Wasm/Pages/Counter.razor diff --git a/Modix.Web.Wasm/Pages/Index.razor b/src/Modix.Web.Wasm/Pages/Index.razor similarity index 100% rename from Modix.Web.Wasm/Pages/Index.razor rename to src/Modix.Web.Wasm/Pages/Index.razor diff --git a/Modix.Web.Wasm/Pages/Stats.razor b/src/Modix.Web.Wasm/Pages/Stats.razor similarity index 100% rename from Modix.Web.Wasm/Pages/Stats.razor rename to src/Modix.Web.Wasm/Pages/Stats.razor diff --git a/Modix.Web.Wasm/Pages/UnAuthorized.razor b/src/Modix.Web.Wasm/Pages/UnAuthorized.razor similarity index 100% rename from Modix.Web.Wasm/Pages/UnAuthorized.razor rename to src/Modix.Web.Wasm/Pages/UnAuthorized.razor diff --git a/Modix.Web.Wasm/Pages/UserLookup.razor b/src/Modix.Web.Wasm/Pages/UserLookup.razor similarity index 100% rename from Modix.Web.Wasm/Pages/UserLookup.razor rename to src/Modix.Web.Wasm/Pages/UserLookup.razor diff --git a/Modix.Web.Wasm/Pages/Weather.razor b/src/Modix.Web.Wasm/Pages/Weather.razor similarity index 100% rename from Modix.Web.Wasm/Pages/Weather.razor rename to src/Modix.Web.Wasm/Pages/Weather.razor diff --git a/Modix.Web.Wasm/Program.cs b/src/Modix.Web.Wasm/Program.cs similarity index 100% rename from Modix.Web.Wasm/Program.cs rename to src/Modix.Web.Wasm/Program.cs diff --git a/Modix.Web.Wasm/Properties/launchSettings.json b/src/Modix.Web.Wasm/Properties/launchSettings.json similarity index 100% rename from Modix.Web.Wasm/Properties/launchSettings.json rename to src/Modix.Web.Wasm/Properties/launchSettings.json diff --git a/Modix.Web.Wasm/Routes.razor b/src/Modix.Web.Wasm/Routes.razor similarity index 100% rename from Modix.Web.Wasm/Routes.razor rename to src/Modix.Web.Wasm/Routes.razor diff --git a/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs b/src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs similarity index 100% rename from Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs rename to src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs diff --git a/Modix.Web.Wasm/Shared/MainLayout.razor b/src/Modix.Web.Wasm/Shared/MainLayout.razor similarity index 100% rename from Modix.Web.Wasm/Shared/MainLayout.razor rename to src/Modix.Web.Wasm/Shared/MainLayout.razor diff --git a/Modix.Web.Wasm/_Imports.razor b/src/Modix.Web.Wasm/_Imports.razor similarity index 100% rename from Modix.Web.Wasm/_Imports.razor rename to src/Modix.Web.Wasm/_Imports.razor diff --git a/src/Modix.Web/Modix.Web.csproj b/src/Modix.Web/Modix.Web.csproj index d56dafda..6f900b1f 100644 --- a/src/Modix.Web/Modix.Web.csproj +++ b/src/Modix.Web/Modix.Web.csproj @@ -8,6 +8,7 @@ + @@ -15,9 +16,8 @@ - - - + + From 12201b8683676d0edb4478cf87d6171aecf1bcfd Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:34:49 +0200 Subject: [PATCH 19/46] Replace the old Error page with the new blazor page variant --- src/Modix.Web/Pages/Error.cshtml | 42 ----------------------------- src/Modix.Web/Pages/Error.cshtml.cs | 16 ----------- src/Modix.Web/Pages/Error.razor | 36 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 58 deletions(-) delete mode 100644 src/Modix.Web/Pages/Error.cshtml delete mode 100644 src/Modix.Web/Pages/Error.cshtml.cs create mode 100644 src/Modix.Web/Pages/Error.razor diff --git a/src/Modix.Web/Pages/Error.cshtml b/src/Modix.Web/Pages/Error.cshtml deleted file mode 100644 index 3ff7e625..00000000 --- a/src/Modix.Web/Pages/Error.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -@page -@model Modix.Web.Pages.ErrorModel - - - - - - - - Error - - - - - -
-
-

Error.

-

An error occurred while processing your request.

- - @if (Model.ShowRequestId) - { -

- Request ID: @Model.RequestId -

- } - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

-
-
- - - diff --git a/src/Modix.Web/Pages/Error.cshtml.cs b/src/Modix.Web/Pages/Error.cshtml.cs deleted file mode 100644 index ad6c2eb2..00000000 --- a/src/Modix.Web/Pages/Error.cshtml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace Modix.Web.Pages; - -[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] -[IgnoreAntiforgeryToken] -public class ErrorModel : PageModel -{ - public string? RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; -} diff --git a/src/Modix.Web/Pages/Error.razor b/src/Modix.Web/Pages/Error.razor new file mode 100644 index 00000000..576cc2d2 --- /dev/null +++ b/src/Modix.Web/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} From 926772a145859d83b768326ea6e3e2d55fe92a27 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:37:46 +0200 Subject: [PATCH 20/46] Move Tags page over to the wasm project (pending auth fix) --- .../Models/Tags/TagCreationData.cs | 3 + src/Modix.Web.Shared/Models/Tags/TagData.cs | 11 ++ .../Pages/Tags.razor | 78 +++++++------- src/Modix.Web/Controllers/RolesController.cs | 43 ++++++++ src/Modix.Web/Controllers/TagsController.cs | 102 ++++++++++++++++++ src/Modix.Web/Models/Tags/TagData.cs | 32 ------ 6 files changed, 200 insertions(+), 69 deletions(-) create mode 100644 src/Modix.Web.Shared/Models/Tags/TagCreationData.cs create mode 100644 src/Modix.Web.Shared/Models/Tags/TagData.cs rename src/{Modix.Web => Modix.Web.Wasm}/Pages/Tags.razor (75%) create mode 100644 src/Modix.Web/Controllers/RolesController.cs create mode 100644 src/Modix.Web/Controllers/TagsController.cs delete mode 100644 src/Modix.Web/Models/Tags/TagData.cs diff --git a/src/Modix.Web.Shared/Models/Tags/TagCreationData.cs b/src/Modix.Web.Shared/Models/Tags/TagCreationData.cs new file mode 100644 index 00000000..adccc14f --- /dev/null +++ b/src/Modix.Web.Shared/Models/Tags/TagCreationData.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Tags; + +public record TagCreationData(string? Name, string? Content); diff --git a/src/Modix.Web.Shared/Models/Tags/TagData.cs b/src/Modix.Web.Shared/Models/Tags/TagData.cs new file mode 100644 index 00000000..25924a8c --- /dev/null +++ b/src/Modix.Web.Shared/Models/Tags/TagData.cs @@ -0,0 +1,11 @@ +namespace Modix.Web.Shared.Models.Tags; + +public record TagData( + string Name, + DateTimeOffset Created, + bool IsOwnedByRole, + ulong OwnerId, + string OwnerName, + string Content, + uint Uses, + bool CanMaintain); diff --git a/src/Modix.Web/Pages/Tags.razor b/src/Modix.Web.Wasm/Pages/Tags.razor similarity index 75% rename from src/Modix.Web/Pages/Tags.razor rename to src/Modix.Web.Wasm/Pages/Tags.razor index a80f318d..5c91bbb1 100644 --- a/src/Modix.Web/Pages/Tags.razor +++ b/src/Modix.Web.Wasm/Pages/Tags.razor @@ -1,14 +1,10 @@ @page "/tags" -@attribute [Authorize] -@using Modix.Data.Models.Core; -@using Modix.Data.Models.Tags; -@using Modix.Services.Tags; -@using Modix.Web.Models; -@using Modix.Web.Models.Tags; -@using Modix.Web.Services; +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.Tags @using MudBlazor -@using System.Globalization; +@attribute [Authorize] Modix - Tags @@ -42,7 +38,9 @@
- + @* TODO *@ + @* *@ + Create Refresh @@ -62,9 +60,9 @@ @tag.Name @tag.Created.ToString("MM/dd/yy, h:mm:ss tt") - @if (tag.OwnerRole is not null) + @if (tag.IsOwnedByRole) { - _ = Roles.TryGetValue(tag.OwnerRole.Id, out var role); + _ = Roles.TryGetValue(tag.OwnerId, out var role); var roleColor = role?.Color ?? "currentColor"; @@@tag.OwnerName } @@ -88,23 +86,20 @@ @code { [Inject] - private ITagService TagService { get; set; } = null!; - - [Inject] - private DiscordHelper DiscordHelper { get; set; } = null!; + public required IDialogService DialogService { get; set; } [Inject] - private IDialogService DialogService { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - private ISnackbar Snackbar { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } [Parameter] [SupplyParameterFromQuery] public string? Query { get; set; } private Dictionary? Roles { get; set; } - private TagData[]? Data { get; set; } + private List? Data { get; set; } private string? _tagNameValue; private string? _tagContentValue; @@ -122,20 +117,24 @@ private async Task FetchData() { - var currentGuild = DiscordHelper.GetUserGuild(); + Data = []; + + using var client = HttpClientFactory.CreateClient("api"); + + var rolesTask = client.GetFromJsonAsync>("api/roles"); - var summaries = await TagService.GetSummariesAsync(new TagSearchCriteria + var tagData = client.GetFromJsonAsAsyncEnumerable("api/tags"); + + await foreach(var tag in tagData) { - GuildId = currentGuild.Id, - }); + if (tag is null) + continue; - Data = summaries - .Select(TagData.CreateFromSummary) - .ToArray(); + Data.Add(tag); + StateHasChanged(); + } - Roles = currentGuild.Roles - .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())) - .ToDictionary(x => x.Id, x => x); + Roles = await rolesTask; } private bool FilterFunction(TagData tag) @@ -143,10 +142,7 @@ if (string.IsNullOrWhiteSpace(Query)) return true; - if (tag.OwnerUser is not null && (tag.OwnerUser.Username.Contains(Query, StringComparison.OrdinalIgnoreCase) || tag.OwnerUser.Id.ToString() == Query)) - return true; - - if (tag.OwnerRole is not null && (tag.OwnerRole.Name.Contains(Query, StringComparison.OrdinalIgnoreCase) || tag.OwnerRole.Id.ToString() == Query)) + if(tag.OwnerName.Contains(Query, StringComparison.OrdinalIgnoreCase) || tag.OwnerId.ToString() == Query) return true; if (tag.Name.Contains(Query, StringComparison.OrdinalIgnoreCase)) @@ -162,12 +158,20 @@ { try { - var currentUser = DiscordHelper.GetCurrentUser(); + var tagCreationData = new TagCreationData(_tagNameValue, _tagContentValue); + + using var client = HttpClientFactory.CreateClient("api"); + using var response = await client.PutAsJsonAsync("api/tags", tagCreationData); + + var createdTag = await response.Content.ReadFromJsonAsync(); - await TagService.CreateTagAsync(currentUser!.Guild.Id, currentUser.Id, _tagNameValue, _tagContentValue); - var createdTag = await TagService.GetTagAsync(currentUser.Guild.Id, _tagNameValue); + if (createdTag is null) + { + Snackbar.Add("Something went wrong while saving the tag.", Severity.Error); + return; + } - Data = Data!.Append(TagData.CreateFromSummary(createdTag)).ToArray(); + Data?.Add(createdTag); Snackbar.Add($"Tag '{_tagNameValue}' created.", Severity.Success); } catch (Exception ex) @@ -187,4 +191,4 @@ { _createDialogVisible = !_createDialogVisible; } -} +} \ No newline at end of file diff --git a/src/Modix.Web/Controllers/RolesController.cs b/src/Modix.Web/Controllers/RolesController.cs new file mode 100644 index 00000000..c7a2c15f --- /dev/null +++ b/src/Modix.Web/Controllers/RolesController.cs @@ -0,0 +1,43 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Web.Models; +using Modix.Web.Shared.Models.Common; + +namespace Modix.Web.Controllers; + +[Route("~/api/roles")] +[ApiController] +[Authorize] +public class RolesController : ControllerBase +{ + private readonly DiscordSocketClient _discordSocketClient; + + public RolesController(DiscordSocketClient discordSocketClient) + { + _discordSocketClient = discordSocketClient; + } + + [HttpGet] + public async Task> GetRoles() + { + // TODO: Move this to a base class like ModixController? + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + return guildToSearch.Roles + .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())) + .ToDictionary(x => x.Id); + } +} diff --git a/src/Modix.Web/Controllers/TagsController.cs b/src/Modix.Web/Controllers/TagsController.cs new file mode 100644 index 00000000..68d97ed3 --- /dev/null +++ b/src/Modix.Web/Controllers/TagsController.cs @@ -0,0 +1,102 @@ +using System.Diagnostics; +using System.Security.Claims; +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Data.Models.Core; +using Modix.Data.Models.Tags; +using Modix.Services.Tags; +using Modix.Web.Models; +using Modix.Web.Shared.Models.Tags; + +namespace Modix.Web.Controllers; + +[Route("~/api/tags")] +[ApiController] +[Authorize] +public class TagsController : ControllerBase +{ + private readonly ITagService _tagService; + private readonly DiscordSocketClient _discordSocketClient; + + public TagsController(ITagService tagService, DiscordSocketClient discordSocketClient) + { + _tagService = tagService; + _discordSocketClient = discordSocketClient; + } + + [HttpGet] + public async Task> GetTagsAsync() + { + // TODO: Move this to a base class like ModixController? + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + var summaries = await _tagService.GetSummariesAsync(new TagSearchCriteria + { + GuildId = guildToSearch.Id, + }); + + return summaries.Select(CreateFromSummary); + } + + [HttpPut] + [Authorize(Roles = nameof(AuthorizationClaim.CreateTag))] + public async Task CreateTagAsync([FromBody] TagCreationData tagCreationData) + { + try + { + // TODO: Move this to a base class like ModixController? + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + + SocketGuild guildToSearch; + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = _discordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = _discordSocketClient.Guilds.First(); + } + + var userId = ulong.Parse(User.FindFirst(ClaimTypes.NameIdentifier)?.Value); + + await _tagService.CreateTagAsync(guildToSearch.Id, userId, tagCreationData.Name, tagCreationData.Content); + var createdTag = await _tagService.GetTagAsync(guildToSearch.Id, tagCreationData.Name); + + var tagSummary = CreateFromSummary(createdTag); + + return Ok(tagSummary); + } + catch (Exception) + { + return BadRequest(); + } + } + + private static TagData CreateFromSummary(TagSummary summary) + { + return new TagData( + summary.Name, + summary.CreateAction.Created, + summary.OwnerRole is not null, + summary.OwnerUser?.Id ?? summary.OwnerRole?.Id ?? throw new UnreachableException("No owner??"), + summary.OwnerUser?.Username ?? summary.OwnerRole?.Name ?? throw new UnreachableException("No owner??"), + summary.Content, + summary.Uses, + false); + } +} diff --git a/src/Modix.Web/Models/Tags/TagData.cs b/src/Modix.Web/Models/Tags/TagData.cs deleted file mode 100644 index 7e6fa3d5..00000000 --- a/src/Modix.Web/Models/Tags/TagData.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Modix.Data.Models.Core; -using Modix.Data.Models.Tags; - -namespace Modix.Web.Models.Tags; - -public record TagData( - string Name, - DateTimeOffset Created, - bool IsOwnedByRole, - GuildUserBrief? OwnerUser, - GuildRoleBrief? OwnerRole, - string? OwnerName, - string Content, - uint Uses, - bool CanMaintain, - TagSummary TagSummary) -{ - public static TagData CreateFromSummary(TagSummary summary) - { - return new TagData( - summary.Name, - summary.CreateAction.Created, - summary.OwnerRole is not null, - summary.OwnerUser, - summary.OwnerRole, - summary.OwnerRole?.Name ?? summary.OwnerUser?.Username, - summary.Content, - summary.Uses, - false, - summary); - } -} From f4028fcbe1d10ef88a3be1986900d5affded16a6 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:58:18 +0200 Subject: [PATCH 21/46] Reintroduce ModixController to get rid of redundant code, also fixes internal authentication towards the AuthenticationService --- .../Controllers/AutocompleteController.cs | 33 ++------- .../Controllers/GuildStatsController.cs | 34 +++------ src/Modix.Web/Controllers/ModixController.cs | 71 +++++++++++++++++++ src/Modix.Web/Controllers/RolesController.cs | 27 ++----- src/Modix.Web/Controllers/TagsController.cs | 48 ++----------- .../Controllers/UserInformationController.cs | 49 +++---------- 6 files changed, 109 insertions(+), 153 deletions(-) create mode 100644 src/Modix.Web/Controllers/ModixController.cs diff --git a/src/Modix.Web/Controllers/AutocompleteController.cs b/src/Modix.Web/Controllers/AutocompleteController.cs index 873a5473..751d6009 100644 --- a/src/Modix.Web/Controllers/AutocompleteController.cs +++ b/src/Modix.Web/Controllers/AutocompleteController.cs @@ -2,10 +2,10 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; using Modix.Data.Utilities; using Modix.Services.Core; using Modix.Services.Utilities; -using Modix.Web.Models; using Modix.Web.Shared.Models.Common; namespace Modix.Web.Controllers; @@ -13,46 +13,28 @@ namespace Modix.Web.Controllers; [Route("~/api/autocomplete")] [ApiController] [Authorize] -public class AutocompleteController : ControllerBase +public class AutocompleteController : ModixController { - private readonly DiscordSocketClient _discordSocketClient; private readonly IUserService _userService; - public AutocompleteController(DiscordSocketClient discordSocketClient, IUserService userService) + public AutocompleteController(DiscordSocketClient discordSocketClient, IUserService userService, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) { - _discordSocketClient = discordSocketClient; _userService = userService; } [HttpGet("users/{query}")] - public async Task> AutocompleteUsers(string query) + public async Task> AutocompleteUsersAsync(string query) { - // TODO: Move this to a base class like ModixController? - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - - if (guildToSearch?.Users is null) - return []; - - var result = guildToSearch.Users + var result = UserGuild.Users .Where(d => d.Username.OrdinalContains(query) || d.Id.ToString() == query) .Take(10) .Select(FromIGuildUser); if (!result.Any() && ulong.TryParse(query, out var userId)) { - var user = await _userService.GetUserInformationAsync(guildToSearch.Id, userId); + var user = await _userService.GetUserInformationAsync(UserGuild.Id, userId); if (user is not null) { @@ -63,7 +45,6 @@ public async Task> AutocompleteUsers(string query) return result; } - public static ModixUser FromIGuildUser(IGuildUser user) => new() { Name = user.GetDisplayName(), diff --git a/src/Modix.Web/Controllers/GuildStatsController.cs b/src/Modix.Web/Controllers/GuildStatsController.cs index a33232ae..1420b790 100644 --- a/src/Modix.Web/Controllers/GuildStatsController.cs +++ b/src/Modix.Web/Controllers/GuildStatsController.cs @@ -2,6 +2,8 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Services.Core; using Modix.Services.GuildStats; using Modix.Web.Models; using Modix.Web.Shared.Models.Stats; @@ -11,43 +13,25 @@ namespace Modix.Web.Controllers; [Route("~/api")] [ApiController] [Authorize] -public class GuildStatsController : ControllerBase +public class GuildStatsController : ModixController { private readonly IGuildStatService _guildStatService; - private readonly DiscordSocketClient _discordSocketClient; - public GuildStatsController(IGuildStatService guildStatService, DiscordSocketClient discordSocketClient) + public GuildStatsController(IGuildStatService guildStatService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) { _guildStatService = guildStatService; - _discordSocketClient = discordSocketClient; } [HttpGet("guildstats")] - public async Task GuildStats() + public async Task GuildStatsAsync() { - // TODO: Move this to a base class like ModixController? - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - - - var userId = ulong.Parse(User.FindFirst(d => d.Type == ClaimTypes.NameIdentifier)?.Value); - - var roleCounts = await _guildStatService.GetGuildMemberDistributionAsync(guildToSearch); - var messageCounts = await _guildStatService.GetTopMessageCounts(guildToSearch, userId); + var roleCounts = await _guildStatService.GetGuildMemberDistributionAsync(UserGuild); + var messageCounts = await _guildStatService.GetTopMessageCounts(UserGuild, SocketUser.Id); var guildRoleCounts = roleCounts.Select(x => new GuildRoleMemberCount(x.Name, x.Count, x.Color)); var topUserMessageCounts = messageCounts.Select(x => new PerUserMessageCount(x.Username, x.Discriminator, x.Rank, x.MessageCount, x.IsCurrentUser)); - return new GuildStatData(guildToSearch.Name, [..guildRoleCounts], [..topUserMessageCounts]); + return new GuildStatData(UserGuild.Name, [..guildRoleCounts], [..topUserMessageCounts]); } } diff --git a/src/Modix.Web/Controllers/ModixController.cs b/src/Modix.Web/Controllers/ModixController.cs new file mode 100644 index 00000000..fa8f7751 --- /dev/null +++ b/src/Modix.Web/Controllers/ModixController.cs @@ -0,0 +1,71 @@ +#nullable enable +using System.Security.Claims; +using Discord.WebSocket; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Modix.Web.Models; + +namespace Modix.Controllers; + +[Authorize] +public class ModixController : Controller +{ + protected DiscordSocketClient DiscordSocketClient { get; private set; } + protected SocketGuildUser SocketUser { get; private set; } + protected SocketGuild UserGuild => SocketUser.Guild; + + protected Services.Core.IAuthorizationService ModixAuth { get; private set; } + + public ModixController(DiscordSocketClient client, Services.Core.IAuthorizationService modixAuth) + { + DiscordSocketClient = client; + ModixAuth = modixAuth; + } + + + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + if (!DiscordSocketClient.Guilds.Any()) + return; + + if (User is null) + { + await HttpContext.ChallengeAsync(); + return; + } + + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); + if (!ulong.TryParse(userId, out var userSnowflake)) + { + await HttpContext.ChallengeAsync(); + return; + } + + var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; + SocketGuild guildToSearch; + + if (!string.IsNullOrWhiteSpace(guildCookie)) + { + var guildId = ulong.Parse(guildCookie); + guildToSearch = DiscordSocketClient.GetGuild(guildId); + } + else + { + guildToSearch = DiscordSocketClient.Guilds.First(); + } + + SocketUser = guildToSearch.GetUser(userSnowflake); + + if (SocketUser is null) + { + await HttpContext.ChallengeAsync(); + return; + } + + await ModixAuth.OnAuthenticatedAsync(SocketUser.Id, SocketUser.Guild.Id, [.. SocketUser.Roles.Select(x => x.Id) ]); + + await next(); + } +} diff --git a/src/Modix.Web/Controllers/RolesController.cs b/src/Modix.Web/Controllers/RolesController.cs index c7a2c15f..f17daf86 100644 --- a/src/Modix.Web/Controllers/RolesController.cs +++ b/src/Modix.Web/Controllers/RolesController.cs @@ -1,7 +1,7 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Web.Models; +using Modix.Controllers; using Modix.Web.Shared.Models.Common; namespace Modix.Web.Controllers; @@ -9,34 +9,17 @@ namespace Modix.Web.Controllers; [Route("~/api/roles")] [ApiController] [Authorize] -public class RolesController : ControllerBase +public class RolesController : ModixController { - private readonly DiscordSocketClient _discordSocketClient; - - public RolesController(DiscordSocketClient discordSocketClient) + public RolesController(DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) { - _discordSocketClient = discordSocketClient; } [HttpGet] public async Task> GetRoles() { - // TODO: Move this to a base class like ModixController? - - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - - return guildToSearch.Roles + return UserGuild.Roles .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())) .ToDictionary(x => x.Id); } diff --git a/src/Modix.Web/Controllers/TagsController.cs b/src/Modix.Web/Controllers/TagsController.cs index 68d97ed3..90e4166d 100644 --- a/src/Modix.Web/Controllers/TagsController.cs +++ b/src/Modix.Web/Controllers/TagsController.cs @@ -1,12 +1,11 @@ using System.Diagnostics; -using System.Security.Claims; using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; using Modix.Data.Models.Core; using Modix.Data.Models.Tags; using Modix.Services.Tags; -using Modix.Web.Models; using Modix.Web.Shared.Models.Tags; namespace Modix.Web.Controllers; @@ -14,38 +13,22 @@ namespace Modix.Web.Controllers; [Route("~/api/tags")] [ApiController] [Authorize] -public class TagsController : ControllerBase +public class TagsController : ModixController { private readonly ITagService _tagService; - private readonly DiscordSocketClient _discordSocketClient; - public TagsController(ITagService tagService, DiscordSocketClient discordSocketClient) + public TagsController(ITagService tagService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) { _tagService = tagService; - _discordSocketClient = discordSocketClient; } [HttpGet] public async Task> GetTagsAsync() { - // TODO: Move this to a base class like ModixController? - - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - var summaries = await _tagService.GetSummariesAsync(new TagSearchCriteria { - GuildId = guildToSearch.Id, + GuildId = UserGuild.Id, }); return summaries.Select(CreateFromSummary); @@ -57,25 +40,8 @@ public async Task CreateTagAsync([FromBody] TagCreationData tagCr { try { - // TODO: Move this to a base class like ModixController? - - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - - var userId = ulong.Parse(User.FindFirst(ClaimTypes.NameIdentifier)?.Value); - - await _tagService.CreateTagAsync(guildToSearch.Id, userId, tagCreationData.Name, tagCreationData.Content); - var createdTag = await _tagService.GetTagAsync(guildToSearch.Id, tagCreationData.Name); + await _tagService.CreateTagAsync(UserGuild.Id, SocketUser.Id, tagCreationData.Name, tagCreationData.Content); + var createdTag = await _tagService.GetTagAsync(UserGuild.Id, tagCreationData.Name); var tagSummary = CreateFromSummary(createdTag); diff --git a/src/Modix.Web/Controllers/UserInformationController.cs b/src/Modix.Web/Controllers/UserInformationController.cs index ea95b353..b0f305ae 100644 --- a/src/Modix.Web/Controllers/UserInformationController.cs +++ b/src/Modix.Web/Controllers/UserInformationController.cs @@ -2,11 +2,11 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; using Modix.Data.Models.Core; using Modix.Data.Repositories; using Modix.Services.Core; using Modix.Services.Utilities; -using Modix.Web.Models; using Modix.Web.Shared.Models.Common; using Modix.Web.Shared.Models.UserLookup; @@ -15,17 +15,16 @@ namespace Modix.Web.Controllers; [Route("~/api/userinformation")] [ApiController] [Authorize] -public class UserInformationController : ControllerBase +public class UserInformationController : ModixController { private readonly IUserService _userService; private readonly IMessageRepository _messageRepository; - private readonly DiscordSocketClient _discordSocketClient; - public UserInformationController(IUserService userService, IMessageRepository messageRepository, DiscordSocketClient discordSocketClient) + public UserInformationController(IUserService userService, IMessageRepository messageRepository, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) { _userService = userService; _messageRepository = messageRepository; - _discordSocketClient = discordSocketClient; } @@ -35,31 +34,16 @@ public UserInformationController(IUserService userService, IMessageRepository me if (!ulong.TryParse(userIdString, out var userId)) return null; - // TODO: Move this to a base class like ModixController? - - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - - var userInformation = await _userService.GetUserInformationAsync(guildToSearch.Id, userId); + var userInformation = await _userService.GetUserInformationAsync(UserGuild.Id, userId); if (userInformation is null) return null; - var userRank = await _messageRepository.GetGuildUserParticipationStatistics(guildToSearch.Id, userId); - var messages7 = await _messageRepository.GetGuildUserMessageCountByDate(guildToSearch.Id, userId, TimeSpan.FromDays(7)); - var messages30 = await _messageRepository.GetGuildUserMessageCountByDate(guildToSearch.Id, userId, TimeSpan.FromDays(30)); + var userRank = await _messageRepository.GetGuildUserParticipationStatistics(UserGuild.Id, userId); + var messages7 = await _messageRepository.GetGuildUserMessageCountByDate(UserGuild.Id, userId, TimeSpan.FromDays(7)); + var messages30 = await _messageRepository.GetGuildUserMessageCountByDate(UserGuild.Id, userId, TimeSpan.FromDays(30)); var roles = userInformation.RoleIds - .Select(x => guildToSearch.GetRole(x)) + .Select(x => UserGuild.GetRole(x)) .OrderByDescending(x => x.IsHoisted) .ThenByDescending(x => x.Position) .ToArray(); @@ -73,21 +57,8 @@ public async Task GetUserMessagesPerChannel if (!ulong.TryParse(userIdString, out var userId)) return []; - var guildCookie = Request.Cookies[CookieConstants.SelectedGuild]; - - SocketGuild guildToSearch; - if (!string.IsNullOrWhiteSpace(guildCookie)) - { - var guildId = ulong.Parse(guildCookie); - guildToSearch = _discordSocketClient.GetGuild(guildId); - } - else - { - guildToSearch = _discordSocketClient.Guilds.First(); - } - var timespan = DateTimeOffset.UtcNow - after; - var result = await _messageRepository.GetGuildUserMessageCountByChannel(guildToSearch.Id, userId, timespan); + var result = await _messageRepository.GetGuildUserMessageCountByChannel(UserGuild.Id, userId, timespan); var colors = ColorUtils.GetRainbowColors(result.Count); return result.Select((x,i) => new MessageCountPerChannelInformation(x.ChannelName, x.MessageCount, colors[i].ToString())) From ee2f8d8a3e9734ff3654facda26c2f7dbd95c511 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:59:18 +0200 Subject: [PATCH 22/46] Remove default pages --- src/Modix.Web.Wasm/Pages/Counter.razor | 19 ------ src/Modix.Web.Wasm/Pages/Weather.razor | 61 ------------------- src/Modix.Web/Controllers/ValuesController.cs | 44 ------------- 3 files changed, 124 deletions(-) delete mode 100644 src/Modix.Web.Wasm/Pages/Counter.razor delete mode 100644 src/Modix.Web.Wasm/Pages/Weather.razor delete mode 100644 src/Modix.Web/Controllers/ValuesController.cs diff --git a/src/Modix.Web.Wasm/Pages/Counter.razor b/src/Modix.Web.Wasm/Pages/Counter.razor deleted file mode 100644 index 64f3e557..00000000 --- a/src/Modix.Web.Wasm/Pages/Counter.razor +++ /dev/null @@ -1,19 +0,0 @@ -@page "/counter" -@rendermode RenderMode.InteractiveAuto - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/src/Modix.Web.Wasm/Pages/Weather.razor b/src/Modix.Web.Wasm/Pages/Weather.razor deleted file mode 100644 index d31785ab..00000000 --- a/src/Modix.Web.Wasm/Pages/Weather.razor +++ /dev/null @@ -1,61 +0,0 @@ -@page "/weather" -@inject HttpClient Http - -Weather - -

Weather

- -

This component demonstrates fetching data from the server.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender) - return; - - forecasts = await Http.GetFromJsonAsync("/api/values/GetData"); - StateHasChanged(); - } - - public class WeatherForecast - { - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public string? Summary { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} diff --git a/src/Modix.Web/Controllers/ValuesController.cs b/src/Modix.Web/Controllers/ValuesController.cs deleted file mode 100644 index 64876486..00000000 --- a/src/Modix.Web/Controllers/ValuesController.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Modix.Web.Controllers; - -[Route("api/[controller]")] -[ApiController] -public class ValuesController : ControllerBase -{ - [HttpGet("getdata")] - public IActionResult GetData() - { - var e = """ - [ - { - "date": "2022-01-06", - "temperatureC": 1, - "summary": "Freezing" - }, - { - "date": "2022-01-07", - "temperatureC": 14, - "summary": "Bracing" - }, - { - "date": "2022-01-08", - "temperatureC": -13, - "summary": "Freezing" - }, - { - "date": "2022-01-09", - "temperatureC": -16, - "summary": "Balmy" - }, - { - "date": "2022-01-10", - "temperatureC": -2, - "summary": "Chilly" - } - ] - """; - return Ok(e); - } - -} From 64ee8f4d00f5e586302cb65a6cfdbec77e715649 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:10:31 +0200 Subject: [PATCH 23/46] Cache roles for 10 minutes --- src/Modix.Web/Controllers/RolesController.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Modix.Web/Controllers/RolesController.cs b/src/Modix.Web/Controllers/RolesController.cs index f17daf86..97a95f98 100644 --- a/src/Modix.Web/Controllers/RolesController.cs +++ b/src/Modix.Web/Controllers/RolesController.cs @@ -1,6 +1,7 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; using Modix.Controllers; using Modix.Web.Shared.Models.Common; @@ -11,16 +12,24 @@ namespace Modix.Web.Controllers; [Authorize] public class RolesController : ModixController { - public RolesController(DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + private readonly IMemoryCache _memoryCache; + + public RolesController(IMemoryCache memoryCache, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) : base(discordSocketClient, authorizationService) { + _memoryCache = memoryCache; } [HttpGet] - public async Task> GetRoles() + public Dictionary GetRoles() { - return UserGuild.Roles - .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())) - .ToDictionary(x => x.Id); + return _memoryCache.GetOrCreate("roles", cacheEntry => + { + cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10); + + return UserGuild.Roles + .Select(x => new RoleInformation(x.Id, x.Name, x.Color.ToString())) + .ToDictionary(x => x.Id); + })!; } } From b6986c5b3e797950ee3c2d4b1df282e03936af4a Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 24 Aug 2024 20:58:16 +0200 Subject: [PATCH 24/46] Move Promotions page over to the wasm project --- .../Models/Promotions/CampaignCommentData.cs | 4 +- .../Promotions/PromotionCampaignData.cs | 16 ++ .../Promotions/PromotionCampaignOutcome.cs | 17 ++ .../Models/Promotions/PromotionSentiment.cs | 17 ++ .../Components/ConfirmationDialog.razor | 28 +++ .../Components/CreateCampaignComment.razor | 2 +- .../EditPromotionCommentDialog.razor | 2 +- .../Pages/Promotions.razor | 210 ++++++++++-------- .../Controllers/CampaignController.cs | 119 ++++++++++ 9 files changed, 313 insertions(+), 102 deletions(-) rename src/{Modix.Web => Modix.Web.Shared}/Models/Promotions/CampaignCommentData.cs (65%) create mode 100644 src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs create mode 100644 src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs create mode 100644 src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs create mode 100644 src/Modix.Web.Wasm/Components/ConfirmationDialog.razor rename src/{Modix.Web => Modix.Web.Wasm}/Components/CreateCampaignComment.razor (96%) rename src/{Modix.Web => Modix.Web.Wasm}/Components/EditPromotionCommentDialog.razor (97%) rename src/{Modix.Web => Modix.Web.Wasm}/Pages/Promotions.razor (63%) create mode 100644 src/Modix.Web/Controllers/CampaignController.cs diff --git a/src/Modix.Web/Models/Promotions/CampaignCommentData.cs b/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs similarity index 65% rename from src/Modix.Web/Models/Promotions/CampaignCommentData.cs rename to src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs index d245e89a..961715ed 100644 --- a/src/Modix.Web/Models/Promotions/CampaignCommentData.cs +++ b/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs @@ -1,5 +1,3 @@ -using Modix.Data.Models.Promotions; - -namespace Modix.Web.Models.Promotions; +namespace Modix.Web.Shared.Models.Promotions; public record CampaignCommentData(long Id, PromotionSentiment PromotionSentiment, string Content, DateTimeOffset CreatedAt, bool IsFromCurrentUser); diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs new file mode 100644 index 00000000..46dc7a40 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs @@ -0,0 +1,16 @@ +namespace Modix.Web.Shared.Models.Promotions; + +public record PromotionCampaignData +{ + public required long Id { get; init; } + public required ulong SubjectId { get; init; } + public required string SubjectName { get; init; } + public required ulong TargetRoleId { get; init; } + public required string TargetRoleName { get; init; } + public required PromotionCampaignOutcome? Outcome { get; set; } + public required DateTimeOffset Created { get; init; } + public required bool IsCurrentUserCampaign { get; init; } + public required int ApproveCount { get; init; } + public required int OpposeCount { get; init; } + public required bool IsClosed { get; init; } +} diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs new file mode 100644 index 00000000..9c2198a5 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs @@ -0,0 +1,17 @@ +namespace Modix.Web.Shared.Models.Promotions; + +public enum PromotionCampaignOutcome +{ + /// + /// Describes a campaign that was accepted, for which the subject was promoted to the proposed rank. + /// + Accepted, + /// + /// Describes a campaign that was rejected, for which the subject was not promoted to the proposed rank. + /// + Rejected, + /// + /// Describes a campaign for which an error occurred during processing, that prevented the proposed promotion from being fully applied. + /// + Failed +} diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs new file mode 100644 index 00000000..42675931 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs @@ -0,0 +1,17 @@ +namespace Modix.Web.Shared.Models.Promotions; + +public enum PromotionSentiment +{ + /// + /// Describes a comment that does not express a specific opinion about a promotion campaign. + /// + Abstain, + /// + /// Describes a comment that approves of a promotion campaign. + /// + Approve, + /// + /// Describes a comment that opposes a promotion campaign. + /// + Oppose +} diff --git a/src/Modix.Web.Wasm/Components/ConfirmationDialog.razor b/src/Modix.Web.Wasm/Components/ConfirmationDialog.razor new file mode 100644 index 00000000..2c929c5f --- /dev/null +++ b/src/Modix.Web.Wasm/Components/ConfirmationDialog.razor @@ -0,0 +1,28 @@ +@using MudBlazor + + + + Confirmation + + + @Content + + + Confirm + + Cancel + + + + + +@code { + [CascadingParameter] + MudDialogInstance? MudDialog { get; set; } + + [Parameter] + public required string Content { get; set; } + + void Submit() => MudDialog?.Close(); + void Cancel() => MudDialog?.Cancel(); +} diff --git a/src/Modix.Web/Components/CreateCampaignComment.razor b/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor similarity index 96% rename from src/Modix.Web/Components/CreateCampaignComment.razor rename to src/Modix.Web.Wasm/Components/CreateCampaignComment.razor index 1d520e2c..389f456a 100644 --- a/src/Modix.Web/Components/CreateCampaignComment.razor +++ b/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor @@ -1,4 +1,4 @@ -@using Modix.Data.Models.Promotions; +@using Modix.Web.Shared.Models.Promotions @using MudBlazor;
diff --git a/src/Modix.Web/Components/EditPromotionCommentDialog.razor b/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor similarity index 97% rename from src/Modix.Web/Components/EditPromotionCommentDialog.razor rename to src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor index 33a7f599..54d2ed78 100644 --- a/src/Modix.Web/Components/EditPromotionCommentDialog.razor +++ b/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor @@ -1,4 +1,4 @@ -@using Modix.Data.Models.Promotions; +@using Modix.Web.Shared.Models.Promotions @using MudBlazor diff --git a/src/Modix.Web/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor similarity index 63% rename from src/Modix.Web/Pages/Promotions.razor rename to src/Modix.Web.Wasm/Pages/Promotions.razor index 4a060df7..8dc22e14 100644 --- a/src/Modix.Web/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -1,18 +1,18 @@ @page "/promotions" -@attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsRead))] - -@using Modix.Data.Models.Core; -@using Modix.Data.Models.Promotions; -@using Modix.Data.Utilities; -@using Modix.Services.Promotions; -@using Modix.Web.Components; -@using Modix.Web.Models.Promotions; +@* TODO *@ +@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsRead))] *@ +@attribute [Authorize(Roles = "PromotionsRead")] + +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@using Modix.Web.Services; +@using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.Promotions +@using Modix.Web.Wasm.Components @using MudBlazor @using Humanizer; -@using Modix.Services.Utilities; +@using System.Security.Claims Modix - Promotions @@ -26,12 +26,10 @@
@foreach (var (roleColor, campaign) in Campaigns - .Where(x => _showInactive ? true : (x.Campaign.Outcome is null)) - .OrderByDescending(x => x.Campaign.Outcome is null) - .ThenByDescending(x => x.Campaign.CreateAction.Created)) + .Where(x => _showInactive ? true : !x.Campaign.IsClosed) + .OrderByDescending(x => !x.Campaign.IsClosed) + .ThenByDescending(x => x.Campaign.Created)) { - var isCurrentUserCampaign = CurrentUserId == campaign.Subject.Id; - var icon = campaign.Outcome switch { PromotionCampaignOutcome.Accepted => Icons.Material.Filled.Check, @@ -40,16 +38,16 @@ _ => Icons.Material.Filled.HowToVote }; - var sentimentRatio = isCurrentUserCampaign ? 0d : (double)campaign.ApproveCount / (campaign.ApproveCount + campaign.OpposeCount); + var sentimentRatio = campaign.IsCurrentUserCampaign ? 0d : (double)campaign.ApproveCount / (campaign.ApproveCount + campaign.OpposeCount); var sentimentColor = sentimentRatio switch { - _ when isCurrentUserCampaign => Color.Transparent, + _ when campaign.IsCurrentUserCampaign => Color.Transparent, > 0.67 => Color.Success, > 0.33 => Color.Warning, _ => Color.Error }; - +
@@ -57,23 +55,25 @@ - @campaign.Subject.GetFullUsername() + @campaign.SubjectName - @campaign.TargetRole.Name + @campaign.TargetRoleName
@if (campaign.Outcome is null) { - + @* TODO *@ + @* *@ + } -
- @(isCurrentUserCampaign ? "?" : campaign.ApproveCount.ToString()) + @(campaign.IsCurrentUserCampaign ? "?" : campaign.ApproveCount.ToString())
- @(isCurrentUserCampaign ? "?" : campaign.OpposeCount.ToString()) + @(campaign.IsCurrentUserCampaign ? "?" : campaign.OpposeCount.ToString())
@@ -97,9 +97,9 @@
- Campaign started @campaign.CreateAction.Created.ToString("MM/dd/yy, h:mm:ss tt") + Campaign started @campaign.Created.ToString("MM/dd/yy, h:mm:ss tt") - @if (campaign.Subject.Id == CurrentUserId) + @if (campaign.IsCurrentUserCampaign) { Sorry, you aren't allowed to see comments on your own campaign. @@ -118,7 +118,7 @@ @comment.Content - @if (comment.IsFromCurrentUser && campaign.CloseAction is null) + @if (comment.IsFromCurrentUser && !campaign.IsClosed) { } @comment.CreatedAt.ToString("MM/dd/yy, h:mm:ss tt") -
+
} - if (campaign.CloseAction is null && !CampaignCommentData[campaign.Id].Any(x => x.Value.IsFromCurrentUser)) + if (!campaign.IsClosed && !CampaignCommentData[campaign.Id].Any(x => x.Value.IsFromCurrentUser)) { - + } } @@ -153,34 +153,29 @@ cursor: inherit; } - .targetRole:hover { - background-color: var(--mud-palette-background) !important; - border-color: currentColor !important; - } + .targetRole:hover { + background-color: var(--mud-palette-background) !important; + border-color: currentColor !important; + } @code { [Inject] - public SessionState SessionState { get; set; } = null!; + public required SessionState SessionState { get; set; } [Inject] - public CookieService CookieService { get; set; } = null!; + public required IDialogService DialogService { get; set; } [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - public IPromotionsService PromotionsService { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } - [Inject] - public IDialogService DialogService { get; set; } = null!; - - [Inject] - public ISnackbar Snackbar { get; set; } = null!; - - private ulong CurrentUserId { get; set; } + [CascadingParameter] + public required Task AuthenticationState { get; set; } - private IReadOnlyCollection<(string RoleColor, PromotionCampaignSummary Campaign)> Campaigns = Array.Empty<(string RoleColor, PromotionCampaignSummary Campaign)>(); + private List<(string RoleColor, PromotionCampaignData Campaign)> Campaigns = []; private Dictionary> CampaignCommentData = new Dictionary>(); private bool _showInactive; @@ -195,36 +190,35 @@ if (!firstRender) return; - var currentUser = DiscordHelper.GetCurrentUser(); - var roleColors = currentUser!.Guild.Roles.ToDictionary(x => x.Id, x => x.Color.ToString()); + using var client = HttpClientFactory.CreateClient("api"); - Campaigns = (await PromotionsService.SearchCampaignsAsync(new PromotionCampaignSearchCriteria - { - GuildId = currentUser.Guild.Id - })) - .Select(campaign => (GetRoleColor(roleColors, campaign.TargetRole.Id), campaign)) - .ToArray(); - - CurrentUserId = currentUser.Id; + var roles = await client.GetFromJsonAsync>("api/roles"); + var campaigns = client.GetFromJsonAsAsyncEnumerable("api/campaigns"); - StateHasChanged(); + await foreach (var campaign in campaigns) + { + var roleColor = GetRoleColor(roles, campaign.TargetRoleId); + Campaigns.Add((roleColor, campaign)); + StateHasChanged(); + } } - private string GetRoleColor(Dictionary roleColors, ulong roleId) + private string GetRoleColor(Dictionary roles, ulong roleId) { // In case the role has been deleted and we still have a campaign record for that role we serve a grey color. - if (!roleColors.TryGetValue(roleId, out var colorHex)) + if (!roles.TryGetValue(roleId, out var roleInformation)) { return $"color: grey"; } - return $"color: {colorHex}"; + return $"color: {roleInformation.Color}"; } private async Task ShowInactiveChanged(bool showInactive) { _showInactive = showInactive; - await CookieService.SetShowInactivePromotionsAsync(showInactive); + //TODO + // await CookieService.SetShowInactivePromotionsAsync(showInactive); } private async Task CampaignExpanded(bool wasExpanded, long campaignId, ulong userId) @@ -232,44 +226,51 @@ if (!wasExpanded) return; - if (CurrentUserId == userId) + var authState = await AuthenticationState; + var currentUserId = authState.User.FindFirst(ClaimTypes.NameIdentifier)!.Value; + var userSnowflake = ulong.Parse(currentUserId); + + if (userSnowflake == userId) return; if (CampaignCommentData.ContainsKey(campaignId)) return; - var result = await PromotionsService.GetCampaignDetailsAsync(campaignId); - if (result is null) + using var client = HttpClientFactory.CreateClient("api"); + var campaignComments = await client.GetFromJsonAsync>($"api/campaigns/{campaignId}"); + + if (campaignComments is null) { Snackbar.Add($"Unable to load campaign details for campaign id {campaignId}.", Severity.Error); return; } - CampaignCommentData[campaignId] = result.Comments - .Where(x => x.ModifyAction is null) - .Select(c => new CampaignCommentData(c.Id, c.Sentiment, c.Content, c.CreateAction.Created, c.CreateAction.CreatedBy.Id == CurrentUserId)) - .ToDictionary(x => x.Id, x => x); + CampaignCommentData[campaignId] = campaignComments; StateHasChanged(); } - private async Task OnCampaignCommentCreation(long campaignId, GuildUserBrief campaignSubject, PromotionSentiment sentiment, string? content) + private async Task OnCampaignCommentCreation(long campaignId, string campaignSubjectName, PromotionSentiment sentiment, string? content) { try { - var promotionActionSummary = await PromotionsService.AddCommentAsync(campaignId, sentiment, content); - var newComment = promotionActionSummary.NewComment; + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PutAsJsonAsync($"api/campaigns/{campaignId}/createcomment", new CampaignCommentData(default, sentiment, content, default, default)); + + response.EnsureSuccessStatusCode(); - CampaignCommentData[campaignId][newComment!.Id] = new CampaignCommentData(newComment.Id, newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + var newComment = await response.Content.ReadFromJsonAsync(); + + CampaignCommentData[campaignId][newComment!.Id] = newComment; + + Snackbar.Add($"Added comment to campaign for user {campaignSubjectName}.", Severity.Success); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Error); return; } - var username = campaignSubject.GetFullUsername(); - Snackbar.Add($"Added comment to campaign for user {username}.", Severity.Success); } private async Task ToggleEditDialog(long campaignId, long commentId, PromotionSentiment oldPromotionSentiment, string oldContent) @@ -290,30 +291,39 @@ try { - var promotionActionSummary = await PromotionsService.UpdateCommentAsync(commentId, newPromotionSentiment, newContent); - var newComment = promotionActionSummary.NewComment; + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PatchAsJsonAsync("api/campaigns/updatecomment", new CampaignCommentData(commentId, newPromotionSentiment, newContent, default, default)); + + response.EnsureSuccessStatusCode(); + + var newComment = await response.Content.ReadFromJsonAsync(); CampaignCommentData[campaignId].Remove(commentId); - CampaignCommentData[campaignId][newComment!.Id] = new CampaignCommentData(newComment.Id, newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + CampaignCommentData[campaignId][newComment!.Id] = newComment; + + Snackbar.Add("Campaign vote was updated.", Severity.Success); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Error); return; } - - Snackbar.Add("Campaign vote was updated.", Severity.Success); } - private async Task AcceptCampaign(PromotionCampaignSummary campaign) + private async Task AcceptCampaign(PromotionCampaignData campaign) { - var timeSince = DateTime.UtcNow - campaign.CreateAction.Created; + var timeSince = DateTime.UtcNow - campaign.Created; - var username = campaign.Subject.GetFullUsername(); + var username = campaign.SubjectName; bool force = false; - if (timeSince < PromotionCampaignEntityExtensions.CampaignAcceptCooldown) + + // TODO: Fix this, this should not be hardcoded + // if (timeSince < PromotionCampaignEntityExtensions.CampaignAcceptCooldown) + var campaignAcceptCooldown = TimeSpan.FromHours(48); + if (timeSince < campaignAcceptCooldown) { - var timeLeftHumanized = campaign.GetTimeUntilCampaignCanBeClosed().Humanize(3); + var timeLeft = campaign.Created.Add(campaignAcceptCooldown) - DateTimeOffset.UtcNow; + var timeLeftHumanized = timeLeft.Humanize(3); var dialogParams = new DialogParameters { { x => x.Content, $"There is {timeLeftHumanized} left on the campaign. Do you want to force accept the campaign for {username}?" } @@ -333,33 +343,39 @@ try { - await PromotionsService.AcceptCampaignAsync(campaign.Id, force); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PostAsync($"api/campaigns/{campaign.Id}/accept/{force}", default); + + response.EnsureSuccessStatusCode(); + + campaign.Outcome = PromotionCampaignOutcome.Accepted; + Snackbar.Add($"Campaign for '{username}' was accepted.", Severity.Success); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Error); return; } - campaign.Outcome = PromotionCampaignOutcome.Accepted; - Snackbar.Add($"Campaign for '{username}' was accepted.", Severity.Success); } - private async Task RejectCampaign(PromotionCampaignSummary campaign) + private async Task RejectCampaign(PromotionCampaignData campaign) { try { - await PromotionsService.RejectCampaignAsync(campaign.Id); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PostAsync($"api/campaigns/{campaign.Id}/reject", default); + + response.EnsureSuccessStatusCode(); + + campaign.Outcome = PromotionCampaignOutcome.Rejected; + Snackbar.Add($"Campaign for '{campaign.SubjectName}' was rejected.", Severity.Success); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Error); return; } - - var username = campaign.Subject.GetFullUsername(); - campaign.Outcome = PromotionCampaignOutcome.Rejected; - Snackbar.Add($"Campaign for '{username}' was rejected.", Severity.Success); } } diff --git a/src/Modix.Web/Controllers/CampaignController.cs b/src/Modix.Web/Controllers/CampaignController.cs new file mode 100644 index 00000000..06c5a0f4 --- /dev/null +++ b/src/Modix.Web/Controllers/CampaignController.cs @@ -0,0 +1,119 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models.Core; +using Modix.Services.Promotions; +using Modix.Services.Utilities; +using Modix.Web.Shared.Models.Promotions; + +namespace Modix.Web.Controllers; + +[Route("~/api/campaigns")] +[ApiController] +[Authorize] +public class CampaignController : ModixController +{ + private readonly IPromotionsService _promotionsService; + + public CampaignController(IPromotionsService promotionsService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _promotionsService = promotionsService; + } + + [HttpGet] + public async IAsyncEnumerable GetCampaignsAsync() + { + var campaigns = await _promotionsService.SearchCampaignsAsync(new Data.Models.Promotions.PromotionCampaignSearchCriteria + { + GuildId = UserGuild.Id + }); + + foreach (var campaign in campaigns) + { + yield return new PromotionCampaignData + { + Id = campaign.Id, + SubjectId = campaign.Subject.Id, + SubjectName = campaign.Subject.GetFullUsername(), + TargetRoleId = campaign.TargetRole.Id, + TargetRoleName = campaign.TargetRole.Name, + + // This is... very neat :) + Outcome = campaign.Outcome == null ? null : (Shared.Models.Promotions.PromotionCampaignOutcome)(int)campaign.Outcome, + + Created = campaign.CreateAction.Created, + IsCurrentUserCampaign = campaign.Subject.Id == SocketUser.Id, + ApproveCount = campaign.ApproveCount, + OpposeCount = campaign.OpposeCount, + IsClosed = campaign.CloseAction is not null + }; + } + } + + [HttpGet("{campaignId}")] + public async Task GetCampaignCommentsAsync(long campaignId) + { + var campaignDetails = await _promotionsService.GetCampaignDetailsAsync(campaignId); + + if (campaignDetails is null) + return NotFound(); + + var campaignComments = campaignDetails.Comments + .Where(x => x.ModifyAction is null) + .Select(x => new CampaignCommentData + ( + x.Id, + (PromotionSentiment)(int)x.Sentiment, + x.Content, + x.CreateAction.Created, + x.CreateAction.CreatedBy.Id == SocketUser.Id + ) + ) + .ToDictionary(x => x.Id); + + return Ok(campaignComments); + } + + [HttpPut("{campaignId}/createcomment")] + public async Task CreateCommentAsync(long campaignId, [FromBody] CampaignCommentData campaignCommentData) + { + var promotionActionSummary = await _promotionsService.AddCommentAsync( + campaignId, + (Data.Models.Promotions.PromotionSentiment)(int)campaignCommentData.PromotionSentiment, + campaignCommentData.Content); + + var newComment = promotionActionSummary.NewComment; + + return new CampaignCommentData(newComment.Id, (PromotionSentiment)(int)newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + } + + + [HttpPatch("updatecomment")] + public async Task UpdateCommentAsync([FromBody] CampaignCommentData campaignCommentData) + { + var promotionActionSummary = await _promotionsService.UpdateCommentAsync( + campaignCommentData.Id, + (Data.Models.Promotions.PromotionSentiment)(int)campaignCommentData.PromotionSentiment, + campaignCommentData.Content); + + var newComment = promotionActionSummary.NewComment; + + return new CampaignCommentData(newComment.Id, (PromotionSentiment)(int)newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + } + + [HttpPost("{campaignId}/accept/{force}")] + [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCloseCampaign))] + public async Task AcceptCampaignAsync(long campaignId, bool force) + { + await _promotionsService.AcceptCampaignAsync(campaignId, force); + } + + [HttpPost("{campaignId}/reject")] + [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCloseCampaign))] + public async Task RejectCampaignAsync(long campaignId) + { + await _promotionsService.RejectCampaignAsync(campaignId); + } +} From 8c55ec67a76d9e3607dad32885fea4982b8748d3 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 24 Aug 2024 21:43:48 +0200 Subject: [PATCH 25/46] Move CreatePromotion page over to the wasm project --- .../Models/Promotions/NextRank.cs | 2 +- .../Promotions/PromotionCreationData.cs | 3 + .../Pages/CreatePromotion.razor | 55 ++++++++++--------- .../Controllers/CampaignController.cs | 22 +++++++- 4 files changed, 52 insertions(+), 30 deletions(-) rename src/{Modix.Web => Modix.Web.Shared}/Models/Promotions/NextRank.cs (51%) create mode 100644 src/Modix.Web.Shared/Models/Promotions/PromotionCreationData.cs rename src/{Modix.Web => Modix.Web.Wasm}/Pages/CreatePromotion.razor (74%) diff --git a/src/Modix.Web/Models/Promotions/NextRank.cs b/src/Modix.Web.Shared/Models/Promotions/NextRank.cs similarity index 51% rename from src/Modix.Web/Models/Promotions/NextRank.cs rename to src/Modix.Web.Shared/Models/Promotions/NextRank.cs index 5c58b804..d60b8210 100644 --- a/src/Modix.Web/Models/Promotions/NextRank.cs +++ b/src/Modix.Web.Shared/Models/Promotions/NextRank.cs @@ -1,3 +1,3 @@ -namespace Modix.Web.Models.Promotions; +namespace Modix.Web.Shared.Models.Promotions; public record NextRank(string? Name, string Color); diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionCreationData.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionCreationData.cs new file mode 100644 index 00000000..43cf035b --- /dev/null +++ b/src/Modix.Web.Shared/Models/Promotions/PromotionCreationData.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Promotions; + +public record PromotionCreationData(ulong UserId, string? Comment); diff --git a/src/Modix.Web/Pages/CreatePromotion.razor b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor similarity index 74% rename from src/Modix.Web/Pages/CreatePromotion.razor rename to src/Modix.Web.Wasm/Pages/CreatePromotion.razor index 4e766dd9..d54cfc83 100644 --- a/src/Modix.Web/Pages/CreatePromotion.razor +++ b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor @@ -1,15 +1,15 @@ @page "/promotions/create" -@attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] -@using Modix.Data.Models.Core; -@using Modix.Services.Promotions; -@using Modix.Web.Components +@using Microsoft.AspNetCore.Authorization @using Modix.Web.Models; -@using Modix.Web.Models.Promotions; -@using Modix.Web.Services; @using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.Promotions @using Modix.Web.Wasm.Components @using MudBlazor +@*TODO*@ +@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] *@ +@attribute [Authorize(Roles = "PromotionsCreateCampaign")] + Modix - Start A Campaign @@ -29,7 +29,7 @@ @@ -70,16 +70,13 @@ @code { [Inject] - public IPromotionsService PromotionsService { get; set; } = null!; - - [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } [Inject] - public ISnackbar Snackbar { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - public NavigationManager NavigationManager { get; set; } = null!; + public required NavigationManager NavigationManager { get; set; } private ModixUser? _selectedUser; private string? _promotionComment; @@ -98,32 +95,36 @@ if (user is null) return; - var nextRank = await PromotionsService.GetNextRankRoleForUserAsync(user.UserId); - var currentGuild = DiscordHelper.GetUserGuild(); - - if (nextRank is null) - { - _nextRank = new NextRank("None", "#607d8b"); - } - else - { - _nextRank = new NextRank(nextRank.Name, currentGuild.Roles.First(x => x.Id == nextRank.Id).Color.ToString()); - } + using var client = HttpClientFactory.CreateClient("api"); + _nextRank = await client.GetFromJsonAsync($"api/campaigns/{user.UserId}/nextrank"); } private async Task CreateCampaign() { try { - await PromotionsService.CreateCampaignAsync(_selectedUser!.UserId, _promotionComment); + using var client = HttpClientFactory.CreateClient("api"); + + var response = await client.PutAsJsonAsync("api/campaigns/create", new PromotionCreationData(_selectedUser!.UserId, _promotionComment)); + + response.EnsureSuccessStatusCode(); + + NavigationManager.NavigateTo("/promotions"); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Configuration.PositionClass = Defaults.Classes.Position.BottomCenter; Snackbar.Add(ex.Message, Severity.Error); return; } + } + + private async Task> AutoCompleteAsync(string query) + { + using var client = HttpClientFactory.CreateClient("api"); + + var users = await client.GetFromJsonAsync($"api/autocomplete/users/{query}"); - NavigationManager.NavigateTo("/promotions"); + return users ?? []; } } diff --git a/src/Modix.Web/Controllers/CampaignController.cs b/src/Modix.Web/Controllers/CampaignController.cs index 06c5a0f4..eedde7f8 100644 --- a/src/Modix.Web/Controllers/CampaignController.cs +++ b/src/Modix.Web/Controllers/CampaignController.cs @@ -1,4 +1,5 @@ -using Discord.WebSocket; +using Discord; +using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; @@ -89,7 +90,6 @@ public async Task CreateCommentAsync(long campaignId, [From return new CampaignCommentData(newComment.Id, (PromotionSentiment)(int)newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); } - [HttpPatch("updatecomment")] public async Task UpdateCommentAsync([FromBody] CampaignCommentData campaignCommentData) { @@ -116,4 +116,22 @@ public async Task RejectCampaignAsync(long campaignId) { await _promotionsService.RejectCampaignAsync(campaignId); } + + [HttpGet("{subjectId}/nextrank")] + public async Task GetNextRankRoleForUserAsync(ulong subjectId) + { + var nextRank = await _promotionsService.GetNextRankRoleForUserAsync(subjectId); + + if (nextRank is null) + return new NextRank("None", "#607d8b"); + + var color = UserGuild.Roles.First(r => r.Id == nextRank.Id).Color; + return new NextRank(nextRank.Name, color.ToString()); + } + + [HttpPut("create")] + public async Task CreateAsync([FromBody] PromotionCreationData creationData) + { + await _promotionsService.CreateCampaignAsync(creationData.UserId, creationData.Comment); + } } From dc5c0ea21868007f4f6bd469046479e5418d04e6 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:15:25 +0200 Subject: [PATCH 26/46] Move Infractions page over to the wasm project --- .../Infractions/InfractionCreationData.cs | 3 + .../Models/Infractions/InfractionData.cs | 16 ++ .../Models/Infractions/InfractionType.cs | 24 +++ .../Models/Infractions/TableFilter.cs | 52 ++++++ .../Components/Infractions}/Infractions.razor | 174 +++++++++--------- .../Pages/Logs.razor | 18 +- .../Components/ConfirmationDialog.razor | 29 --- .../Controllers/InfractionsController.cs | 131 +++++++++++++ 8 files changed, 320 insertions(+), 127 deletions(-) create mode 100644 src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs create mode 100644 src/Modix.Web.Shared/Models/Infractions/InfractionData.cs create mode 100644 src/Modix.Web.Shared/Models/Infractions/InfractionType.cs create mode 100644 src/Modix.Web.Shared/Models/Infractions/TableFilter.cs rename src/{Modix.Web/Components => Modix.Web.Wasm/Components/Infractions}/Infractions.razor (70%) rename src/{Modix.Web => Modix.Web.Wasm}/Pages/Logs.razor (77%) delete mode 100644 src/Modix.Web/Components/ConfirmationDialog.razor create mode 100644 src/Modix.Web/Controllers/InfractionsController.cs diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs new file mode 100644 index 00000000..96949d02 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.Infractions; + +public record InfractionCreationData(InfractionType Type, ulong SubjectId, string Reason, TimeSpan? Duration); diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs new file mode 100644 index 00000000..82c93633 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs @@ -0,0 +1,16 @@ +namespace Modix.Web.Shared.Models.Infractions; + +public record InfractionData( + long Id, + ulong GuildId, + InfractionType Type, + string Reason, + TimeSpan? Duration, + string SubjectName, + string CreatedBy, + DateTimeOffset Created, + bool IsRescinded, + bool IsDeleted, + bool CanBeRescinded, + bool CanBeDeleted +); diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionType.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionType.cs new file mode 100644 index 00000000..52761fa2 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionType.cs @@ -0,0 +1,24 @@ +namespace Modix.Web.Shared.Models.Infractions; + +/// +/// Defines the possible types of infractions that can be recorded for a user. +/// +public enum InfractionType +{ + /// + /// Describes an "infraction" that is really just a note for communication between moderators. + /// + Notice, + /// + /// Describes an infraction where the user was issued a warning. + /// + Warning, + /// + /// Describes an infraction where the user was muted, preventing them from sending messages in text channels or speaking in voice channels. + /// + Mute, + /// + /// Describes an infraction where the user was banned from a guild. + /// + Ban, +} diff --git a/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs b/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs new file mode 100644 index 00000000..03804dbd --- /dev/null +++ b/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs @@ -0,0 +1,52 @@ +namespace Modix.Web.Shared.Models.Infractions; + +public class TableFilter +{ + public long? Id => long.TryParse(IdString, out var id) ? id : null; + public string? IdString { get; set; } + + public InfractionType? Type { get; set; } + public InfractionType[]? Types => Type is not null ? new[] { Type.Value } : null; + + private string? _subject; + public string? Subject + { + get => _subject; + set + { + if (string.IsNullOrWhiteSpace(value)) + { + _subject = null; + SubjectId = null; + } + else if (ulong.TryParse(value, out var subjectId)) + SubjectId = subjectId; + else + _subject = value; + } + } + + public ulong? SubjectId { get; private set; } + + private string? _creator; + public string? Creator + { + get => _creator; + set + { + if (string.IsNullOrWhiteSpace(value)) + { + _creator = null; + CreatedById = null; + } + else if (ulong.TryParse(value, out var createdById)) + CreatedById = createdById; + else + _creator = value; + } + } + + public ulong? CreatedById { get; private set; } + + public bool ShowDeleted { get; set; } +} diff --git a/src/Modix.Web/Components/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor similarity index 70% rename from src/Modix.Web/Components/Infractions.razor rename to src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index 31b192bd..81aff8cc 100644 --- a/src/Modix.Web/Components/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -1,11 +1,7 @@ -@using Modix.Data.Models; -@using Modix.Data.Models.Core; -@using Modix.Data.Models.Moderation; -@using Modix.Services.Moderation; +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; -@using Modix.Web.Models.Infractions; -@using Modix.Web.Services; @using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.Infractions @using Modix.Web.Wasm.Components @using MudBlazor; @using System.Security.Claims; @@ -20,7 +16,7 @@ @@ -34,16 +30,21 @@
- + @*TODO *@ + @* *@ + - + @* *@ + - + @* *@ + - + @* *@ + @@ -99,7 +100,7 @@ Id - + Type @@ -111,15 +112,15 @@ - Created On + Created On - Subject - + Subject + - Creator - + Creator + Reason @if (_showState) @@ -134,13 +135,24 @@ @infraction.Id @infraction.Type - @infraction.CreateAction.Created.ToString("MM/dd/yy, h:mm:ss tt") - @(GetUsername(infraction.Subject)) - @(GetUsername(infraction.CreateAction.CreatedBy)) + @infraction.Created.ToString("MM/dd/yy, h:mm:ss tt") + @infraction.SubjectName + @infraction.CreatedBy @infraction.Reason @if (_showState) { - @(infraction.RescindAction != null ? "Rescinded" : infraction.DeleteAction != null ? "Deleted" : "Active") + if(!infraction.IsRescinded && !infraction.IsDeleted) + { + Active + } + else if (infraction.IsDeleted) + { + Deleted + } + else + { + Rescinded + } } @if (_canDeleteInfractions || _canRescind) { @@ -150,7 +162,7 @@ { Delete } - @if (infraction.CanBeRescind) + @if (infraction.CanBeRescinded) { Rescind } @@ -173,22 +185,19 @@ public string? Id { get; set; } [Inject] - public ModerationService ModerationService { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required IDialogService DialogService { get; set; } [Inject] - public ISnackbar Snackbar { get; set; } = null!; + public required SessionState SessionState { get; set; } [Inject] - public IDialogService DialogService { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } - [Inject] - public SessionState SessionState { get; set; } = null!; - - [Inject] - public CookieService CookieService { get; set; } = null!; + // [Inject] + // public CookieService CookieService { get; set; } = null!; [CascadingParameter] private Task? AuthState { get; set; } @@ -227,8 +236,10 @@ return; var auth = await AuthState; - _canRescind = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationRescind)); - _canDeleteInfractions = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationDeleteInfraction)); + + // TODO: + // _canRescind = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationRescind)); + // _canDeleteInfractions = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationDeleteInfraction)); _tableFilter.Subject = Subject; _tableFilter.IdString = Id; @@ -237,13 +248,15 @@ private async Task ShowStateChanged(bool showState) { _showState = showState; - await CookieService.SetShowInfractionStateAsync(showState); + // TODO: + // await CookieService.SetShowInfractionStateAsync(showState); } private async Task ShowDeletedChanged(bool showDeleted) { await FilterChanged(() => _tableFilter.ShowDeleted = showDeleted); - await CookieService.SetShowDeletedInfractionsAsync(showDeleted); + //TODO: + // await CookieService.SetShowDeletedInfractionsAsync(showDeleted); } private void ToggleDialog() @@ -262,10 +275,10 @@ await RefreshTable(); } - private static string GetUsername(GuildUserBrief userBrief) - { - return $"{userBrief.Username}{(userBrief.Discriminator == "0000" ? "" : "#" + userBrief.Discriminator)}"; - } + // private static string GetUsername(GuildUserBrief userBrief) + // { + // return $"{userBrief.Username}{(userBrief.Discriminator == "0000" ? "" : "#" + userBrief.Discriminator)}"; + // } private async Task RescindInfraction(InfractionData infraction) { @@ -285,7 +298,11 @@ return; } - await ModerationService.RescindInfractionAsync(infraction.Id); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PostAsync($"api/infractions/rescind/{infraction.Id}", default); + + response.EnsureSuccessStatusCode(); + await RefreshTable(); } catch (Exception ex) @@ -312,7 +329,11 @@ return; } - await ModerationService.DeleteInfractionAsync(infraction.Id); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.DeleteAsync($"api/infractions/{infraction.Id}"); + + response.EnsureSuccessStatusCode(); + await RefreshTable(); } catch (Exception ex) @@ -334,18 +355,19 @@ try { - var currentUser = DiscordHelper.GetCurrentUser(); - await ModerationService.CreateInfractionAsync(currentUser!.Guild.Id, currentUser.Id, _infractionType, _selectedUser!.UserId, _infractionReason!, duration); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PostAsJsonAsync("api/infractions/create", new InfractionCreationData(_infractionType, _selectedUser!.UserId, _infractionReason!, duration)); + + response.EnsureSuccessStatusCode(); + + Snackbar.Add($"Added infraction for user {_selectedUser!.Name}", Severity.Success); } - catch (InvalidOperationException ex) + catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Error); return; } - - Snackbar.Add($"Added infraction for user {_selectedUser!.Name}", Severity.Success); - _selectedUser = null; _newInfractionMonths = null; _newInfractionDays = null; @@ -402,58 +424,26 @@ private async Task> LoadInfractions(TableState tableState) { - var currentUser = DiscordHelper.GetCurrentUser(); - var sortingCriteria = new[] - { - new SortingCriteria() - { - PropertyName = tableState.SortLabel ?? nameof(InfractionData.Id), - Direction = tableState.SortDirection == MudBlazor.SortDirection.Ascending - ? Data.Models.SortDirection.Ascending - : Data.Models.SortDirection.Descending, - } - }; + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PutAsJsonAsync("api/infractions", new { tableFilter = _tableFilter, tableState = tableState }); + response.EnsureSuccessStatusCode(); - var searchCriteria = new InfractionSearchCriteria - { - GuildId = currentUser!.Guild.Id, - Id = _tableFilter.Id, - Types = _tableFilter.Types, - Subject = _tableFilter.Subject, - SubjectId = _tableFilter.SubjectId, - Creator = _tableFilter.Creator, - CreatedById = _tableFilter.CreatedById, - IsDeleted = _tableFilter.ShowDeleted ? null : false - }; + var infractions = await response.Content.ReadFromJsonAsync(); - var pagingCriteria = new PagingCriteria + return new TableData { - FirstRecordIndex = tableState.Page * tableState.PageSize, - PageSize = tableState.PageSize, + Items = infractions, + TotalItems = infractions.Length }; + } - var result = await ModerationService.SearchInfractionsAsync( - searchCriteria, - sortingCriteria, - pagingCriteria); - - var outranksValues = new Dictionary(); - - foreach (var (guildId, subjectId) in result.Records - .Select(x => (guildId: x.GuildId, subjectId: x.Subject.Id)) - .Distinct()) - { - outranksValues[subjectId] - = await ModerationService.DoesModeratorOutrankUserAsync(guildId, currentUser.Id, subjectId); - } + private async Task> AutoCompleteAsync(string query) + { + using var client = HttpClientFactory.CreateClient("api"); - var mapped = result.Records.Select(x => InfractionData.FromInfractionSummary(x, outranksValues)).ToArray(); + var users = await client.GetFromJsonAsync($"api/autocomplete/users/{query}"); - return new TableData - { - Items = mapped, - TotalItems = mapped.Length - }; + return users ?? []; } } diff --git a/src/Modix.Web/Pages/Logs.razor b/src/Modix.Web.Wasm/Pages/Logs.razor similarity index 77% rename from src/Modix.Web/Pages/Logs.razor rename to src/Modix.Web.Wasm/Pages/Logs.razor index 65f6cf74..1193be3f 100644 --- a/src/Modix.Web/Pages/Logs.razor +++ b/src/Modix.Web.Wasm/Pages/Logs.razor @@ -1,9 +1,14 @@ @page "/logs/{SubPage}" @page "/logs" @page "/infractions" -@attribute [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] -@using Modix.Data.Models.Core; -@using Modix.Web.Components + +@* TODO *@ +@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] *@ +@attribute [Authorize(Roles = "ModerationRead")] + +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Modix.Web.Wasm.Components.Infractions @using MudBlazor Modix - Logs @@ -24,15 +29,16 @@
@if (SubPage == "infractions") { - + @* *@ + } else if (SubPage == "deletedMessages") { - +@* - + *@ }
diff --git a/src/Modix.Web/Components/ConfirmationDialog.razor b/src/Modix.Web/Components/ConfirmationDialog.razor deleted file mode 100644 index 4fd7aba0..00000000 --- a/src/Modix.Web/Components/ConfirmationDialog.razor +++ /dev/null @@ -1,29 +0,0 @@ -@using Modix.Data.Models.Promotions; -@using MudBlazor - - - - Confirmation - - - @Content - - - Confirm - - Cancel - - - - - -@code { - [CascadingParameter] - MudDialogInstance? MudDialog { get; set; } - - [Parameter] - public required string Content { get; set; } - - void Submit() => MudDialog?.Close(); - void Cancel() => MudDialog?.Cancel(); -} diff --git a/src/Modix.Web/Controllers/InfractionsController.cs b/src/Modix.Web/Controllers/InfractionsController.cs new file mode 100644 index 00000000..3435c8cf --- /dev/null +++ b/src/Modix.Web/Controllers/InfractionsController.cs @@ -0,0 +1,131 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models; +using Modix.Data.Models.Core; +using Modix.Data.Models.Moderation; +using Modix.Services.Moderation; +using Modix.Web.Shared.Models.Infractions; +using MudBlazor; + +namespace Modix.Web.Controllers; + +[Route("~/api/infractions")] +[ApiController] +[Authorize] +public class InfractionsController : ModixController +{ + private readonly ModerationService _moderationService; + + public InfractionsController(ModerationService moderationService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _moderationService = moderationService; + } + + // TODO: Refactor this + public class Intermediate + { + public TableFilter tableFilter { get; set; } + public TableState tableState { get; set; } + } + + [HttpPut] + [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] + public async Task GetInfractionsAsync(Intermediate inter) + { + var tableState = inter.tableState; + var tableFilter = inter.tableFilter; + + var sortingCriteria = new[] + { + new SortingCriteria + { + PropertyName = tableState.SortLabel ?? nameof(InfractionData.Id), + Direction = tableState.SortDirection == MudBlazor.SortDirection.Ascending + ? Data.Models.SortDirection.Ascending + : Data.Models.SortDirection.Descending, + } + }; + + var searchCriteria = new InfractionSearchCriteria + { + GuildId = UserGuild.Id, + Id = tableFilter.Id, + Types = tableFilter.Types?.Cast().ToArray(), + Subject = tableFilter.Subject, + SubjectId = tableFilter.SubjectId, + Creator = tableFilter.Creator, + CreatedById = tableFilter.CreatedById, + IsDeleted = tableFilter.ShowDeleted ? null : false + }; + + var pagingCriteria = new PagingCriteria + { + FirstRecordIndex = tableState.Page * tableState.PageSize, + PageSize = tableState.PageSize, + }; + + var infractions = await _moderationService.SearchInfractionsAsync(searchCriteria, sortingCriteria, pagingCriteria); + var outranksValues = new Dictionary(); + + foreach (var (guildId, subjectId) in infractions.Records.Select(x => (guildId: x.GuildId, subjectId: x.Subject.Id))) + { + outranksValues[subjectId] = await _moderationService.DoesModeratorOutrankUserAsync(guildId, SocketUser.Id, subjectId); + } + + return infractions.Records + .Select(x => new InfractionData( + x.Id, + x.GuildId, + // TODO: + (Shared.Models.Infractions.InfractionType)(int)x.Type, + x.Reason, + x.Duration, + x.Subject.Username, + x.CreateAction.CreatedBy.Username, + x.CreateAction.Created, + + x.RescindAction is not null, + x.DeleteAction is not null, + + x.RescindAction is null + && x.DeleteAction is null + && (x.Type == Data.Models.Moderation.InfractionType.Mute || x.Type == Data.Models.Moderation.InfractionType.Ban) + && outranksValues[x.Subject.Id], + + x.DeleteAction is null + && outranksValues[x.Subject.Id] + )) + .ToArray(); + } + + [Authorize(Roles = nameof(AuthorizationClaim.ModerationRescind))] + [HttpPost("rescind/{infractionId}")] + public async Task RescindInfractionAsync(long infractionId) + { + await _moderationService.RescindInfractionAsync(infractionId); + } + + [Authorize(Roles = nameof(AuthorizationClaim.ModerationDeleteInfraction))] + [HttpDelete("{infractionId}")] + public async Task DeleteInfractionAsync(long infractionId) + { + await _moderationService.DeleteInfractionAsync(infractionId); + } + + [Authorize(Roles = $"{nameof(AuthorizationClaim.ModerationNote)},{nameof(AuthorizationClaim.ModerationWarn)},{nameof(AuthorizationClaim.ModerationBan)},{nameof(AuthorizationClaim.ModerationMute)}")] + [HttpPost("create")] + public async Task CreateInfractionAsync(Shared.Models.Infractions.InfractionCreationData creationData) + { + await _moderationService.CreateInfractionAsync( + UserGuild.Id, + SocketUser.Id, + // TODO: + (Data.Models.Moderation.InfractionType)(int)creationData.Type, + creationData.SubjectId, + creationData.Reason, + creationData.Duration); + } +} From 853a9fd7e31d63bd65ac3f0b9bae01aebb1b0139 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:45:30 +0200 Subject: [PATCH 27/46] Move DeletedMessages page over to the wasm project --- .../DeletedMessageBatchInformation.cs | 12 ++ .../DeletedMessageInformation.cs | 3 + .../Models/DeletedMessages/TableFilter.cs | 2 +- .../Infractions}/DeletedMessages.razor | 127 ++++----------- src/Modix.Web.Wasm/Pages/Logs.razor | 5 +- .../Controllers/DeletedMessagesController.cs | 146 ++++++++++++++++++ .../DeletedMessageInformation.cs | 27 ---- .../Models/Infractions/InfractionData.cs | 43 ------ .../Models/Infractions/TableFilter.cs | 62 -------- 9 files changed, 194 insertions(+), 233 deletions(-) create mode 100644 src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageBatchInformation.cs create mode 100644 src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageInformation.cs rename src/{Modix.Web => Modix.Web.Shared}/Models/DeletedMessages/TableFilter.cs (97%) rename src/{Modix.Web/Components => Modix.Web.Wasm/Components/Infractions}/DeletedMessages.razor (58%) create mode 100644 src/Modix.Web/Controllers/DeletedMessagesController.cs delete mode 100644 src/Modix.Web/Models/DeletedMessages/DeletedMessageInformation.cs delete mode 100644 src/Modix.Web/Models/Infractions/InfractionData.cs delete mode 100644 src/Modix.Web/Models/Infractions/TableFilter.cs diff --git a/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageBatchInformation.cs b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageBatchInformation.cs new file mode 100644 index 00000000..eeaf38f4 --- /dev/null +++ b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageBatchInformation.cs @@ -0,0 +1,12 @@ +namespace Modix.Web.Shared.Models.DeletedMessages; + +public record DeletedMessageBatchInformation +( + string ChannelName, + string AuthorUsername, + DateTimeOffset Created, + string CreatedByUsername, + string Content, + string Reason, + long? BatchId +); diff --git a/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageInformation.cs b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageInformation.cs new file mode 100644 index 00000000..e194d77e --- /dev/null +++ b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessageInformation.cs @@ -0,0 +1,3 @@ +namespace Modix.Web.Shared.Models.DeletedMessages; + +public record DeletedMessageInformation(ulong MessageId, DateTimeOffset? SentTime, string? Url, string Username, string Content); diff --git a/src/Modix.Web/Models/DeletedMessages/TableFilter.cs b/src/Modix.Web.Shared/Models/DeletedMessages/TableFilter.cs similarity index 97% rename from src/Modix.Web/Models/DeletedMessages/TableFilter.cs rename to src/Modix.Web.Shared/Models/DeletedMessages/TableFilter.cs index 48157fbf..f08f0417 100644 --- a/src/Modix.Web/Models/DeletedMessages/TableFilter.cs +++ b/src/Modix.Web.Shared/Models/DeletedMessages/TableFilter.cs @@ -1,4 +1,4 @@ -namespace Modix.Web.Models.DeletedMessages; +namespace Modix.Web.Shared.Models.DeletedMessages; public class TableFilter { diff --git a/src/Modix.Web/Components/DeletedMessages.razor b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor similarity index 58% rename from src/Modix.Web/Components/DeletedMessages.razor rename to src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor index d9055cd9..b6367f9c 100644 --- a/src/Modix.Web/Components/DeletedMessages.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor @@ -1,12 +1,5 @@ -@using Discord.WebSocket; -@using Discord; -@using Modix.Data.Models.Moderation; -@using Modix.Data.Models; -@using Modix.Services.Moderation; -@using Modix.Web.Models.DeletedMessages; -@using Modix.Web.Services; +@using Modix.Web.Shared.Models.DeletedMessages @using MudBlazor -@using Modix.Services.Utilities Modix - Deletions @@ -79,39 +72,39 @@ - Channel + Channel - Author + Author - Deleted On + Deleted On - Deleted By + Deleted By - Content + Content - Reason + Reason - Batch ID + Batch ID Actions - #@deletedMessage.Channel.Name - @deletedMessage.Author.GetFullUsername() + #@deletedMessage.ChannelName + @deletedMessage.AuthorUsername @deletedMessage.Created - @deletedMessage.CreatedBy.GetFullUsername() + @deletedMessage.CreatedByUsername @@ -137,15 +130,12 @@ @code { [Inject] - public ModerationService ModerationService { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } - [Inject] - public ISnackbar Snackbar { get; set; } = null!; - - private MudTable? TableRef; + private MudTable? TableRef; private Dictionary> DeletedMessagesContext { get; } = new Dictionary>(); private bool _deletedMessagesContextDialogVisible; private long _currentContext; @@ -179,46 +169,19 @@ private void CloseDialog() => _deletedMessagesContextDialogVisible = false; - private async Task> LoadDeletedMessages(TableState tableState) + private async Task> LoadDeletedMessages(TableState tableState) { - var currentGuild = DiscordHelper.GetUserGuild(); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PutAsJsonAsync("api/deletedmessages", new { tableFilter = _tableFilter, tableState = tableState }); - var searchCriteria = new DeletedMessageSearchCriteria - { - GuildId = currentGuild.Id, - Channel = _tableFilter.Channel, - ChannelId = _tableFilter.ChannelId, - Author = _tableFilter.Author, - AuthorId = _tableFilter.AuthorId, - CreatedBy = _tableFilter.CreatedBy, - CreatedById = _tableFilter.CreatedById, - Content = _tableFilter.Content, - Reason = _tableFilter.Reason, - BatchId = _tableFilter.BatchId - }; + response.EnsureSuccessStatusCode(); - var result = await ModerationService.SearchDeletedMessagesAsync(searchCriteria, - new[] - { - new SortingCriteria - { - PropertyName = tableState.SortLabel ?? nameof(DeletedMessageSummary.Created), - Direction = tableState.SortDirection == MudBlazor.SortDirection.Ascending - ? Data.Models.SortDirection.Ascending - : Data.Models.SortDirection.Descending - } - }, - new PagingCriteria - { - FirstRecordIndex = tableState.Page * tableState.PageSize, - PageSize = tableState.PageSize, - } - ); + var result = await response.Content.ReadFromJsonAsync(); - return new TableData + return new TableData { - TotalItems = (int)result.FilteredRecordCount, - Items = result.Records + TotalItems = result.Length, + Items = result }; } @@ -229,53 +192,21 @@ if (DeletedMessagesContext.ContainsKey(batchId)) return; - var deletedMessages = await ModerationService.SearchDeletedMessagesAsync( - new DeletedMessageSearchCriteria - { - BatchId = batchId - }, - new SortingCriteria[] - { - //Sort ascending, so the earliest message is first - new SortingCriteria { PropertyName = nameof(DeletedMessageSummary.MessageId), Direction = Data.Models.SortDirection.Ascending } - }, - new PagingCriteria() - ); - - var firstMessage = deletedMessages.Records.FirstOrDefault(); + using var client = HttpClientFactory.CreateClient("api"); + using var response = await client.GetAsync($"api/deletedmessages/{batchId}"); - if (firstMessage is null) + if(!response.IsSuccessStatusCode) { CloseDialog(); - Snackbar.Add($"Couldn't find messages for batch id {batchId}", Severity.Error); - return; - } - var currentUser = DiscordHelper.GetCurrentUser(); - var batchChannelId = deletedMessages.Records.First().Channel.Id; - if (currentUser!.Guild.GetChannel(batchChannelId) is not ISocketMessageChannel foundChannel) - { - CloseDialog(); - Snackbar.Add($"Couldn't recreate context - text channel with id {batchChannelId} not found", Severity.Error); - return; - } + var errorMessage = await response.Content.ReadAsStringAsync(); + Snackbar.Add(errorMessage, Severity.Error); - if (currentUser.GetPermissions(foundChannel as IGuildChannel).ReadMessageHistory == false) - { - CloseDialog(); - Snackbar.Add($"You don't have read permissions for the channel this batch was deleted in (#{foundChannel.Name})", Severity.Error); return; } - var beforeMessages = await foundChannel.GetMessagesAsync(firstMessage.MessageId, Discord.Direction.Before, 25).FlattenAsync(); - var afterMessages = await foundChannel.GetMessagesAsync(firstMessage.MessageId, Discord.Direction.After, 25 + (int)deletedMessages.FilteredRecordCount).FlattenAsync(); - - var allMessages = new List(); - allMessages.AddRange(deletedMessages.Records.Select(d => new DeletedMessageInformation(d.MessageId, null, null, d.Author.GetFullUsername(), d.Content))); - allMessages.AddRange(beforeMessages.Select(d => DeletedMessageInformation.FromIMessage(d))); - allMessages.AddRange(afterMessages.Select(d => DeletedMessageInformation.FromIMessage(d))); - - DeletedMessagesContext[batchId] = allMessages.OrderBy(d => d.MessageId).ToList(); + var allMessages = await response.Content.ReadFromJsonAsync(); + DeletedMessagesContext[batchId] = allMessages!.OrderBy(d => d.MessageId).ToList(); } } diff --git a/src/Modix.Web.Wasm/Pages/Logs.razor b/src/Modix.Web.Wasm/Pages/Logs.razor index 1193be3f..f74cfdfc 100644 --- a/src/Modix.Web.Wasm/Pages/Logs.razor +++ b/src/Modix.Web.Wasm/Pages/Logs.razor @@ -36,9 +36,10 @@ } else if (SubPage == "deletedMessages") { -@* + @* TODO *@ + - *@ + }
diff --git a/src/Modix.Web/Controllers/DeletedMessagesController.cs b/src/Modix.Web/Controllers/DeletedMessagesController.cs new file mode 100644 index 00000000..92b0d82c --- /dev/null +++ b/src/Modix.Web/Controllers/DeletedMessagesController.cs @@ -0,0 +1,146 @@ +using Discord; +using Discord.WebSocket; +using Humanizer.Bytes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models; +using Modix.Data.Models.Core; +using Modix.Data.Models.Moderation; +using Modix.Services.Moderation; +using Modix.Services.Utilities; +using Modix.Web.Shared.Models.DeletedMessages; +using MudBlazor; + +namespace Modix.Web.Controllers; + +[Route("~/api/deletedmessages")] +[ApiController] +[Authorize(Roles = nameof(AuthorizationClaim.LogViewDeletedMessages))] +public class DeletedMessagesController : ModixController +{ + private readonly ModerationService _moderationService; + + public DeletedMessagesController(ModerationService moderationService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _moderationService = moderationService; + } + + // TODO: Refactor this + public class Intermediate + { + public TableFilter tableFilter { get; set; } + public TableState tableState { get; set; } + } + + + [HttpPut] + public async Task GetDeletedMessagesBatchAsync(Intermediate inter) + { + var tableState = inter.tableState; + var tableFilter = inter.tableFilter; + + var searchCriteria = new DeletedMessageSearchCriteria + { + GuildId = UserGuild.Id, + Channel = tableFilter.Channel, + ChannelId = tableFilter.ChannelId, + Author = tableFilter.Author, + AuthorId = tableFilter.AuthorId, + CreatedBy = tableFilter.CreatedBy, + CreatedById = tableFilter.CreatedById, + Content = tableFilter.Content, + Reason = tableFilter.Reason, + BatchId = tableFilter.BatchId + }; + + var sortingCriteria = new SortingCriteria + { + PropertyName = tableState.SortLabel ?? nameof(DeletedMessageSummary.Created), + Direction = tableState.SortDirection == MudBlazor.SortDirection.Ascending + ? Data.Models.SortDirection.Ascending + : Data.Models.SortDirection.Descending + }; + + var pagingCriteria = new PagingCriteria + { + FirstRecordIndex = tableState.Page * tableState.PageSize, + PageSize = tableState.PageSize, + }; + + + var deletedMessages = await _moderationService.SearchDeletedMessagesAsync(searchCriteria, [sortingCriteria], pagingCriteria); + + return deletedMessages.Records + .Select(x => new DeletedMessageBatchInformation( + x.Channel.Name, + x.Author.GetFullUsername(), + x.Created, + x.CreatedBy.GetFullUsername(), + x.Content, + x.Reason, + x.BatchId)) + .ToArray(); + } + + [HttpGet("{batchId}")] + public async Task GetDeletedMessageContext(long batchId) + { + + var searchCriteria = new DeletedMessageSearchCriteria + { + BatchId = batchId + }; + + var sortingCriteria = new SortingCriteria + { + PropertyName = nameof(DeletedMessageSummary.MessageId), + + //Sort ascending, so the earliest message is first + Direction = Data.Models.SortDirection.Ascending + }; + + var deletedMessages = await _moderationService.SearchDeletedMessagesAsync(searchCriteria, [sortingCriteria], new()); + var firstMessage = deletedMessages.Records.FirstOrDefault(); + + if (firstMessage is null) + return NotFound($"Couldn't find messages for batch id {batchId}"); + + var batchChannelId = firstMessage.Channel.Id; + if (SocketUser.Guild.GetChannel(batchChannelId) is not ISocketMessageChannel foundChannel) + return NotFound($"Couldn't recreate context - text channel with id {batchChannelId} not found"); + + if (!SocketUser.GetPermissions(foundChannel as IGuildChannel).ReadMessageHistory) + return Unauthorized($"You don't have read permissions for the channel this batch was deleted in (#{foundChannel.Name})"); + + var beforeMessages = await foundChannel.GetMessagesAsync(firstMessage.MessageId, Discord.Direction.Before, 25).FlattenAsync(); + var afterMessages = await foundChannel.GetMessagesAsync(firstMessage.MessageId, Discord.Direction.After, 25 + (int)deletedMessages.FilteredRecordCount).FlattenAsync(); + + var allMessages = new List(); + allMessages.AddRange(deletedMessages.Records.Select(d => new DeletedMessageInformation(d.MessageId, null, null, d.Author.GetFullUsername(), d.Content))); + allMessages.AddRange(beforeMessages.Select(FromIMessage)); + allMessages.AddRange(afterMessages.Select(FromIMessage)); + + return Ok(allMessages); + } + + private static DeletedMessageInformation FromIMessage(IMessage message) + { + var content = message.Content; + + if (string.IsNullOrWhiteSpace(content)) + { + if (message.Embeds.Count > 0) + { + content = $"Embed: {message.Embeds.First().Title}: {message.Embeds.First().Description}"; + } + else if (message.Attachments.Count > 0) + { + content = $"Attachment: {message.Attachments.First().Filename} {ByteSize.FromBytes(message.Attachments.First().Size)}"; + } + } + + return new DeletedMessageInformation(message.Id, message.CreatedAt, message.GetJumpUrl(), message.Author.GetDisplayName(), content); + } +} diff --git a/src/Modix.Web/Models/DeletedMessages/DeletedMessageInformation.cs b/src/Modix.Web/Models/DeletedMessages/DeletedMessageInformation.cs deleted file mode 100644 index e87f73a0..00000000 --- a/src/Modix.Web/Models/DeletedMessages/DeletedMessageInformation.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Discord; -using Humanizer.Bytes; -using Modix.Services.Utilities; - -namespace Modix.Web.Models.DeletedMessages; - -public record DeletedMessageInformation(ulong MessageId, DateTimeOffset? SentTime, string? Url, string Username, string Content) -{ - public static DeletedMessageInformation FromIMessage(IMessage message) - { - var content = message.Content; - - if (string.IsNullOrWhiteSpace(content)) - { - if (message.Embeds.Count > 0) - { - content = $"Embed: {message.Embeds.First().Title}: {message.Embeds.First().Description}"; - } - else if (message.Attachments.Count > 0) - { - content = $"Attachment: {message.Attachments.First().Filename} {ByteSize.FromBytes(message.Attachments.First().Size)}"; - } - } - - return new DeletedMessageInformation(message.Id, message.CreatedAt, message.GetJumpUrl(), message.Author.GetDisplayName(), content); - } -} diff --git a/src/Modix.Web/Models/Infractions/InfractionData.cs b/src/Modix.Web/Models/Infractions/InfractionData.cs deleted file mode 100644 index c1491ab7..00000000 --- a/src/Modix.Web/Models/Infractions/InfractionData.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Modix.Data.Models.Core; -using Modix.Data.Models.Moderation; - -namespace Modix.Web.Models.Infractions; - -public record InfractionData( - long Id, - ulong GuildId, - InfractionType Type, - string Reason, - TimeSpan? Duration, - GuildUserBrief Subject, - ModerationActionBrief CreateAction, - ModerationActionBrief? RescindAction, - ModerationActionBrief? DeleteAction, - bool CanBeRescind, - bool CanBeDeleted -) -{ - public static InfractionData FromInfractionSummary(InfractionSummary summary, Dictionary outranksValues) - { - return new InfractionData( - summary.Id, - summary.GuildId, - summary.Type, - summary.Reason, - summary.Duration, - summary.Subject, - - summary.CreateAction, - summary.RescindAction, - summary.DeleteAction, - - summary.RescindAction is null - && summary.DeleteAction is null - && (summary.Type == InfractionType.Mute || summary.Type == InfractionType.Ban) - && outranksValues[summary.Subject.Id], - - summary.DeleteAction is null - && outranksValues[summary.Subject.Id] - ); - } -} diff --git a/src/Modix.Web/Models/Infractions/TableFilter.cs b/src/Modix.Web/Models/Infractions/TableFilter.cs deleted file mode 100644 index fc7e112a..00000000 --- a/src/Modix.Web/Models/Infractions/TableFilter.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Modix.Data.Models.Moderation; - -namespace Modix.Web.Models.Infractions; - -public class TableFilter -{ - public long? Id => long.TryParse(IdString, out var id) ? id : null; - public string? IdString { get; set; } - - public InfractionType? Type { get; set; } - public InfractionType[]? Types => Type is not null ? new[] { Type.Value } : null; - - private string? _subject; - public string? Subject - { - get => _subject; - set - { - if (string.IsNullOrWhiteSpace(value)) - { - _subject = null; - SubjectId = null; - } - else if (ulong.TryParse(value, out var subjectId)) - { - SubjectId = subjectId; - } - else - { - _subject = value; - } - } - } - - public ulong? SubjectId { get; private set; } - - private string? _creator; - public string? Creator - { - get => _creator; - set - { - if (string.IsNullOrWhiteSpace(value)) - { - _creator = null; - CreatedById = null; - } - else if (ulong.TryParse(value, out var createdById)) - { - CreatedById = createdById; - } - else - { - _creator = value; - } - } - } - - public ulong? CreatedById { get; private set; } - - public bool ShowDeleted { get; set; } -} From fd8ef15c45f12f8085d65ebcb2b9ea4db9025dec Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:28:26 +0200 Subject: [PATCH 28/46] Implement guild option endpoint to fix guild selection --- .../Models/CookieConstants.cs | 0 .../Models/GuildOption.cs | 2 +- .../Services/ICookieService.cs | 10 ++++ src/Modix.Web.Wasm/Components/MiniUser.razor | 54 +++++++++++-------- src/Modix.Web.Wasm/Program.cs | 6 ++- src/Modix.Web.Wasm/Routes.razor | 30 +++++++---- src/Modix.Web.Wasm/Services/CookieService.cs | 41 ++++++++++++++ src/Modix.Web/Controllers/GuildController.cs | 28 ++++++++++ src/Modix.Web/Security/ClaimsMiddleware.cs | 3 ++ src/Modix.Web/Services/CookieService.cs | 3 +- src/Modix.Web/Services/DiscordHelper.cs | 36 ------------- src/Modix.Web/Setup.cs | 3 +- 12 files changed, 145 insertions(+), 71 deletions(-) rename src/{Modix.Web => Modix.Web.Shared}/Models/CookieConstants.cs (100%) rename src/{Modix.Web => Modix.Web.Shared}/Models/GuildOption.cs (63%) create mode 100644 src/Modix.Web.Shared/Services/ICookieService.cs create mode 100644 src/Modix.Web.Wasm/Services/CookieService.cs create mode 100644 src/Modix.Web/Controllers/GuildController.cs diff --git a/src/Modix.Web/Models/CookieConstants.cs b/src/Modix.Web.Shared/Models/CookieConstants.cs similarity index 100% rename from src/Modix.Web/Models/CookieConstants.cs rename to src/Modix.Web.Shared/Models/CookieConstants.cs diff --git a/src/Modix.Web/Models/GuildOption.cs b/src/Modix.Web.Shared/Models/GuildOption.cs similarity index 63% rename from src/Modix.Web/Models/GuildOption.cs rename to src/Modix.Web.Shared/Models/GuildOption.cs index 7307007f..fe039b07 100644 --- a/src/Modix.Web/Models/GuildOption.cs +++ b/src/Modix.Web.Shared/Models/GuildOption.cs @@ -1,3 +1,3 @@ -namespace Modix.Web.Models; +namespace Modix.Web.Shared.Models; public record GuildOption(ulong Id, string Name, string IconUrl); diff --git a/src/Modix.Web.Shared/Services/ICookieService.cs b/src/Modix.Web.Shared/Services/ICookieService.cs new file mode 100644 index 00000000..c5452785 --- /dev/null +++ b/src/Modix.Web.Shared/Services/ICookieService.cs @@ -0,0 +1,10 @@ +namespace Modix.Web.Shared.Services; + +public interface ICookieService +{ + Task SetSelectedGuildAsync(ulong guildId); + Task SetShowDeletedInfractionsAsync(bool showDeleted); + Task SetShowInfractionStateAsync(bool showInfractionState); + Task SetShowInactivePromotionsAsync(bool showInactivePromotions); + Task SetUseDarkModeAsync(bool useDarkMode); +} diff --git a/src/Modix.Web.Wasm/Components/MiniUser.razor b/src/Modix.Web.Wasm/Components/MiniUser.razor index 1e780b86..f6734622 100644 --- a/src/Modix.Web.Wasm/Components/MiniUser.razor +++ b/src/Modix.Web.Wasm/Components/MiniUser.razor @@ -1,6 +1,7 @@ -@using Microsoft.AspNetCore.Components.Authorization -@using Modix.Web.Models; +@using Modix.Web.Models @using Modix.Web.Shared.Models +@using Microsoft.AspNetCore.Components.Authorization +@using Modix.Web.Shared.Services @using MudBlazor @using System.Security.Claims @@ -24,19 +25,18 @@
- @* *@ - +
-@* @foreach (var guildOption in GuildOptions) + @foreach (var guildOption in GuildOptions) { @guildOption.Name - } *@ + }
@@ -49,23 +49,26 @@ @code { - [CascadingParameter] - public Task? AuthenticationState { get; set; } = null!; - - // [Inject] - // public DiscordHelper DiscordHelper { get; set; } = null!; - private string? AvatarUrl { get; set; } private string? Username { get; set; } - // private IEnumerable GuildOptions { get; set; } = Array.Empty(); - // private SocketGuild? SelectedGuild { get; set; } + private IEnumerable GuildOptions { get; set; } = Array.Empty(); + private GuildOption? SelectedGuild { get; set; } + + [CascadingParameter] + public required Task AuthenticationState { get; set; } + + [Inject] + public required ICookieService CookieService { get; set; } + + [CascadingParameter] + public required SessionState SessionState { get; set; } - // [Inject] - // public CookieService CookieService { get; set; } = null!; + [Inject] + public required IHttpClientFactory HttpClientFactory { get; set; } [Inject] - public NavigationManager NavigationManager { get; set; } = null!; + public required NavigationManager NavigationManager { get; set; } protected override async Task OnInitializedAsync() { @@ -76,20 +79,27 @@ if (authState.User.Identity?.IsAuthenticated is not true) return; - // var user = DiscordHelper.GetCurrentUser(); - var avatarHash = authState.User.FindFirst(x => x.Type == nameof(DiscordUser.AvatarHash))?.Value; var userId = ulong.Parse(authState.User.FindFirst(d => d.Type == ClaimTypes.NameIdentifier)?.Value); AvatarUrl = $"https://cdn.discordapp.com/avatars/{userId}/{avatarHash}.png"; Username = authState.User.Identity.Name; - // GuildOptions = DiscordHelper.GetGuildOptions(); - // SelectedGuild = user.Guild; + // TODO: Figure out why HttpClient does not have a base address in OnInitializedAsync() + + using var client = HttpClientFactory.CreateClient("api"); + + if (client.BaseAddress is null) + return; + + var result = await client.GetFromJsonAsync("api/guild/guildoptions"); + + GuildOptions = result ?? []; + SelectedGuild = GuildOptions.FirstOrDefault(x => x.Id == SessionState.SelectedGuild); } private async Task SelectGuild(ulong guildId) { - // await CookieService.SetSelectedGuildAsync(guildId); + await CookieService.SetSelectedGuildAsync(guildId); NavigationManager.NavigateTo(NavigationManager.Uri, true); } } diff --git a/src/Modix.Web.Wasm/Program.cs b/src/Modix.Web.Wasm/Program.cs index 5b671634..5e02e05e 100644 --- a/src/Modix.Web.Wasm/Program.cs +++ b/src/Modix.Web.Wasm/Program.cs @@ -1,7 +1,9 @@ using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Modix.Web.Models; +using Modix.Web.Shared.Services; using Modix.Web.Wasm.Security; +using Modix.Web.Wasm.Services; using MudBlazor; using MudBlazor.Services; @@ -13,7 +15,9 @@ public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.Services.AddScoped(); + builder.Services + .AddScoped() + .AddScoped(); builder.Services.AddCascadingValue(sp => sp.GetRequiredService()); diff --git a/src/Modix.Web.Wasm/Routes.razor b/src/Modix.Web.Wasm/Routes.razor index d1fd8cee..afa832b2 100644 --- a/src/Modix.Web.Wasm/Routes.razor +++ b/src/Modix.Web.Wasm/Routes.razor @@ -45,14 +45,17 @@ [CascadingParameter] public required SessionState SessionState { get; set; } - // [Inject] - // public DiscordHelper DiscordHelper { get; set; } = null!; - [CascadingParameter] public required Task AuthenticationState { get; set; } - // [Inject] - // public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; + [Inject] + public required PersistentComponentState State { get; set; } + + + protected override void OnParametersSet() + { + State.RegisterOnPersisting(OnPersisting, RenderMode.InteractiveAuto); + } protected override async Task OnInitializedAsync() { @@ -60,10 +63,17 @@ if (authState.User.Identity?.IsAuthenticated is not true) return; + if (State.TryTakeFromJson(nameof(SessionState), out var sessionState) && sessionState is not null) + { + SessionState = sessionState; + return; + } + var userId = authState.User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; + var currentGuild = authState.User.FindFirst(ClaimTypes.PostalCode)?.Value; _ = ulong.TryParse(userId, out var userSnowflake); - _ = ulong.TryParse(SelectedGuild, out var selectedGuildId); + _ = ulong.TryParse(currentGuild ?? SelectedGuild, out var selectedGuildId); _ = bool.TryParse(ShowInfractionState, out var showInfractionState); _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); @@ -75,9 +85,11 @@ SessionState.ShowDeletedInfractions = showDeletedInfractions; SessionState.ShowInactivePromotions = showInactivePromotions; SessionState.UseDarkMode = useDarkMode; + } - // var currentUser = DiscordHelper.GetCurrentUser(); - - // await AuthorizationService.OnAuthenticatedAsync(currentUser!.Id, currentUser.Guild.Id, currentUser.Roles.Select(x => x.Id).ToList()); + private Task OnPersisting() + { + State.PersistAsJson(nameof(SessionState), SessionState); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/Modix.Web.Wasm/Services/CookieService.cs b/src/Modix.Web.Wasm/Services/CookieService.cs new file mode 100644 index 00000000..2a1dd1e7 --- /dev/null +++ b/src/Modix.Web.Wasm/Services/CookieService.cs @@ -0,0 +1,41 @@ +using Microsoft.JSInterop; +using Modix.Web.Models; +using Modix.Web.Shared.Services; + +namespace Modix.Web.Wasm.Services; + +public class CookieService(IJSRuntime jsRuntime, SessionState sessionState) : ICookieService +{ + public async Task SetSelectedGuildAsync(ulong guildId) + { + await SetCookieAsync(CookieConstants.SelectedGuild, guildId); + sessionState.SelectedGuild = guildId; + } + + public async Task SetShowDeletedInfractionsAsync(bool showDeleted) + { + await SetCookieAsync(CookieConstants.ShowDeletedInfractions, showDeleted); + sessionState.ShowDeletedInfractions = showDeleted; + } + + public async Task SetShowInfractionStateAsync(bool showInfractionState) + { + await SetCookieAsync(CookieConstants.ShowInfractionState, showInfractionState); + sessionState.ShowInfractionState = showInfractionState; + } + + public async Task SetShowInactivePromotionsAsync(bool showInactivePromotions) + { + await SetCookieAsync(CookieConstants.ShowInactivePromotions, showInactivePromotions); + sessionState.ShowInactivePromotions = showInactivePromotions; + } + + public async Task SetUseDarkModeAsync(bool useDarkMode) + { + await SetCookieAsync(CookieConstants.UseDarkMode, useDarkMode); + sessionState.UseDarkMode = useDarkMode; + } + + private async Task SetCookieAsync(string key, T value) + => await jsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{key}={value}; path=/\";"); +} diff --git a/src/Modix.Web/Controllers/GuildController.cs b/src/Modix.Web/Controllers/GuildController.cs new file mode 100644 index 00000000..017d44e2 --- /dev/null +++ b/src/Modix.Web/Controllers/GuildController.cs @@ -0,0 +1,28 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Web.Shared.Models; + +namespace Modix.Web.Controllers; + +[Route("~/api/guild")] +[ApiController] +[Authorize] +public class GuildController : ModixController +{ + public GuildController(DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + } + + [HttpGet("guildoptions")] + public GuildOption[] GuildOptions() + { + return DiscordSocketClient + .Guilds + .Where(d => d.GetUser(SocketUser?.Id ?? 0) != null) + .Select(d => new GuildOption(d.Id, d.Name, d.IconUrl)) + .ToArray(); + } +} diff --git a/src/Modix.Web/Security/ClaimsMiddleware.cs b/src/Modix.Web/Security/ClaimsMiddleware.cs index ada0a226..3e745063 100644 --- a/src/Modix.Web/Security/ClaimsMiddleware.cs +++ b/src/Modix.Web/Security/ClaimsMiddleware.cs @@ -33,6 +33,9 @@ public async Task InvokeAsync(HttpContext context, IAuthorizationService authori claimsIdentity.AddClaims(claims); + // Look, I thought it was funny, okay? :D + claimsIdentity.AddClaim(new Claim(ClaimTypes.PostalCode, currentGuild.Id.ToString())); + await next(context); } } diff --git a/src/Modix.Web/Services/CookieService.cs b/src/Modix.Web/Services/CookieService.cs index b9afd472..2b575097 100644 --- a/src/Modix.Web/Services/CookieService.cs +++ b/src/Modix.Web/Services/CookieService.cs @@ -1,9 +1,10 @@ using Microsoft.JSInterop; using Modix.Web.Models; +using Modix.Web.Shared.Services; namespace Modix.Web.Services; -public class CookieService(IJSRuntime jsRuntime, SessionState sessionState) +public class CookieService(IJSRuntime jsRuntime, SessionState sessionState) : ICookieService { public async Task SetSelectedGuildAsync(ulong guildId) { diff --git a/src/Modix.Web/Services/DiscordHelper.cs b/src/Modix.Web/Services/DiscordHelper.cs index d80cfce2..08d51efd 100644 --- a/src/Modix.Web/Services/DiscordHelper.cs +++ b/src/Modix.Web/Services/DiscordHelper.cs @@ -16,42 +16,6 @@ public SocketGuild GetUserGuild() return client.Guilds.First(); } - public IEnumerable GetGuildOptions() - { - var currentUser = GetCurrentUser(); - if (currentUser is null) - return Array.Empty(); - - return client - .Guilds - .Where(d => d.GetUser(currentUser.Id) != null) - .Select(d => new GuildOption(d.Id, d.Name, d.IconUrl)); - } - - public SocketGuildUser? GetCurrentUser() - { - var currentGuild = GetUserGuild(); - return currentGuild.GetUser(sessionState.CurrentUserId); - } - - public IEnumerable AutoCompleteRoles(string query) - { - if (query.StartsWith('@')) - { - query = query[1..]; - } - - var currentGuild = GetUserGuild(); - IEnumerable result = currentGuild.Roles; - - if (!string.IsNullOrWhiteSpace(query)) - { - result = result.Where(d => d.Name.Contains(query, StringComparison.OrdinalIgnoreCase)); - } - - return result.Take(10).Select(d => new RoleInformation(d.Id, d.Name, d.Color.ToString())); - } - public IEnumerable AutocompleteChannels(string query) { if (query.StartsWith('#')) diff --git a/src/Modix.Web/Setup.cs b/src/Modix.Web/Setup.cs index 9390adf0..cdc13574 100644 --- a/src/Modix.Web/Setup.cs +++ b/src/Modix.Web/Setup.cs @@ -5,6 +5,7 @@ using Modix.Web.Models; using Modix.Web.Security; using Modix.Web.Services; +using Modix.Web.Shared.Services; using MudBlazor; using MudBlazor.Services; @@ -64,7 +65,7 @@ public static IServiceCollection ConfigureBlazorServices(this IServiceCollection services .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() .AddCascadingValue(sp => sp.GetRequiredService()) .AddMudServices() From 23af1d76d04a3c194b6b1e2237671b1d4d88663a Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:28:00 +0200 Subject: [PATCH 29/46] Move Configuration/Channels page over to the wasm project --- .../Configuration/DesignatedChannelData.cs | 4 +- .../Configuration/DesignatedChannelType.cs | 40 +++++++++++ .../Components/Configuration/Channels.razor | 66 +++++++++++-------- .../Configuration/IndividualDesignation.razor | 65 ++++++++++++++++++ .../Pages/Configuration.razor | 28 +++++--- .../Controllers/AutocompleteController.cs | 31 ++++++--- .../DesignatedChannelController.cs | 61 +++++++++++++++++ 7 files changed, 246 insertions(+), 49 deletions(-) rename src/{Modix.Web => Modix.Web.Shared}/Models/Configuration/DesignatedChannelData.cs (60%) create mode 100644 src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs rename src/{Modix.Web => Modix.Web.Wasm}/Components/Configuration/Channels.razor (74%) create mode 100644 src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor rename src/{Modix.Web => Modix.Web.Wasm}/Pages/Configuration.razor (64%) create mode 100644 src/Modix.Web/Controllers/DesignatedChannelController.cs diff --git a/src/Modix.Web/Models/Configuration/DesignatedChannelData.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs similarity index 60% rename from src/Modix.Web/Models/Configuration/DesignatedChannelData.cs rename to src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs index d2e8cbdb..7dbcb6fb 100644 --- a/src/Modix.Web/Models/Configuration/DesignatedChannelData.cs +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs @@ -1,5 +1,3 @@ -using Modix.Data.Models.Core; - -namespace Modix.Web.Models.Configuration; +namespace Modix.Web.Shared.Models.Configuration; public record DesignatedChannelData(long Id, ulong RoleId, DesignatedChannelType ChannelDesignation, string Name); diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs new file mode 100644 index 00000000..723db694 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs @@ -0,0 +1,40 @@ +namespace Modix.Web.Shared.Models.Configuration; + +/// +/// Defines the possible types designations that may be assigned to channels. +/// +public enum DesignatedChannelType +{ + /// + /// Defines a channel that logs actions performed by the moderation feature. + /// + ModerationLog, + /// + /// Defines a channel that logs modified and deleted messages. + /// + MessageLog, + /// + /// Defines a channel that logs actions performed by the promotions feature. + /// + PromotionLog, + /// + /// Defines a channel to send promotion campaign creation/closing notifications. + /// + PromotionNotifications, + /// + /// Defines a channel that is not subject to auto-moderation behaviors of the moderation feature. + /// + Unmoderated, + /// + /// Defines a channel to which starred messages are sent. + /// + Starboard, + /// + /// Defines a channel that should be included when calculating user participation. + /// + CountsTowardsParticipation, + /// + /// Defines a channel where messages, if starred, are not sent to the starboard + /// + IgnoredFromStarboard, +} diff --git a/src/Modix.Web/Components/Configuration/Channels.razor b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor similarity index 74% rename from src/Modix.Web/Components/Configuration/Channels.razor rename to src/Modix.Web.Wasm/Components/Configuration/Channels.razor index aad41f96..202dddbd 100644 --- a/src/Modix.Web/Components/Configuration/Channels.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor @@ -1,7 +1,5 @@ -@using Modix.Data.Models.Core; -@using Modix.Services; -@using Modix.Web.Models.Configuration; -@using Modix.Web.Services; +@using Microsoft.AspNetCore.Components.Authorization +@using Modix.Web.Shared.Models.Configuration @using Modix.Web.Wasm.Components @using MudBlazor @using Humanizer; @@ -19,11 +17,10 @@ + Title="Channel Name" /> Designation @foreach (var designation in DesignatedChannelTypes) @@ -64,7 +61,9 @@ { @foreach (var designatedChannelMapping in channelDesignations) { -
- + @*TODO*@ + @* *@ +
@@ -89,13 +90,10 @@ @code { [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - public DesignatedChannelService DesignatedChannelService { get; set; } = null!; - - [Inject] - public ISnackbar Snackbar { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } private Dictionary>? DesignatedChannelMappings { get; set; } private DesignatedChannelType[]? DesignatedChannelTypes { get; set; } @@ -109,19 +107,24 @@ if (!firstRender) return; - var currentGuild = DiscordHelper.GetUserGuild(); - var designatedChannels = await DesignatedChannelService.GetDesignatedChannels(currentGuild.Id); - - DesignatedChannelMappings = designatedChannels - .Select(d => new DesignatedChannelData(d.Id, d.Channel.Id, d.Type, currentGuild?.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name)) - .ToLookup(x => x.ChannelDesignation, x => x) - .ToDictionary(x => x.Key, x => x.ToList()); + using var client = HttpClientFactory.CreateClient("api"); + DesignatedChannelMappings = await client.GetFromJsonAsync>>("api/config/channels"); DesignatedChannelTypes = Enum.GetValues(); StateHasChanged(); } + private async Task> AutoCompleteChannels(string query) + { + using var client = HttpClientFactory.CreateClient("api"); + + var escapedQuery = Uri.EscapeDataString(query); + var channels = await client.GetFromJsonAsync($"api/autocomplete/channels/{escapedQuery}"); + + return channels ?? []; + } + public void ToggleCreateDialog() { _createDialogVisible = !_createDialogVisible; @@ -139,13 +142,21 @@ public async Task SaveDesignation() { - var currentGuild = DiscordHelper.GetUserGuild(); - var channel = (Discord.IMessageChannel)currentGuild.GetChannel(_selectedChannel!.Id); - - var id = await DesignatedChannelService.AddDesignatedChannel(currentGuild, channel, _selectedDesignatedChannelType!.Value); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.PutAsync($"api/config/channels/{_selectedChannel.Id}/{_selectedDesignatedChannelType.Value}", default); _createDialogVisible = false; + if(!response.IsSuccessStatusCode) + { + var errorMessage = await response.Content.ReadAsStringAsync(); + Snackbar.Add(errorMessage, Severity.Error); + + return; + } + + var id = long.Parse(await response.Content.ReadAsStringAsync()); + if (!DesignatedChannelMappings!.ContainsKey(_selectedDesignatedChannelType.Value)) { DesignatedChannelMappings[_selectedDesignatedChannelType.Value] = new List(); @@ -158,7 +169,10 @@ public async Task RemoveDesignation(long id, DesignatedChannelType designatedChannelType) { - await DesignatedChannelService.RemoveDesignatedChannelById(id); + using var client = HttpClientFactory.CreateClient("api"); + var response = await client.DeleteAsync($"api/config/channels/{id}"); + + response.EnsureSuccessStatusCode(); var channelMappingsWithType = DesignatedChannelMappings![designatedChannelType]; var removedChannelMapping = channelMappingsWithType.First(x => x.Id == id); diff --git a/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor b/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor new file mode 100644 index 00000000..d04d7a5a --- /dev/null +++ b/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor @@ -0,0 +1,65 @@ +@using Microsoft.AspNetCore.Components.Authorization +@using MudBlazor + + + + + @NamePrefix@Name + + + @if (!_showConfirm) + { + + X + + } + else + { + Remove Designation? + + + Yes + + + No + + } + + + + +@code { + [Parameter, EditorRequired] + public EventCallback RemoveDesignation { get; set; } + + [Parameter, EditorRequired] + public string? AuthorizationRoleForDelete { get; set; } + + [Parameter, EditorRequired] + public string? NamePrefix { get; set; } + + [Parameter, EditorRequired] + public string? Name { get; set; } + + [Parameter, EditorRequired] + public long Id { get; set; } + + private bool _showConfirm; +} diff --git a/src/Modix.Web/Pages/Configuration.razor b/src/Modix.Web.Wasm/Pages/Configuration.razor similarity index 64% rename from src/Modix.Web/Pages/Configuration.razor rename to src/Modix.Web.Wasm/Pages/Configuration.razor index a763f58d..dc2c9992 100644 --- a/src/Modix.Web/Pages/Configuration.razor +++ b/src/Modix.Web.Wasm/Pages/Configuration.razor @@ -1,15 +1,22 @@ @page "/config" @page "/config/{SubPage}" -@attribute [Authorize( +@* TODO *@ +@* @attribute [Authorize( Roles = $@" {nameof(AuthorizationClaim.DesignatedRoleMappingRead)}, {nameof(AuthorizationClaim.DesignatedChannelMappingRead)}, - {nameof(AuthorizationClaim.AuthorizationConfigure)}")] + {nameof(AuthorizationClaim.AuthorizationConfigure)}")] *@ + +@attribute [Authorize( + Roles = $@" + DesignatedRoleMappingRead, + DesignatedChannelMappingRead, + AuthorizationConfigure")] -@using Modix.Data.Models.Core; -@using Modix.Web.Components -@using Modix.Web.Components.Configuration +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Modix.Web.Wasm.Components.Configuration @using MudBlazor Modix - Configuration @@ -32,21 +39,22 @@
@if (SubPage == "roles") { - +@* - + *@ } else if (SubPage == "channels") { - + @* *@ + } else if (SubPage == "claims") { - +@* - + *@ }
diff --git a/src/Modix.Web/Controllers/AutocompleteController.cs b/src/Modix.Web/Controllers/AutocompleteController.cs index 751d6009..644b782e 100644 --- a/src/Modix.Web/Controllers/AutocompleteController.cs +++ b/src/Modix.Web/Controllers/AutocompleteController.cs @@ -1,6 +1,5 @@ using Discord; using Discord.WebSocket; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; using Modix.Data.Utilities; @@ -12,7 +11,6 @@ namespace Modix.Web.Controllers; [Route("~/api/autocomplete")] [ApiController] -[Authorize] public class AutocompleteController : ModixController { private readonly IUserService _userService; @@ -32,17 +30,30 @@ public async Task> AutocompleteUsersAsync(string query) .Take(10) .Select(FromIGuildUser); - if (!result.Any() && ulong.TryParse(query, out var userId)) - { - var user = await _userService.GetUserInformationAsync(UserGuild.Id, userId); + if (result.Any() || !ulong.TryParse(query, out var userId)) + return result; + + var user = await _userService.GetUserInformationAsync(UserGuild.Id, userId); - if (user is not null) - { - result = [ FromNonGuildUser(user) ]; - } + if (user is not null) + return [ FromNonGuildUser(user) ]; + + return []; + } + + [HttpGet("channels/{query}")] + public IEnumerable AutoCompleteChannels(string query) + { + if (query.StartsWith('#')) + { + query = query[1..]; } - return result; + return UserGuild.Channels + .Where(d => d is SocketTextChannel + && d.Name.Contains(query, StringComparison.OrdinalIgnoreCase)) + .Take(10) + .Select(d => new ChannelInformation(d.Id, d.Name)); } public static ModixUser FromIGuildUser(IGuildUser user) => new() diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs new file mode 100644 index 00000000..893fc172 --- /dev/null +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -0,0 +1,61 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models.Core; +using Modix.Services; +using Modix.Web.Shared.Models.Configuration; + +namespace Modix.Web.Controllers; + +[Route("~/api/config/channels")] +[ApiController] +[Authorize] +public class DesignatedChannelController : ModixController +{ + private readonly DesignatedChannelService _designatedChannelService; + + public DesignatedChannelController(DesignatedChannelService designatedChannelService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _designatedChannelService = designatedChannelService; + } + + + [HttpGet] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingRead))] + public async Task>> GetChannelDesignationsAsync() + { + var designatedChannels = await _designatedChannelService.GetDesignatedChannels(UserGuild.Id); + + return designatedChannels + .Select(d => new DesignatedChannelData( + d.Id, + d.Channel.Id, + (Shared.Models.Configuration.DesignatedChannelType)(int)d.Type, + UserGuild?.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name)) + .ToLookup(x => x.ChannelDesignation, x => x) + .ToDictionary(x => x.Key, x => x.ToList()); + } + + [HttpPut("{channelId}/{designatedChannelType}")] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingCreate))] + public async Task CreateDesignationAsync(ulong channelId, Shared.Models.Configuration.DesignatedChannelType designatedChannelType) + { + var foundChannel = UserGuild?.GetChannel(channelId); + + if (foundChannel is not ISocketMessageChannel messageChannel) + return BadRequest($"A message channel was not found with id {channelId} in guild with id {UserGuild.Id}"); + + var id = await _designatedChannelService.AddDesignatedChannel(foundChannel.Guild, messageChannel, (Data.Models.Core.DesignatedChannelType)(int)designatedChannelType); + + return Ok(id); + } + + [HttpDelete("{id}")] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingDelete))] + public async Task RemoveDesignationAsync(long id) + { + await _designatedChannelService.RemoveDesignatedChannelById(id); + } +} From d2447cb150148718b144c23287e64bbb4985454f Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sat, 31 Aug 2024 11:04:07 +0200 Subject: [PATCH 30/46] Move Configuration/Roles page over to the wasm project --- .../Configuration/DesignatedRoleData.cs | 4 +- .../Configuration/DesignatedRoleType.cs | 20 ++++++ .../Components/Configuration/Channels.razor | 4 +- .../Components/Configuration/Roles.razor | 61 ++++++++++------- src/Modix.Web.Wasm/Pages/Configuration.razor | 5 +- .../Configuration/IndividualDesignation.razor | 66 ------------------- .../Controllers/AutocompleteController.cs | 18 +++++ .../DesignatedChannelController.cs | 4 +- .../Controllers/DesignatedRoleController.cs | 60 +++++++++++++++++ 9 files changed, 143 insertions(+), 99 deletions(-) rename src/{Modix.Web => Modix.Web.Shared}/Models/Configuration/DesignatedRoleData.cs (58%) create mode 100644 src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs rename src/{Modix.Web => Modix.Web.Wasm}/Components/Configuration/Roles.razor (75%) delete mode 100644 src/Modix.Web/Components/Configuration/IndividualDesignation.razor create mode 100644 src/Modix.Web/Controllers/DesignatedRoleController.cs diff --git a/src/Modix.Web/Models/Configuration/DesignatedRoleData.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs similarity index 58% rename from src/Modix.Web/Models/Configuration/DesignatedRoleData.cs rename to src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs index 169c92c1..49d2b0ac 100644 --- a/src/Modix.Web/Models/Configuration/DesignatedRoleData.cs +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs @@ -1,5 +1,3 @@ -using Modix.Data.Models.Core; - -namespace Modix.Web.Models.Configuration; +namespace Modix.Web.Shared.Models.Configuration; public record DesignatedRoleData(long Id, ulong RoleId, DesignatedRoleType RoleDesignation, string Name); diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs new file mode 100644 index 00000000..8f78d771 --- /dev/null +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs @@ -0,0 +1,20 @@ +namespace Modix.Web.Shared.Models.Configuration; + +/// +/// Defines the possible types designations that may be assigned to roles. +/// +public enum DesignatedRoleType +{ + /// + /// Defines a role that serves as a member of the rank hierarchy. + /// + Rank, + /// + /// Defines a role that is used by the moderation feature to mute users. + /// + ModerationMute, + /// + /// Defines a role whose mentionability is allowed throughout the guild. + /// + Pingable, +} diff --git a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor index 202dddbd..67746db1 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor @@ -143,7 +143,7 @@ public async Task SaveDesignation() { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsync($"api/config/channels/{_selectedChannel.Id}/{_selectedDesignatedChannelType.Value}", default); + using var response = await client.PutAsync($"api/config/channels/{_selectedChannel.Id}/{_selectedDesignatedChannelType.Value}", default); _createDialogVisible = false; @@ -170,7 +170,7 @@ public async Task RemoveDesignation(long id, DesignatedChannelType designatedChannelType) { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.DeleteAsync($"api/config/channels/{id}"); + using var response = await client.DeleteAsync($"api/config/channels/{id}"); response.EnsureSuccessStatusCode(); diff --git a/src/Modix.Web/Components/Configuration/Roles.razor b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor similarity index 75% rename from src/Modix.Web/Components/Configuration/Roles.razor rename to src/Modix.Web.Wasm/Components/Configuration/Roles.razor index 4f6b92d7..9b89de09 100644 --- a/src/Modix.Web/Components/Configuration/Roles.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor @@ -1,11 +1,8 @@ -@using Modix.Data.Models.Core; -@using Modix.Services.Core; -@using Modix.Web.Models.Configuration; -@using Modix.Web.Services; +@using Humanizer +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Shared.Models.Common +@using Modix.Web.Shared.Models.Configuration @using MudBlazor -@using Humanizer; -@using System.Security.Claims; Modix - Roles Role Designations @@ -19,7 +16,7 @@
- + @* *@ +
@@ -90,13 +90,10 @@ @code { [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } [Inject] - public IDesignatedRoleService DesignatedRoleService { get; set; } = null!; - - [Inject] - public ISnackbar Snackbar { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } private Dictionary>? DesignatedRoleMappings { get; set; } private DesignatedRoleType[]? DesignatedRoleTypes { get; set; } @@ -110,13 +107,9 @@ if (!firstRender) return; - var currentGuild = DiscordHelper.GetUserGuild(); - var designatedRoles = await DesignatedRoleService.GetDesignatedRolesAsync(currentGuild.Id); - DesignatedRoleMappings = designatedRoles - .Select(d => new DesignatedRoleData(d.Id, d.Role.Id, d.Type, currentGuild?.GetRole(d.Role.Id)?.Name ?? d.Role.Name)) - .ToLookup(x => x.RoleDesignation, x => x) - .ToDictionary(x => x.Key, x => x.ToList()); + using var client = HttpClientFactory.CreateClient("api"); + DesignatedRoleMappings = await client.GetFromJsonAsync>>("api/config/roles"); DesignatedRoleTypes = Enum.GetValues(); @@ -133,6 +126,14 @@ } } + private async Task> AutoCompleteRoles(string query) + { + using var client = HttpClientFactory.CreateClient("api"); + var roles = await client.GetFromJsonAsync($"api/autocomplete/roles/{query}"); + + return roles ?? []; + } + private void SelectedRoleChanged(RoleInformation role) { _selectedRole = role; @@ -140,12 +141,21 @@ public async Task SaveDesignation() { - var currentGuild = DiscordHelper.GetUserGuild(); - - var id = await DesignatedRoleService.AddDesignatedRoleAsync(currentGuild.Id, _selectedRole!.Id, _selectedDesignatedRoleType!.Value); + using var client = HttpClientFactory.CreateClient("api"); + using var response = await client.PutAsync($"api/config/roles/{_selectedRole.Id}/{_selectedDesignatedRoleType.Value}", default); _createDialogVisible = false; + if (!response.IsSuccessStatusCode) + { + var errorMessage = await response.Content.ReadAsStringAsync(); + Snackbar.Add(errorMessage, Severity.Error); + + return; + } + + var id = long.Parse(await response.Content.ReadAsStringAsync()); + if (!DesignatedRoleMappings!.ContainsKey(_selectedDesignatedRoleType.Value)) { DesignatedRoleMappings[_selectedDesignatedRoleType.Value] = new List(); @@ -158,7 +168,10 @@ public async Task RemoveDesignation(long id, DesignatedRoleType designatedRoleType) { - await DesignatedRoleService.RemoveDesignatedRoleByIdAsync(id); + using var client = HttpClientFactory.CreateClient("api"); + using var response = await client.DeleteAsync($"api/config/roles/{id}"); + + response.EnsureSuccessStatusCode(); var roleMappingsWithType = DesignatedRoleMappings![designatedRoleType]; var removedRoleMapping = roleMappingsWithType.First(x => x.Id == id); diff --git a/src/Modix.Web.Wasm/Pages/Configuration.razor b/src/Modix.Web.Wasm/Pages/Configuration.razor index dc2c9992..5d8516d3 100644 --- a/src/Modix.Web.Wasm/Pages/Configuration.razor +++ b/src/Modix.Web.Wasm/Pages/Configuration.razor @@ -39,9 +39,10 @@
@if (SubPage == "roles") { -@* + @* *@ + - *@ + } else if (SubPage == "channels") { diff --git a/src/Modix.Web/Components/Configuration/IndividualDesignation.razor b/src/Modix.Web/Components/Configuration/IndividualDesignation.razor deleted file mode 100644 index f79267fa..00000000 --- a/src/Modix.Web/Components/Configuration/IndividualDesignation.razor +++ /dev/null @@ -1,66 +0,0 @@ -@using Modix.Data.Models.Core; -@using Modix.Web.Models.Configuration; -@using MudBlazor - - - - - @NamePrefix@Name - - - @if (!_showConfirm) - { - - X - - } - else - { - Remove Designation? - - - Yes - - - No - - } - - - - -@code { - [Parameter, EditorRequired] - public EventCallback RemoveDesignation { get; set; } - - [Parameter, EditorRequired] - public string? AuthorizationRoleForDelete { get; set; } - - [Parameter, EditorRequired] - public string? NamePrefix { get; set; } - - [Parameter, EditorRequired] - public string? Name { get; set; } - - [Parameter, EditorRequired] - public long Id { get; set; } - - private bool _showConfirm; -} diff --git a/src/Modix.Web/Controllers/AutocompleteController.cs b/src/Modix.Web/Controllers/AutocompleteController.cs index 644b782e..ed489ce8 100644 --- a/src/Modix.Web/Controllers/AutocompleteController.cs +++ b/src/Modix.Web/Controllers/AutocompleteController.cs @@ -56,6 +56,24 @@ public IEnumerable AutoCompleteChannels(string query) .Select(d => new ChannelInformation(d.Id, d.Name)); } + [HttpGet("roles/{query}")] + public IEnumerable AutoCompleteRoles(string query) + { + if (query.StartsWith('@')) + { + query = query[1..]; + } + + IEnumerable result = UserGuild.Roles; + + if (!string.IsNullOrWhiteSpace(query)) + { + result = result.Where(d => d.Name.Contains(query, StringComparison.OrdinalIgnoreCase)); + } + + return result.Take(10).Select(d => new RoleInformation(d.Id, d.Name, d.Color.ToString())); + } + public static ModixUser FromIGuildUser(IGuildUser user) => new() { Name = user.GetDisplayName(), diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs index 893fc172..848935eb 100644 --- a/src/Modix.Web/Controllers/DesignatedChannelController.cs +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -33,7 +33,7 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer d.Id, d.Channel.Id, (Shared.Models.Configuration.DesignatedChannelType)(int)d.Type, - UserGuild?.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name)) + UserGuild.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name)) .ToLookup(x => x.ChannelDesignation, x => x) .ToDictionary(x => x.Key, x => x.ToList()); } @@ -42,7 +42,7 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingCreate))] public async Task CreateDesignationAsync(ulong channelId, Shared.Models.Configuration.DesignatedChannelType designatedChannelType) { - var foundChannel = UserGuild?.GetChannel(channelId); + var foundChannel = UserGuild.GetChannel(channelId); if (foundChannel is not ISocketMessageChannel messageChannel) return BadRequest($"A message channel was not found with id {channelId} in guild with id {UserGuild.Id}"); diff --git a/src/Modix.Web/Controllers/DesignatedRoleController.cs b/src/Modix.Web/Controllers/DesignatedRoleController.cs new file mode 100644 index 00000000..1c26a3a1 --- /dev/null +++ b/src/Modix.Web/Controllers/DesignatedRoleController.cs @@ -0,0 +1,60 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models.Core; +using Modix.Services.Core; +using Modix.Web.Shared.Models.Configuration; + +namespace Modix.Web.Controllers; + +[Route("~/api/config/roles")] +[ApiController] +[Authorize] +public class DesignatedRoleController : ModixController +{ + private readonly IDesignatedRoleService _designatedRoleService; + + public DesignatedRoleController(IDesignatedRoleService designatedRoleService, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _designatedRoleService = designatedRoleService; + } + + [HttpGet] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedRoleMappingRead))] + public async Task>> GetRoleDesignationsAsync() + { + var designatedRoles = await _designatedRoleService.GetDesignatedRolesAsync(UserGuild.Id); + + return designatedRoles + .Select(d => new DesignatedRoleData( + d.Id, + d.Role.Id, + (Shared.Models.Configuration.DesignatedRoleType)(int)d.Type, + UserGuild.GetRole(d.Role.Id)?.Name ?? d.Role.Name)) + .ToLookup(x => x.RoleDesignation, x => x) + .ToDictionary(x => x.Key, x => x.ToList()); + } + + [HttpPut("{roleId}/{designatedRoleType}")] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedRoleMappingCreate))] + public async Task CreateDesignationAsync(ulong roleId, Shared.Models.Configuration.DesignatedRoleType designatedRoleType) + { + var foundRole = UserGuild.GetRole(roleId); + + if (foundRole is null) + return BadRequest($"A role was not found with id {roleId} in guild with id {ModixAuth.CurrentGuildId}"); + + var id = await _designatedRoleService.AddDesignatedRoleAsync(UserGuild.Id, roleId, (Data.Models.Core.DesignatedRoleType)(int)designatedRoleType); + + return Ok(id); + } + + [HttpDelete("{id}")] + [Authorize(Roles = nameof(AuthorizationClaim.DesignatedRoleMappingDelete))] + public async Task RemoveDesignationAsync(long id) + { + await _designatedRoleService.RemoveDesignatedRoleByIdAsync(id); + } +} From e584bb1db6e046eebafdbcf6eedf54b5d716be69 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:38:46 +0200 Subject: [PATCH 31/46] Create common models project --- Modix.sln | 14 ++ src/Modix.Models/Core/AuthorizationClaim.cs | 166 ++++++++++++++++++ .../Core/AuthorizationClaimCategory.cs | 13 ++ src/Modix.Models/Modix.Models.csproj | 9 + .../Utilities/ClaimInfoAttribute.cs | 16 ++ 5 files changed, 218 insertions(+) create mode 100644 src/Modix.Models/Core/AuthorizationClaim.cs create mode 100644 src/Modix.Models/Core/AuthorizationClaimCategory.cs create mode 100644 src/Modix.Models/Modix.Models.csproj create mode 100644 src/Modix.Models/Utilities/ClaimInfoAttribute.cs diff --git a/Modix.sln b/Modix.sln index ed938efc..615ec3d0 100644 --- a/Modix.sln +++ b/Modix.sln @@ -68,6 +68,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Wasm", "src\Modix EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Web.Shared", "src\Modix.Web.Shared\Modix.Web.Shared.csproj", "{A603AE22-F588-466E-A7B1-298989E76890}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modix.Models", "src\Modix.Models\Modix.Models.csproj", "{A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -246,6 +248,18 @@ Global {A603AE22-F588-466E-A7B1-298989E76890}.Release|x64.Build.0 = Release|Any CPU {A603AE22-F588-466E-A7B1-298989E76890}.Release|x86.ActiveCfg = Release|Any CPU {A603AE22-F588-466E-A7B1-298989E76890}.Release|x86.Build.0 = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|x64.Build.0 = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Debug|x86.Build.0 = Debug|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|Any CPU.Build.0 = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|x64.ActiveCfg = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|x64.Build.0 = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|x86.ActiveCfg = Release|Any CPU + {A7FCE49E-5579-4E37-81E0-6AADEA0FBEA1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Modix.Models/Core/AuthorizationClaim.cs b/src/Modix.Models/Core/AuthorizationClaim.cs new file mode 100644 index 00000000..ea11e226 --- /dev/null +++ b/src/Modix.Models/Core/AuthorizationClaim.cs @@ -0,0 +1,166 @@ +using Modix.Models.Utilities; +using static Modix.Models.Core.AuthorizationClaimCategory; + +namespace Modix.Models.Core; + +/// +/// Defines the types of claims that can be used to authorize a request. +/// +public enum AuthorizationClaim +{ + /// + /// Authorizes a request to configure the authorization feature. + /// + [ClaimInfo(Configuration, "Authorizes a request to configure the authorization feature.")] + AuthorizationConfigure, + /// + /// Authorizes a request to read infraction/moderation data. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to read infraction/moderation data.")] + ModerationRead, + /// + /// Authorizes a request to attach a note upon a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to attach a note upon a user.")] + ModerationNote, + /// + /// Authorizes a request to issue a warning to a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to issue a warning to a user.")] + ModerationWarn, + /// + /// Authorizes a request to mute a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to mute a user.")] + ModerationMute, + /// + /// Authorizes a request to ban a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to ban a user.")] + ModerationBan, + /// + /// Authorizes a request to configure the moderation feature. + /// + [ClaimInfo(Configuration, "Authorizes a request to configure the moderation feature.")] + ModerationConfigure, + /// + /// Authorizes a request to rescind an infraction upon a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to rescind an infraction upon a user.")] + ModerationRescind, + /// + /// Authorizes a request to delete an infraction upon a user. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to delete an infraction upon a user.")] + ModerationDeleteInfraction, + /// + /// Authorizes a request to update an infraction. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to update an infraction.")] + ModerationUpdateInfraction, + /// + /// Authorizes a request to delete a message from a guild. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to delete a message from a guild.")] + ModerationDeleteMessage, + /// + /// Authorizes a request to mass-delete messages from a guild. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to mass-delete messages from a guild.")] + ModerationMassDeleteMessages, + /// + /// Authorizes a request to view deleted message logs. + /// + [ClaimInfo(Log, "Authorizes a request to view deleted message logs.")] + LogViewDeletedMessages, + /// + /// Authorizes a request to post a message to a guild, containing content that is in a pattern check. + /// + [ClaimInfo(Misc, "Authorizes a request to post a message to a guild, containing blocked content.")] + BypassMessageContentPatternCheck, + /// + /// Authorizes a request to create a designated channel mapping. + /// + [ClaimInfo(DesignatedChannels, "Authorizes a request to create a designated channel mapping.")] + DesignatedChannelMappingCreate, + /// + /// Authorizes a request to read designated channel mappings. + /// + [ClaimInfo(DesignatedChannels, "Authorizes a request to read designated channel mappings.")] + DesignatedChannelMappingRead, + /// + /// Authorizes a request to delete a designated channel mapping. + /// + [ClaimInfo(DesignatedChannels, "Authorizes a request to delete a designated channel mapping.")] + DesignatedChannelMappingDelete, + /// + /// Authorizes a request to create a designated role mapping. + /// + [ClaimInfo(DesignatedRoles, "Authorizes a request to create a designated role mapping.")] + DesignatedRoleMappingCreate, + /// + /// Authorizes a request to read designated role mappings. + /// + [ClaimInfo(DesignatedRoles, "Authorizes a request to read designated role mappings.")] + DesignatedRoleMappingRead, + /// + /// Authorizes a request to delete a designated role mapping. + /// + [ClaimInfo(DesignatedRoles, "Authorizes a request to delete a designated role mapping.")] + DesignatedRoleMappingDelete, + /// + /// Authorizes a request to create a promotion campaign for a user. + /// + [ClaimInfo(PromotionActions, "Authorizes a request to create a promotion campaign for a user.")] + PromotionsCreateCampaign, + /// + /// Authorizes a request to close a promotion campaign for a user. + /// + [ClaimInfo(PromotionActions, "Authorizes a request to close a promotion campaign for a user.")] + PromotionsCloseCampaign, + /// + /// Authorizes a request to comment on a promotion campaign for a user. + /// + [ClaimInfo(PromotionActions, "Authorizes a request to comment on a promotion campaign for a user.")] + PromotionsComment, + /// + /// Authorizes a request to read promotion campaign data. + /// + [ClaimInfo(PromotionActions, "Authorizes a request to read promotion campaign data.")] + PromotionsRead, + /// + /// Authorizes a request to perform a count for a popularity contest + /// + [ClaimInfo(Misc, "Authorizes a request to perform a count for a popularity contest")] + PopularityContestCount, + /// + /// Authorizes a request to mention a role that has restricted mentionability. + /// + [ClaimInfo(Misc, "Authorizes a request to mention a role that has restricted mentionability.")] + MentionRestrictedRole, + /// + /// Authorizes a request to create a tag. + /// + [ClaimInfo(TagActions, "Authorizes a request to create a tag.")] + CreateTag, + /// + /// Authorizes a request to invoke a tag. + /// + [ClaimInfo(TagActions, "Authorizes a request to invoke a tag.")] + UseTag, + /// + /// Authorizes a request to maintain a tag that was not created by the requesting user. + /// + [ClaimInfo(TagActions, "Authorizes a request to maintain a tag that was not created by the requesting user.")] + MaintainOtherUserTag, + /// + /// Authorizes a request to create a giveaway and determine its winners. + /// + [ClaimInfo(Misc, "Authorizes a request to create a giveaway and determine its winners.")] + ExecuteGiveaway, + /// + /// Authorizes a request to manage message patterns. + /// + [ClaimInfo(ModerationActions, "Authorizes a request to manage message patterns.")] + ManageMessageContentPatterns, +} diff --git a/src/Modix.Models/Core/AuthorizationClaimCategory.cs b/src/Modix.Models/Core/AuthorizationClaimCategory.cs new file mode 100644 index 00000000..7277416a --- /dev/null +++ b/src/Modix.Models/Core/AuthorizationClaimCategory.cs @@ -0,0 +1,13 @@ +namespace Modix.Models.Core; + +public enum AuthorizationClaimCategory +{ + Configuration, + ModerationActions, + DesignatedChannels, + DesignatedRoles, + PromotionActions, + TagActions, + Misc, + Log, +} diff --git a/src/Modix.Models/Modix.Models.csproj b/src/Modix.Models/Modix.Models.csproj new file mode 100644 index 00000000..fa71b7ae --- /dev/null +++ b/src/Modix.Models/Modix.Models.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/src/Modix.Models/Utilities/ClaimInfoAttribute.cs b/src/Modix.Models/Utilities/ClaimInfoAttribute.cs new file mode 100644 index 00000000..8b486a26 --- /dev/null +++ b/src/Modix.Models/Utilities/ClaimInfoAttribute.cs @@ -0,0 +1,16 @@ +using Modix.Models.Core; + +namespace Modix.Models.Utilities; + +[AttributeUsage(AttributeTargets.Field)] +public class ClaimInfoAttribute : Attribute +{ + public AuthorizationClaimCategory Category { get; set; } + public string Description { get; set; } + + public ClaimInfoAttribute(AuthorizationClaimCategory category, string description) + { + Category = category; + Description = description; + } +} From 0034e8dd0c4035faeefadf373cfca5db5eae7eed Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:49:34 +0200 Subject: [PATCH 32/46] Switch over to using AuthorizationClaim enum from newly created models project --- src/Modix.Bot/DiscordBotSession.cs | 2 +- src/Modix.Bot/Modules/AuthorizationModule.cs | 1 + .../Modules/DesignatedChannelsModule.cs | 1 + src/Modix.Bot/Modules/DesignatedRoleModule.cs | 1 + src/Modix.Bot/Modules/InfractionModule.cs | 1 + src/Modix.Bot/Modules/PromotionsModule.cs | 1 + src/Modix.Bot/Modules/TagModule.cs | 1 + src/Modix.Bot/Modules/UserInfoModule.cs | 1 + .../Preconditions/RequireAnyClaimAttribute.cs | 3 +- .../Preconditions/RequireClaimsAttribute.cs | 3 +- .../Models/Core/AuthorizationClaim.cs | 167 ------------------ .../Models/Core/AuthorizationClaimCategory.cs | 14 -- src/Modix.Data/Models/Core/ClaimInfoData.cs | 3 +- .../Models/Core/ClaimMappingBrief.cs | 1 + .../Models/Core/ClaimMappingCreationData.cs | 1 + .../Models/Core/ClaimMappingEntity.cs | 1 + .../Models/Core/ClaimMappingSearchCriteria.cs | 1 + .../Models/Core/ClaimMappingSummary.cs | 1 + src/Modix.Data/Modix.Data.csproj | 1 + .../Utilities/ClaimInfoAttribute.cs | 18 -- .../AuthorizationClaimMappingService.cs | 1 + .../AuthorizationClaimService.cs | 1 + .../Core/AuthorizationService.cs | 1 + .../Core/DesignatedRoleService.cs | 1 + .../DesignatedChannelService.cs | 1 + src/Modix.Services/IScopedSession.cs | 2 +- .../MessageContentPatternService.cs | 1 + .../Moderation/GuildOnboardingService.cs | 1 + .../MessageContentCheckBehaviour.cs | 1 + .../Moderation/ModerationService.cs | 9 +- .../Promotions/PromotionsService.cs | 1 + .../Tags/TagInlineParsingHandler.cs | 1 + src/Modix.Services/Tags/TagService.cs | 1 + .../Components/Configuration/Claims.razor | 1 + .../Controllers/CampaignController.cs | 5 +- .../Controllers/DeletedMessagesController.cs | 2 +- .../DesignatedChannelController.cs | 2 +- .../Controllers/DesignatedRoleController.cs | 2 +- .../Controllers/InfractionsController.cs | 2 +- src/Modix.Web/Controllers/TagsController.cs | 2 +- .../Modix.Data.Test/TestData/ClaimMappings.cs | 1 + 41 files changed, 43 insertions(+), 219 deletions(-) delete mode 100644 src/Modix.Data/Models/Core/AuthorizationClaim.cs delete mode 100644 src/Modix.Data/Models/Core/AuthorizationClaimCategory.cs delete mode 100644 src/Modix.Data/Utilities/ClaimInfoAttribute.cs diff --git a/src/Modix.Bot/DiscordBotSession.cs b/src/Modix.Bot/DiscordBotSession.cs index 13bc302b..ceca86e2 100644 --- a/src/Modix.Bot/DiscordBotSession.cs +++ b/src/Modix.Bot/DiscordBotSession.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Discord.Commands; using Discord.WebSocket; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services; namespace Modix.Bot; diff --git a/src/Modix.Bot/Modules/AuthorizationModule.cs b/src/Modix.Bot/Modules/AuthorizationModule.cs index 20113ba1..4485900a 100644 --- a/src/Modix.Bot/Modules/AuthorizationModule.cs +++ b/src/Modix.Bot/Modules/AuthorizationModule.cs @@ -10,6 +10,7 @@ using Modix.Bot.Extensions; using Modix.Bot.Preconditions; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.CommandHelp; using Modix.Services.Core; diff --git a/src/Modix.Bot/Modules/DesignatedChannelsModule.cs b/src/Modix.Bot/Modules/DesignatedChannelsModule.cs index 2b49c92b..1cbe5836 100644 --- a/src/Modix.Bot/Modules/DesignatedChannelsModule.cs +++ b/src/Modix.Bot/Modules/DesignatedChannelsModule.cs @@ -9,6 +9,7 @@ using Modix.Bot.Preconditions; using Modix.Common.Extensions; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services; using Modix.Services.CommandHelp; diff --git a/src/Modix.Bot/Modules/DesignatedRoleModule.cs b/src/Modix.Bot/Modules/DesignatedRoleModule.cs index 9e0e5fcd..058be5b5 100644 --- a/src/Modix.Bot/Modules/DesignatedRoleModule.cs +++ b/src/Modix.Bot/Modules/DesignatedRoleModule.cs @@ -9,6 +9,7 @@ using Modix.Bot.Preconditions; using Modix.Common.Extensions; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.CommandHelp; using Modix.Services.Core; diff --git a/src/Modix.Bot/Modules/InfractionModule.cs b/src/Modix.Bot/Modules/InfractionModule.cs index 9a4671e9..4d9ef32d 100644 --- a/src/Modix.Bot/Modules/InfractionModule.cs +++ b/src/Modix.Bot/Modules/InfractionModule.cs @@ -15,6 +15,7 @@ using Modix.Data.Models; using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; +using Modix.Models.Core; using Modix.Services.CommandHelp; using Modix.Services.Moderation; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Modules/PromotionsModule.cs b/src/Modix.Bot/Modules/PromotionsModule.cs index 5b6c8b38..f68ebefe 100644 --- a/src/Modix.Bot/Modules/PromotionsModule.cs +++ b/src/Modix.Bot/Modules/PromotionsModule.cs @@ -20,6 +20,7 @@ using Modix.Data.Models.Core; using Modix.Data.Models.Promotions; using Modix.Data.Utilities; +using Modix.Models.Core; using Modix.Services.CommandHelp; using Modix.Services.Promotions; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Modules/TagModule.cs b/src/Modix.Bot/Modules/TagModule.cs index d701d83b..d017fe6b 100644 --- a/src/Modix.Bot/Modules/TagModule.cs +++ b/src/Modix.Bot/Modules/TagModule.cs @@ -14,6 +14,7 @@ using Modix.Common.Extensions; using Modix.Data.Models.Core; using Modix.Data.Models.Tags; +using Modix.Models.Core; using Modix.Services.CommandHelp; using Modix.Services.Core; using Modix.Services.Tags; diff --git a/src/Modix.Bot/Modules/UserInfoModule.cs b/src/Modix.Bot/Modules/UserInfoModule.cs index 7078b5e5..ada57179 100644 --- a/src/Modix.Bot/Modules/UserInfoModule.cs +++ b/src/Modix.Bot/Modules/UserInfoModule.cs @@ -21,6 +21,7 @@ using Modix.Data.Models.Emoji; using Modix.Data.Models.Promotions; using Modix.Data.Repositories; +using Modix.Models.Core; using Modix.Services.AutoRemoveMessage; using Modix.Services.CommandHelp; using Modix.Services.Core; diff --git a/src/Modix.Bot/Preconditions/RequireAnyClaimAttribute.cs b/src/Modix.Bot/Preconditions/RequireAnyClaimAttribute.cs index 7037b2c5..c8e3394d 100644 --- a/src/Modix.Bot/Preconditions/RequireAnyClaimAttribute.cs +++ b/src/Modix.Bot/Preconditions/RequireAnyClaimAttribute.cs @@ -6,8 +6,7 @@ using Discord.Interactions; using Microsoft.Extensions.DependencyInjection; - -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Bot.Preconditions diff --git a/src/Modix.Bot/Preconditions/RequireClaimsAttribute.cs b/src/Modix.Bot/Preconditions/RequireClaimsAttribute.cs index 4fc8c1dd..b2c8962b 100644 --- a/src/Modix.Bot/Preconditions/RequireClaimsAttribute.cs +++ b/src/Modix.Bot/Preconditions/RequireClaimsAttribute.cs @@ -6,8 +6,7 @@ using Discord.Interactions; using Microsoft.Extensions.DependencyInjection; - -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Bot.Preconditions diff --git a/src/Modix.Data/Models/Core/AuthorizationClaim.cs b/src/Modix.Data/Models/Core/AuthorizationClaim.cs deleted file mode 100644 index 0bdf7cb4..00000000 --- a/src/Modix.Data/Models/Core/AuthorizationClaim.cs +++ /dev/null @@ -1,167 +0,0 @@ -using Modix.Data.Utilities; -using static Modix.Data.Models.Core.AuthorizationClaimCategory; - -namespace Modix.Data.Models.Core -{ - /// - /// Defines the types of claims that can be used to authorize a request. - /// - public enum AuthorizationClaim - { - /// - /// Authorizes a request to configure the authorization feature. - /// - [ClaimInfo(Configuration, "Authorizes a request to configure the authorization feature.")] - AuthorizationConfigure, - /// - /// Authorizes a request to read infraction/moderation data. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to read infraction/moderation data.")] - ModerationRead, - /// - /// Authorizes a request to attach a note upon a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to attach a note upon a user.")] - ModerationNote, - /// - /// Authorizes a request to issue a warning to a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to issue a warning to a user.")] - ModerationWarn, - /// - /// Authorizes a request to mute a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to mute a user.")] - ModerationMute, - /// - /// Authorizes a request to ban a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to ban a user.")] - ModerationBan, - /// - /// Authorizes a request to configure the moderation feature. - /// - [ClaimInfo(Configuration, "Authorizes a request to configure the moderation feature.")] - ModerationConfigure, - /// - /// Authorizes a request to rescind an infraction upon a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to rescind an infraction upon a user.")] - ModerationRescind, - /// - /// Authorizes a request to delete an infraction upon a user. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to delete an infraction upon a user.")] - ModerationDeleteInfraction, - /// - /// Authorizes a request to update an infraction. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to update an infraction.")] - ModerationUpdateInfraction, - /// - /// Authorizes a request to delete a message from a guild. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to delete a message from a guild.")] - ModerationDeleteMessage, - /// - /// Authorizes a request to mass-delete messages from a guild. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to mass-delete messages from a guild.")] - ModerationMassDeleteMessages, - /// - /// Authorizes a request to view deleted message logs. - /// - [ClaimInfo(Log, "Authorizes a request to view deleted message logs.")] - LogViewDeletedMessages, - /// - /// Authorizes a request to post a message to a guild, containing content that is in a pattern check. - /// - [ClaimInfo(Misc, "Authorizes a request to post a message to a guild, containing blocked content.")] - BypassMessageContentPatternCheck, - /// - /// Authorizes a request to create a designated channel mapping. - /// - [ClaimInfo(DesignatedChannels, "Authorizes a request to create a designated channel mapping.")] - DesignatedChannelMappingCreate, - /// - /// Authorizes a request to read designated channel mappings. - /// - [ClaimInfo(DesignatedChannels, "Authorizes a request to read designated channel mappings.")] - DesignatedChannelMappingRead, - /// - /// Authorizes a request to delete a designated channel mapping. - /// - [ClaimInfo(DesignatedChannels, "Authorizes a request to delete a designated channel mapping.")] - DesignatedChannelMappingDelete, - /// - /// Authorizes a request to create a designated role mapping. - /// - [ClaimInfo(DesignatedRoles, "Authorizes a request to create a designated role mapping.")] - DesignatedRoleMappingCreate, - /// - /// Authorizes a request to read designated role mappings. - /// - [ClaimInfo(DesignatedRoles, "Authorizes a request to read designated role mappings.")] - DesignatedRoleMappingRead, - /// - /// Authorizes a request to delete a designated role mapping. - /// - [ClaimInfo(DesignatedRoles, "Authorizes a request to delete a designated role mapping.")] - DesignatedRoleMappingDelete, - /// - /// Authorizes a request to create a promotion campaign for a user. - /// - [ClaimInfo(PromotionActions, "Authorizes a request to create a promotion campaign for a user.")] - PromotionsCreateCampaign, - /// - /// Authorizes a request to close a promotion campaign for a user. - /// - [ClaimInfo(PromotionActions, "Authorizes a request to close a promotion campaign for a user.")] - PromotionsCloseCampaign, - /// - /// Authorizes a request to comment on a promotion campaign for a user. - /// - [ClaimInfo(PromotionActions, "Authorizes a request to comment on a promotion campaign for a user.")] - PromotionsComment, - /// - /// Authorizes a request to read promotion campaign data. - /// - [ClaimInfo(PromotionActions, "Authorizes a request to read promotion campaign data.")] - PromotionsRead, - /// - /// Authorizes a request to perform a count for a popularity contest - /// - [ClaimInfo(Misc, "Authorizes a request to perform a count for a popularity contest")] - PopularityContestCount, - /// - /// Authorizes a request to mention a role that has restricted mentionability. - /// - [ClaimInfo(Misc, "Authorizes a request to mention a role that has restricted mentionability.")] - MentionRestrictedRole, - /// - /// Authorizes a request to create a tag. - /// - [ClaimInfo(TagActions, "Authorizes a request to create a tag.")] - CreateTag, - /// - /// Authorizes a request to invoke a tag. - /// - [ClaimInfo(TagActions, "Authorizes a request to invoke a tag.")] - UseTag, - /// - /// Authorizes a request to maintain a tag that was not created by the requesting user. - /// - [ClaimInfo(TagActions, "Authorizes a request to maintain a tag that was not created by the requesting user.")] - MaintainOtherUserTag, - /// - /// Authorizes a request to create a giveaway and determine its winners. - /// - [ClaimInfo(Misc, "Authorizes a request to create a giveaway and determine its winners.")] - ExecuteGiveaway, - /// - /// Authorizes a request to manage message patterns. - /// - [ClaimInfo(ModerationActions, "Authorizes a request to manage message patterns.")] - ManageMessageContentPatterns, - } -} diff --git a/src/Modix.Data/Models/Core/AuthorizationClaimCategory.cs b/src/Modix.Data/Models/Core/AuthorizationClaimCategory.cs deleted file mode 100644 index 2c55ea9c..00000000 --- a/src/Modix.Data/Models/Core/AuthorizationClaimCategory.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Modix.Data.Models.Core -{ - public enum AuthorizationClaimCategory - { - Configuration, - ModerationActions, - DesignatedChannels, - DesignatedRoles, - PromotionActions, - TagActions, - Misc, - Log, - } -} diff --git a/src/Modix.Data/Models/Core/ClaimInfoData.cs b/src/Modix.Data/Models/Core/ClaimInfoData.cs index 0611663f..80b8a257 100644 --- a/src/Modix.Data/Models/Core/ClaimInfoData.cs +++ b/src/Modix.Data/Models/Core/ClaimInfoData.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Modix.Data.Models.Core; -using Modix.Data.Utilities; +using Modix.Models.Utilities; namespace Modix.Models.Core { diff --git a/src/Modix.Data/Models/Core/ClaimMappingBrief.cs b/src/Modix.Data/Models/Core/ClaimMappingBrief.cs index 6432dced..bdc87d9d 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingBrief.cs +++ b/src/Modix.Data/Models/Core/ClaimMappingBrief.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/ClaimMappingCreationData.cs b/src/Modix.Data/Models/Core/ClaimMappingCreationData.cs index 8d039ef5..ef545a76 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingCreationData.cs +++ b/src/Modix.Data/Models/Core/ClaimMappingCreationData.cs @@ -1,4 +1,5 @@ using System; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/ClaimMappingEntity.cs b/src/Modix.Data/Models/Core/ClaimMappingEntity.cs index 6f808434..a468513d 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingEntity.cs +++ b/src/Modix.Data/Models/Core/ClaimMappingEntity.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/ClaimMappingSearchCriteria.cs b/src/Modix.Data/Models/Core/ClaimMappingSearchCriteria.cs index 1079bfe4..8de04be1 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingSearchCriteria.cs +++ b/src/Modix.Data/Models/Core/ClaimMappingSearchCriteria.cs @@ -3,6 +3,7 @@ using Modix.Data.Repositories; using Modix.Data.Utilities; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/ClaimMappingSummary.cs b/src/Modix.Data/Models/Core/ClaimMappingSummary.cs index feb5aef5..c8e62671 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingSummary.cs +++ b/src/Modix.Data/Models/Core/ClaimMappingSummary.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Modix.Data.csproj b/src/Modix.Data/Modix.Data.csproj index 17148c12..1bb9bd3d 100644 --- a/src/Modix.Data/Modix.Data.csproj +++ b/src/Modix.Data/Modix.Data.csproj @@ -18,5 +18,6 @@ + \ No newline at end of file diff --git a/src/Modix.Data/Utilities/ClaimInfoAttribute.cs b/src/Modix.Data/Utilities/ClaimInfoAttribute.cs deleted file mode 100644 index cb00ffd6..00000000 --- a/src/Modix.Data/Utilities/ClaimInfoAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Modix.Data.Models.Core; - -namespace Modix.Data.Utilities -{ - [AttributeUsage(AttributeTargets.Field)] - public class ClaimInfoAttribute : Attribute - { - public AuthorizationClaimCategory Category { get; set; } - public string Description { get; set; } - - public ClaimInfoAttribute(AuthorizationClaimCategory category, string description) - { - Category = category; - Description = description; - } - } -} diff --git a/src/Modix.Services/AuthorizationClaimMappingService.cs b/src/Modix.Services/AuthorizationClaimMappingService.cs index f48169bd..dffac3f4 100644 --- a/src/Modix.Services/AuthorizationClaimMappingService.cs +++ b/src/Modix.Services/AuthorizationClaimMappingService.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Modix.Data; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Services; diff --git a/src/Modix.Services/AuthorizationClaimService.cs b/src/Modix.Services/AuthorizationClaimService.cs index 2ca4e624..134c32fc 100644 --- a/src/Modix.Services/AuthorizationClaimService.cs +++ b/src/Modix.Services/AuthorizationClaimService.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Modix.Data; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Services; diff --git a/src/Modix.Services/Core/AuthorizationService.cs b/src/Modix.Services/Core/AuthorizationService.cs index 90c4f994..6460802c 100644 --- a/src/Modix.Services/Core/AuthorizationService.cs +++ b/src/Modix.Services/Core/AuthorizationService.cs @@ -15,6 +15,7 @@ using Modix.Data.Models.Core; using Modix.Data.Repositories; using Modix.Services.Utilities; +using Modix.Models.Core; namespace Modix.Services.Core { diff --git a/src/Modix.Services/Core/DesignatedRoleService.cs b/src/Modix.Services/Core/DesignatedRoleService.cs index 0df61133..85327a9b 100644 --- a/src/Modix.Services/Core/DesignatedRoleService.cs +++ b/src/Modix.Services/Core/DesignatedRoleService.cs @@ -6,6 +6,7 @@ using Modix.Data.Models.Core; using Modix.Data.Repositories; +using Modix.Models.Core; namespace Modix.Services.Core { diff --git a/src/Modix.Services/DesignatedChannelService.cs b/src/Modix.Services/DesignatedChannelService.cs index 0343d33d..f706f320 100644 --- a/src/Modix.Services/DesignatedChannelService.cs +++ b/src/Modix.Services/DesignatedChannelService.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Modix.Data; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Services; diff --git a/src/Modix.Services/IScopedSession.cs b/src/Modix.Services/IScopedSession.cs index b79b02bc..bf16c5ea 100644 --- a/src/Modix.Services/IScopedSession.cs +++ b/src/Modix.Services/IScopedSession.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Services; diff --git a/src/Modix.Services/MessageContentPatterns/MessageContentPatternService.cs b/src/Modix.Services/MessageContentPatterns/MessageContentPatternService.cs index 1f7c026c..a9e61b0c 100644 --- a/src/Modix.Services/MessageContentPatterns/MessageContentPatternService.cs +++ b/src/Modix.Services/MessageContentPatterns/MessageContentPatternService.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Modix.Data; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Services.MessageContentPatterns diff --git a/src/Modix.Services/Moderation/GuildOnboardingService.cs b/src/Modix.Services/Moderation/GuildOnboardingService.cs index a64adec3..56fb57ac 100644 --- a/src/Modix.Services/Moderation/GuildOnboardingService.cs +++ b/src/Modix.Services/Moderation/GuildOnboardingService.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Modix.Data; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; using Serilog; diff --git a/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs b/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs index 42c3bdb1..77d17f3c 100644 --- a/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs +++ b/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs @@ -7,6 +7,7 @@ using Discord.WebSocket; using Modix.Common.Messaging; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; using Modix.Services.MessageContentPatterns; using Serilog; diff --git a/src/Modix.Services/Moderation/ModerationService.cs b/src/Modix.Services/Moderation/ModerationService.cs index 7dea9e2f..f9a7b453 100644 --- a/src/Modix.Services/Moderation/ModerationService.cs +++ b/src/Modix.Services/Moderation/ModerationService.cs @@ -1,19 +1,20 @@ #nullable enable using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Discord; +using Microsoft.EntityFrameworkCore; +using Modix.Data; using Modix.Data.Models; using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; using Modix.Data.Repositories; +using Modix.Models.Core; using Modix.Services.Core; using Modix.Services.Utilities; using Serilog; -using System.Threading; -using Microsoft.EntityFrameworkCore; -using Modix.Data; namespace Modix.Services.Moderation; diff --git a/src/Modix.Services/Promotions/PromotionsService.cs b/src/Modix.Services/Promotions/PromotionsService.cs index b0f45dff..e7e4394c 100644 --- a/src/Modix.Services/Promotions/PromotionsService.cs +++ b/src/Modix.Services/Promotions/PromotionsService.cs @@ -15,6 +15,7 @@ using Modix.Data.Models.Promotions; using Modix.Data.Repositories; using Modix.Data.Utilities; +using Modix.Models.Core; using Modix.Services.Core; using Modix.Services.Utilities; diff --git a/src/Modix.Services/Tags/TagInlineParsingHandler.cs b/src/Modix.Services/Tags/TagInlineParsingHandler.cs index 15bb9455..3f6e8f0b 100644 --- a/src/Modix.Services/Tags/TagInlineParsingHandler.cs +++ b/src/Modix.Services/Tags/TagInlineParsingHandler.cs @@ -9,6 +9,7 @@ using Modix.Common.Messaging; using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Services.Tags diff --git a/src/Modix.Services/Tags/TagService.cs b/src/Modix.Services/Tags/TagService.cs index 331786c4..13e097fd 100644 --- a/src/Modix.Services/Tags/TagService.cs +++ b/src/Modix.Services/Tags/TagService.cs @@ -13,6 +13,7 @@ using Modix.Data.Models.Core; using Modix.Data.Models.Tags; using Modix.Data.Repositories; +using Modix.Models.Core; using Modix.Services.Core; namespace Modix.Services.Tags diff --git a/src/Modix.Web/Components/Configuration/Claims.razor b/src/Modix.Web/Components/Configuration/Claims.razor index ddf87a3e..ceff0ea4 100644 --- a/src/Modix.Web/Components/Configuration/Claims.razor +++ b/src/Modix.Web/Components/Configuration/Claims.razor @@ -2,6 +2,7 @@ @using Modix.Data.Repositories; @using Modix.Data.Utilities; @using Modix.Models.Core; +@using Modix.Models.Utilities @using Modix.Web.Services; @using Modix.Web.Shared.Models.Common @using MudBlazor diff --git a/src/Modix.Web/Controllers/CampaignController.cs b/src/Modix.Web/Controllers/CampaignController.cs index eedde7f8..bfd86bd2 100644 --- a/src/Modix.Web/Controllers/CampaignController.cs +++ b/src/Modix.Web/Controllers/CampaignController.cs @@ -1,9 +1,8 @@ -using Discord; -using Discord.WebSocket; +using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Promotions; using Modix.Services.Utilities; using Modix.Web.Shared.Models.Promotions; diff --git a/src/Modix.Web/Controllers/DeletedMessagesController.cs b/src/Modix.Web/Controllers/DeletedMessagesController.cs index 92b0d82c..18c5d26e 100644 --- a/src/Modix.Web/Controllers/DeletedMessagesController.cs +++ b/src/Modix.Web/Controllers/DeletedMessagesController.cs @@ -5,8 +5,8 @@ using Microsoft.AspNetCore.Mvc; using Modix.Controllers; using Modix.Data.Models; -using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; +using Modix.Models.Core; using Modix.Services.Moderation; using Modix.Services.Utilities; using Modix.Web.Shared.Models.DeletedMessages; diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs index 848935eb..d2be33a0 100644 --- a/src/Modix.Web/Controllers/DesignatedChannelController.cs +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services; using Modix.Web.Shared.Models.Configuration; diff --git a/src/Modix.Web/Controllers/DesignatedRoleController.cs b/src/Modix.Web/Controllers/DesignatedRoleController.cs index 1c26a3a1..3333e0c0 100644 --- a/src/Modix.Web/Controllers/DesignatedRoleController.cs +++ b/src/Modix.Web/Controllers/DesignatedRoleController.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; using Modix.Web.Shared.Models.Configuration; diff --git a/src/Modix.Web/Controllers/InfractionsController.cs b/src/Modix.Web/Controllers/InfractionsController.cs index 3435c8cf..43212ff8 100644 --- a/src/Modix.Web/Controllers/InfractionsController.cs +++ b/src/Modix.Web/Controllers/InfractionsController.cs @@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Mvc; using Modix.Controllers; using Modix.Data.Models; -using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; +using Modix.Models.Core; using Modix.Services.Moderation; using Modix.Web.Shared.Models.Infractions; using MudBlazor; diff --git a/src/Modix.Web/Controllers/TagsController.cs b/src/Modix.Web/Controllers/TagsController.cs index 90e4166d..5181924c 100644 --- a/src/Modix.Web/Controllers/TagsController.cs +++ b/src/Modix.Web/Controllers/TagsController.cs @@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modix.Controllers; -using Modix.Data.Models.Core; using Modix.Data.Models.Tags; +using Modix.Models.Core; using Modix.Services.Tags; using Modix.Web.Shared.Models.Tags; diff --git a/test/Modix.Data.Test/TestData/ClaimMappings.cs b/test/Modix.Data.Test/TestData/ClaimMappings.cs index 5a66f8b5..6e7df907 100644 --- a/test/Modix.Data.Test/TestData/ClaimMappings.cs +++ b/test/Modix.Data.Test/TestData/ClaimMappings.cs @@ -4,6 +4,7 @@ using Modix.Data.Models; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Data.Test.TestData { From d7054f2905fe52b3c28238e778e3c5758ff1d073 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:19:24 +0200 Subject: [PATCH 33/46] Move Configuration/Claims page over to the wasm project --- src/Modix.Data/Models/Core/ClaimInfoData.cs | 37 ---------- src/Modix.Models/Core/ClaimInfoData.cs | 34 ++++++++++ .../Core/ClaimMappingType.cs | 2 +- .../Models/Configuration/ClaimMappingData.cs | 5 ++ src/Modix.Web.Shared/Modix.Web.Shared.csproj | 4 ++ .../Components/Configuration/Claims.razor | 68 ++++++------------- src/Modix.Web.Wasm/Pages/Configuration.razor | 5 +- src/Modix.Web/Controllers/ClaimsController.cs | 43 ++++++++++++ 8 files changed, 110 insertions(+), 88 deletions(-) delete mode 100644 src/Modix.Data/Models/Core/ClaimInfoData.cs create mode 100644 src/Modix.Models/Core/ClaimInfoData.cs rename src/{Modix.Data/Models => Modix.Models}/Core/ClaimMappingType.cs (92%) create mode 100644 src/Modix.Web.Shared/Models/Configuration/ClaimMappingData.cs rename src/{Modix.Web => Modix.Web.Wasm}/Components/Configuration/Claims.razor (70%) create mode 100644 src/Modix.Web/Controllers/ClaimsController.cs diff --git a/src/Modix.Data/Models/Core/ClaimInfoData.cs b/src/Modix.Data/Models/Core/ClaimInfoData.cs deleted file mode 100644 index 80b8a257..00000000 --- a/src/Modix.Data/Models/Core/ClaimInfoData.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Modix.Models.Utilities; - -namespace Modix.Models.Core -{ - public class ClaimInfoData - { - public string Name { get; set; } = null!; - public string Description { get; set; } = null!; - public AuthorizationClaimCategory Category { get; set; } - - private static Dictionary? _cachedClaimData; - - public static Dictionary GetClaims() - { - _cachedClaimData ??= typeof(AuthorizationClaim).GetFields(BindingFlags.Public | BindingFlags.Static).ToDictionary - ( - d => (AuthorizationClaim)d.GetValue(null)!, - d => - { - var claimInfo = (ClaimInfoAttribute)d.GetCustomAttributes(typeof(ClaimInfoAttribute), true).First()!; - - return new ClaimInfoData - { - Name = d.Name, - Description = claimInfo.Description, - Category = claimInfo.Category - }; - } - ); - - return _cachedClaimData; - } - } -} diff --git a/src/Modix.Models/Core/ClaimInfoData.cs b/src/Modix.Models/Core/ClaimInfoData.cs new file mode 100644 index 00000000..21ab5d97 --- /dev/null +++ b/src/Modix.Models/Core/ClaimInfoData.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using Modix.Models.Utilities; + +namespace Modix.Models.Core; + +public class ClaimInfoData +{ + public string Name { get; set; } = null!; + public string Description { get; set; } = null!; + public AuthorizationClaimCategory Category { get; set; } + + private static Dictionary? _cachedClaimData; + + public static Dictionary GetClaims() + { + _cachedClaimData ??= typeof(AuthorizationClaim).GetFields(BindingFlags.Public | BindingFlags.Static).ToDictionary + ( + d => (AuthorizationClaim)d.GetValue(null)!, + d => + { + var claimInfo = (ClaimInfoAttribute)d.GetCustomAttributes(typeof(ClaimInfoAttribute), true).First()!; + + return new ClaimInfoData + { + Name = d.Name, + Description = claimInfo.Description, + Category = claimInfo.Category + }; + } + ); + + return _cachedClaimData; + } +} diff --git a/src/Modix.Data/Models/Core/ClaimMappingType.cs b/src/Modix.Models/Core/ClaimMappingType.cs similarity index 92% rename from src/Modix.Data/Models/Core/ClaimMappingType.cs rename to src/Modix.Models/Core/ClaimMappingType.cs index 4c34f635..48298e22 100644 --- a/src/Modix.Data/Models/Core/ClaimMappingType.cs +++ b/src/Modix.Models/Core/ClaimMappingType.cs @@ -1,4 +1,4 @@ -namespace Modix.Data.Models.Core +namespace Modix.Models.Core { /// /// Defines the possible types of claim mappings. diff --git a/src/Modix.Web.Shared/Models/Configuration/ClaimMappingData.cs b/src/Modix.Web.Shared/Models/Configuration/ClaimMappingData.cs new file mode 100644 index 00000000..6405ce1d --- /dev/null +++ b/src/Modix.Web.Shared/Models/Configuration/ClaimMappingData.cs @@ -0,0 +1,5 @@ +using Modix.Models.Core; + +namespace Modix.Web.Shared.Models.Configuration; + +public record class ClaimMappingData(ulong? RoleId, AuthorizationClaim Claim, ClaimMappingType Type); diff --git a/src/Modix.Web.Shared/Modix.Web.Shared.csproj b/src/Modix.Web.Shared/Modix.Web.Shared.csproj index fa71b7ae..c13714a1 100644 --- a/src/Modix.Web.Shared/Modix.Web.Shared.csproj +++ b/src/Modix.Web.Shared/Modix.Web.Shared.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/src/Modix.Web/Components/Configuration/Claims.razor b/src/Modix.Web.Wasm/Components/Configuration/Claims.razor similarity index 70% rename from src/Modix.Web/Components/Configuration/Claims.razor rename to src/Modix.Web.Wasm/Components/Configuration/Claims.razor index ceff0ea4..46ca4105 100644 --- a/src/Modix.Web/Components/Configuration/Claims.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Claims.razor @@ -1,13 +1,10 @@ -@using Modix.Data.Models.Core; -@using Modix.Data.Repositories; -@using Modix.Data.Utilities; -@using Modix.Models.Core; +@using Modix.Models.Core; @using Modix.Models.Utilities -@using Modix.Web.Services; @using Modix.Web.Shared.Models.Common @using MudBlazor @using System.Reflection; @using Humanizer; +@using Modix.Web.Shared.Models.Configuration; Modix - Claims @@ -76,19 +73,13 @@ @code { [Inject] - public IClaimMappingRepository ClaimMappingRepository { get; set; } = null!; + public required IHttpClientFactory HttpClientFactory { get; set; } [Inject] - public DiscordHelper DiscordHelper { get; set; } = null!; - - [Inject] - public Modix.Services.Core.IAuthorizationService AuthorizationService { get; set; } = null!; - - [Inject] - public ISnackbar Snackbar { get; set; } = null!; + public required ISnackbar Snackbar { get; set; } private Dictionary? ClaimData { get; set; } - private Dictionary<(ulong?, AuthorizationClaim), ClaimMappingBrief>? MappedClaims { get; set; } + private Dictionary<(ulong?, AuthorizationClaim), ClaimMappingData>? MappedClaims { get; set; } private Dictionary? Roles { get; set; } private ulong? _selectedRole; @@ -98,37 +89,18 @@ if (!firstRender) return; - ClaimData = typeof(AuthorizationClaim).GetFields(BindingFlags.Public | BindingFlags.Static).ToDictionary - ( - d => (AuthorizationClaim)d.GetValue(null)!, - d => - { - var claimInfo = (ClaimInfoAttribute)d.GetCustomAttributes(typeof(ClaimInfoAttribute), true).First()!; + ClaimData = ClaimInfoData.GetClaims(); - return new ClaimInfoData - { - Name = d.Name, - Description = claimInfo.Description, - Category = claimInfo.Category - }; - } - ); + using var client = HttpClientFactory.CreateClient("api"); - var currentGuild = DiscordHelper.GetUserGuild(); - var mappedClaims = await ClaimMappingRepository.SearchBriefsAsync(new ClaimMappingSearchCriteria - { - IsDeleted = false, - GuildId = currentGuild.Id - }); + var rolesTask = client.GetFromJsonAsync>("api/roles"); - MappedClaims = mappedClaims.ToDictionary(x => (x.RoleId, x.Claim), x => x); + var mappedClaims = await client.GetFromJsonAsync("api/config/claims"); + MappedClaims = mappedClaims?.ToDictionary(x => (x.RoleId, x.Claim), x => x); - Roles = currentGuild.Roles - .Select(d => new RoleInformation(d.Id, d.Name, d.Color.ToString())) - .OrderBy(x => x.Name) - .ToDictionary(x => x.Id, x => x); + Roles = await rolesTask; - _selectedRole = Roles.First().Key; + _selectedRole = Roles?.First().Key; StateHasChanged(); } @@ -139,22 +111,22 @@ if (MappedClaims!.TryGetValue(key, out var claimMapping) && claimMapping.Type == claimMappingType) return; - await AuthorizationService.ModifyClaimMappingAsync(roleId, authorizationClaim, claimMappingType); + using var client = HttpClientFactory.CreateClient("api"); + using var response = await client.PatchAsync($"api/config/claims/{roleId}/{authorizationClaim}/{claimMappingType}", default); + + response.EnsureSuccessStatusCode(); + if (claimMappingType is ClaimMappingType.Denied or ClaimMappingType.Granted) { Snackbar.Add($"Claim '{authorizationClaim}' for '{Roles![roleId].Name}' was changed to '{claimMappingType}'", Severity.Success); if (claimMapping is null) { - MappedClaims[key] = new ClaimMappingBrief - { - Claim = authorizationClaim, - RoleId = roleId, - Type = claimMappingType.Value - }; + MappedClaims[key] = new ClaimMappingData(roleId, authorizationClaim, claimMappingType.Value); } else { - claimMapping.Type = claimMappingType.Value; + var clone = claimMapping with { Type = claimMappingType.Value }; + MappedClaims[key] = clone; } } else diff --git a/src/Modix.Web.Wasm/Pages/Configuration.razor b/src/Modix.Web.Wasm/Pages/Configuration.razor index 5d8516d3..fd6cf629 100644 --- a/src/Modix.Web.Wasm/Pages/Configuration.razor +++ b/src/Modix.Web.Wasm/Pages/Configuration.razor @@ -53,9 +53,10 @@ } else if (SubPage == "claims") { -@* + @* *@ + - *@ + }
diff --git a/src/Modix.Web/Controllers/ClaimsController.cs b/src/Modix.Web/Controllers/ClaimsController.cs new file mode 100644 index 00000000..1ee85c87 --- /dev/null +++ b/src/Modix.Web/Controllers/ClaimsController.cs @@ -0,0 +1,43 @@ +using Discord.WebSocket; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Modix.Controllers; +using Modix.Data.Models.Core; +using Modix.Data.Repositories; +using Modix.Models.Core; +using Modix.Web.Shared.Models.Configuration; + +namespace Modix.Web.Controllers; + +[Route("~/api/config/claims")] +[ApiController] +[Authorize(Roles = nameof(AuthorizationClaim.AuthorizationConfigure))] +public class ClaimsController : ModixController +{ + private readonly IClaimMappingRepository _claimMappingRepository; + + public ClaimsController(IClaimMappingRepository claimMappingRepository, DiscordSocketClient discordSocketClient, Modix.Services.Core.IAuthorizationService authorizationService) + : base(discordSocketClient, authorizationService) + { + _claimMappingRepository = claimMappingRepository; + } + + [HttpGet] + public async Task> GetMappedClaimsAsync() + { + var mappedClaims = await _claimMappingRepository.SearchBriefsAsync(new ClaimMappingSearchCriteria + { + IsDeleted = false, + GuildId = UserGuild.Id + }); + + return mappedClaims + .Select(x => new ClaimMappingData(x.RoleId, x.Claim, x.Type)); + } + + [HttpPatch("{roleId}/{authorizationClaim}/{claimMappingType?}")] + public async Task ModifyClaimMappingAsync(ulong roleId, AuthorizationClaim authorizationClaim, ClaimMappingType? claimMappingType) + { + await ModixAuth.ModifyClaimMappingAsync(roleId, authorizationClaim, claimMappingType); + } +} From b2803946db9f5bbf8140d8aefbeb767e976bde88 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:47:44 +0200 Subject: [PATCH 34/46] Remove/fix TODOs --- .../Components/Configuration/Channels.razor | 9 ++--- .../Components/Configuration/Roles.razor | 8 ++-- .../Components/Infractions/Infractions.razor | 39 +++++++------------ src/Modix.Web.Wasm/Components/NavMenu.razor | 7 ++-- .../Components/NavMenuLinks.razor | 10 ++--- src/Modix.Web.Wasm/Pages/Configuration.razor | 21 +++------- .../Pages/CreatePromotion.razor | 5 +-- src/Modix.Web.Wasm/Pages/Logs.razor | 11 ++---- src/Modix.Web.Wasm/Pages/Promotions.razor | 22 +++++------ src/Modix.Web.Wasm/Pages/Tags.razor | 5 +-- src/Modix.Web.Wasm/Pages/UserLookup.razor | 4 -- src/Modix.Web.Wasm/Routes.razor | 4 +- 12 files changed, 54 insertions(+), 91 deletions(-) diff --git a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor index 67746db1..59a72156 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor @@ -1,4 +1,5 @@ @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Shared.Models.Configuration @using Modix.Web.Wasm.Components @using MudBlazor @@ -61,9 +62,7 @@ { @foreach (var designatedChannelMapping in channelDesignations) { - @*TODO*@ - @*
- @*TODO*@ - @* *@ - +
diff --git a/src/Modix.Web.Wasm/Components/Configuration/Roles.razor b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor index 9b89de09..947c8efe 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/Roles.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor @@ -1,5 +1,6 @@ @using Humanizer @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Configuration @using MudBlazor @@ -62,9 +63,7 @@ { @foreach (var designatedRoleMapping in roleDesignations) { - @* TODO *@ - @*
- @* *@ - +
diff --git a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index 81aff8cc..98a13023 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -1,7 +1,9 @@ @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Models; @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Infractions +@using Modix.Web.Shared.Services @using Modix.Web.Wasm.Components @using MudBlazor; @using System.Security.Claims; @@ -30,21 +32,16 @@
- @*TODO *@ - @* *@ - + - @* *@ - + - @* *@ - + - @* *@ - + @@ -191,13 +188,13 @@ public required IDialogService DialogService { get; set; } [Inject] - public required SessionState SessionState { get; set; } + public required IHttpClientFactory HttpClientFactory { get; set; } [Inject] - public required IHttpClientFactory HttpClientFactory { get; set; } + public required ICookieService CookieService { get; set; } - // [Inject] - // public CookieService CookieService { get; set; } = null!; + [CascadingParameter] + public required SessionState SessionState { get; set; } [CascadingParameter] private Task? AuthState { get; set; } @@ -237,9 +234,8 @@ var auth = await AuthState; - // TODO: - // _canRescind = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationRescind)); - // _canDeleteInfractions = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationDeleteInfraction)); + _canRescind = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationRescind)); + _canDeleteInfractions = auth.User.HasClaim(ClaimTypes.Role, nameof(AuthorizationClaim.ModerationDeleteInfraction)); _tableFilter.Subject = Subject; _tableFilter.IdString = Id; @@ -248,15 +244,13 @@ private async Task ShowStateChanged(bool showState) { _showState = showState; - // TODO: - // await CookieService.SetShowInfractionStateAsync(showState); + await CookieService.SetShowInfractionStateAsync(showState); } private async Task ShowDeletedChanged(bool showDeleted) { await FilterChanged(() => _tableFilter.ShowDeleted = showDeleted); - //TODO: - // await CookieService.SetShowDeletedInfractionsAsync(showDeleted); + await CookieService.SetShowDeletedInfractionsAsync(showDeleted); } private void ToggleDialog() @@ -275,11 +269,6 @@ await RefreshTable(); } - // private static string GetUsername(GuildUserBrief userBrief) - // { - // return $"{userBrief.Username}{(userBrief.Discriminator == "0000" ? "" : "#" + userBrief.Discriminator)}"; - // } - private async Task RescindInfraction(InfractionData infraction) { try diff --git a/src/Modix.Web.Wasm/Components/NavMenu.razor b/src/Modix.Web.Wasm/Components/NavMenu.razor index 0b77eac6..b5ef812a 100644 --- a/src/Modix.Web.Wasm/Components/NavMenu.razor +++ b/src/Modix.Web.Wasm/Components/NavMenu.razor @@ -1,5 +1,6 @@ @using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models; +@using Modix.Web.Shared.Services @using MudBlazor; @using System.Security.Claims; @@ -36,8 +37,8 @@ @code { - // [Inject] - // public required CookieService CookieService { get; set; } + [Inject] + public required ICookieService CookieService { get; set; } [Parameter] public bool DarkMode { get; set; } @@ -53,6 +54,6 @@ { DarkMode = toggled; await DarkModeChanged.InvokeAsync(DarkMode); - // await CookieService.SetUseDarkModeAsync(DarkMode); + await CookieService.SetUseDarkModeAsync(DarkMode); } } diff --git a/src/Modix.Web.Wasm/Components/NavMenuLinks.razor b/src/Modix.Web.Wasm/Components/NavMenuLinks.razor index bed8a9ab..53f467cd 100644 --- a/src/Modix.Web.Wasm/Components/NavMenuLinks.razor +++ b/src/Modix.Web.Wasm/Components/NavMenuLinks.razor @@ -1,4 +1,5 @@ @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Models; @using MudBlazor @@ -10,18 +11,15 @@ User Lookup Tags - @* *@ - + Promotions - @* *@ - + Logs - @* *@ - + Config diff --git a/src/Modix.Web.Wasm/Pages/Configuration.razor b/src/Modix.Web.Wasm/Pages/Configuration.razor index fd6cf629..a4ae3360 100644 --- a/src/Modix.Web.Wasm/Pages/Configuration.razor +++ b/src/Modix.Web.Wasm/Pages/Configuration.razor @@ -1,21 +1,15 @@ @page "/config" @page "/config/{SubPage}" -@* TODO *@ -@* @attribute [Authorize( +@attribute [Authorize( Roles = $@" {nameof(AuthorizationClaim.DesignatedRoleMappingRead)}, {nameof(AuthorizationClaim.DesignatedChannelMappingRead)}, - {nameof(AuthorizationClaim.AuthorizationConfigure)}")] *@ - -@attribute [Authorize( - Roles = $@" - DesignatedRoleMappingRead, - DesignatedChannelMappingRead, - AuthorizationConfigure")] + {nameof(AuthorizationClaim.AuthorizationConfigure)}")] @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Wasm.Components.Configuration @using MudBlazor @@ -39,22 +33,19 @@
@if (SubPage == "roles") { - @* *@ - + } else if (SubPage == "channels") { - @* *@ - + } else if (SubPage == "claims") { - @* *@ - + } diff --git a/src/Modix.Web.Wasm/Pages/CreatePromotion.razor b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor index d54cfc83..35e51651 100644 --- a/src/Modix.Web.Wasm/Pages/CreatePromotion.razor +++ b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor @@ -1,14 +1,13 @@ @page "/promotions/create" @using Microsoft.AspNetCore.Authorization +@using Modix.Models.Core @using Modix.Web.Models; @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Promotions @using Modix.Web.Wasm.Components @using MudBlazor -@*TODO*@ -@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] *@ -@attribute [Authorize(Roles = "PromotionsCreateCampaign")] +@attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] Modix - Start A Campaign diff --git a/src/Modix.Web.Wasm/Pages/Logs.razor b/src/Modix.Web.Wasm/Pages/Logs.razor index f74cfdfc..6405a40f 100644 --- a/src/Modix.Web.Wasm/Pages/Logs.razor +++ b/src/Modix.Web.Wasm/Pages/Logs.razor @@ -2,12 +2,11 @@ @page "/logs" @page "/infractions" -@* TODO *@ -@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] *@ -@attribute [Authorize(Roles = "ModerationRead")] +@attribute [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Wasm.Components.Infractions @using MudBlazor @@ -29,15 +28,13 @@
@if (SubPage == "infractions") { - @* *@ - + } else if (SubPage == "deletedMessages") { - @* TODO *@ - + } diff --git a/src/Modix.Web.Wasm/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor index 8dc22e14..1bd35f4f 100644 --- a/src/Modix.Web.Wasm/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -1,14 +1,14 @@ @page "/promotions" -@* TODO *@ -@* @attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsRead))] *@ -@attribute [Authorize(Roles = "PromotionsRead")] +@attribute [Authorize(Roles = nameof(AuthorizationClaim.PromotionsRead))] @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Models; @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Promotions +@using Modix.Web.Shared.Services @using Modix.Web.Wasm.Components @using MudBlazor @using Humanizer; @@ -64,9 +64,7 @@
@if (campaign.Outcome is null) { - @* TODO *@ - @* *@ - + @@ -160,18 +158,21 @@ @code { - [Inject] - public required SessionState SessionState { get; set; } - [Inject] public required IDialogService DialogService { get; set; } [Inject] public required ISnackbar Snackbar { get; set; } + [Inject] + public required ICookieService CookieService { get; set; } + [Inject] public required IHttpClientFactory HttpClientFactory { get; set; } + [CascadingParameter] + public required SessionState SessionState { get; set; } + [CascadingParameter] public required Task AuthenticationState { get; set; } @@ -217,8 +218,7 @@ private async Task ShowInactiveChanged(bool showInactive) { _showInactive = showInactive; - //TODO - // await CookieService.SetShowInactivePromotionsAsync(showInactive); + await CookieService.SetShowInactivePromotionsAsync(showInactive); } private async Task CampaignExpanded(bool wasExpanded, long campaignId, ulong userId) diff --git a/src/Modix.Web.Wasm/Pages/Tags.razor b/src/Modix.Web.Wasm/Pages/Tags.razor index 5c91bbb1..d8a77c25 100644 --- a/src/Modix.Web.Wasm/Pages/Tags.razor +++ b/src/Modix.Web.Wasm/Pages/Tags.razor @@ -1,6 +1,7 @@ @page "/tags" @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization +@using Modix.Models.Core @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Tags @using MudBlazor @@ -38,9 +39,7 @@
- @* TODO *@ - @* *@ - + Create Refresh diff --git a/src/Modix.Web.Wasm/Pages/UserLookup.razor b/src/Modix.Web.Wasm/Pages/UserLookup.razor index a2db2051..f2379735 100644 --- a/src/Modix.Web.Wasm/Pages/UserLookup.razor +++ b/src/Modix.Web.Wasm/Pages/UserLookup.razor @@ -124,10 +124,6 @@ if (!firstRender) return; - //TODO: add an endpoint for this? - // var currentUser = DiscordHelper.GetCurrentUser(); - // await SelectedUserChanged(ModixUser.FromIGuildUser(currentUser!)); - var authState = await AuthState; var userId = authState.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; diff --git a/src/Modix.Web.Wasm/Routes.razor b/src/Modix.Web.Wasm/Routes.razor index afa832b2..4848d944 100644 --- a/src/Modix.Web.Wasm/Routes.razor +++ b/src/Modix.Web.Wasm/Routes.razor @@ -1,6 +1,4 @@ -@* @using Modix.Web.Models -@using Modix.Web.Services *@ -@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Modix.Web.Models @using Modix.Web.Wasm.Components @using Modix.Web.Wasm.Shared From c7475f48a74e5282dda8c959c13c70e68c22fe4e Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:09:31 +0200 Subject: [PATCH 35/46] Remove SelectedGuild from Routes params in favor of using user claim --- src/Modix.Web.Shared/Models/DiscordUser.cs | 1 + src/Modix.Web.Wasm/Routes.razor | 6 +----- .../Security/PersistentAuthenticationStateProvider.cs | 4 +++- src/Modix.Web/App.razor | 1 - .../Security/PersistingAuthenticationStateProvider.cs | 1 + 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Modix.Web.Shared/Models/DiscordUser.cs b/src/Modix.Web.Shared/Models/DiscordUser.cs index c9cd8176..e1635bc6 100644 --- a/src/Modix.Web.Shared/Models/DiscordUser.cs +++ b/src/Modix.Web.Shared/Models/DiscordUser.cs @@ -5,5 +5,6 @@ public class DiscordUser public required ulong UserId { get; init; } public required string Name { get; init; } public required string AvatarHash { get; init; } + public required ulong CurrentGuild { get; init; } public required IEnumerable Claims { get; set; } } diff --git a/src/Modix.Web.Wasm/Routes.razor b/src/Modix.Web.Wasm/Routes.razor index 4848d944..107d1e10 100644 --- a/src/Modix.Web.Wasm/Routes.razor +++ b/src/Modix.Web.Wasm/Routes.razor @@ -24,10 +24,6 @@ @code { - - [Parameter] - public string? SelectedGuild { get; set; } - [Parameter] public string? ShowInfractionState { get; set; } @@ -71,7 +67,7 @@ var currentGuild = authState.User.FindFirst(ClaimTypes.PostalCode)?.Value; _ = ulong.TryParse(userId, out var userSnowflake); - _ = ulong.TryParse(currentGuild ?? SelectedGuild, out var selectedGuildId); + _ = ulong.TryParse(currentGuild, out var selectedGuildId); _ = bool.TryParse(ShowInfractionState, out var showInfractionState); _ = bool.TryParse(ShowDeletedInfractions, out var showDeletedInfractions); _ = bool.TryParse(ShowInactivePromotions, out var showInactivePromotions); diff --git a/src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs b/src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs index b8ffdbc7..fe74666c 100644 --- a/src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs +++ b/src/Modix.Web.Wasm/Security/PersistentAuthenticationStateProvider.cs @@ -19,7 +19,9 @@ public PersistentAuthenticationStateProvider(PersistentComponentState state) Claim[] claims = [ new Claim(ClaimTypes.NameIdentifier, userInfo.UserId.ToString()), new Claim(ClaimTypes.Name, userInfo.Name), - new Claim(nameof(DiscordUser.AvatarHash), userInfo.AvatarHash)]; + new Claim(nameof(DiscordUser.AvatarHash), userInfo.AvatarHash), + new Claim(ClaimTypes.PostalCode, userInfo.CurrentGuild.ToString()) + ]; var roles = userInfo.Claims.Select(role => new Claim(ClaimTypes.Role, role)); diff --git a/src/Modix.Web/App.razor b/src/Modix.Web/App.razor index f8dd74b2..1c814fa4 100644 --- a/src/Modix.Web/App.razor +++ b/src/Modix.Web/App.razor @@ -25,7 +25,6 @@ x.Type == ClaimTypes.Role).Select(x => x.Value) }); } From f316322ead1a18d94aede42a519124493827e3e1 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:11:59 +0200 Subject: [PATCH 36/46] Disable SSR for now --- src/Modix.Web/App.razor | 4 ++-- src/Modix.Web/Setup.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Modix.Web/App.razor b/src/Modix.Web/App.razor index 1c814fa4..2fe181a4 100644 --- a/src/Modix.Web/App.razor +++ b/src/Modix.Web/App.razor @@ -20,11 +20,11 @@ - + - await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" })); app.MapRazorComponents() - .AddInteractiveServerRenderMode() + //.AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Wasm._Imports).Assembly); @@ -76,7 +76,7 @@ public static IServiceCollection ConfigureBlazorServices(this IServiceCollection services .AddRazorComponents() - .AddInteractiveServerComponents() + //.AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); return services; From 3812d54c09dc8be523f953aaaa1599f95b2426de Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:20:43 +0200 Subject: [PATCH 37/46] Fixup authorization in controllers --- src/Modix.Web/Controllers/AutocompleteController.cs | 2 -- src/Modix.Web/Controllers/CampaignController.cs | 5 +++-- src/Modix.Web/Controllers/ClaimsController.cs | 1 - .../Controllers/DeletedMessagesController.cs | 1 - .../Controllers/DesignatedChannelController.cs | 2 -- .../Controllers/DesignatedRoleController.cs | 1 - src/Modix.Web/Controllers/GuildController.cs | 1 - src/Modix.Web/Controllers/GuildStatsController.cs | 1 - src/Modix.Web/Controllers/InfractionsController.cs | 1 - src/Modix.Web/Controllers/ModixController.cs | 12 +++++------- src/Modix.Web/Controllers/RolesController.cs | 1 - src/Modix.Web/Controllers/TagsController.cs | 3 +-- .../Controllers/UserInformationController.cs | 2 -- 13 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/Modix.Web/Controllers/AutocompleteController.cs b/src/Modix.Web/Controllers/AutocompleteController.cs index ed489ce8..b97ef3f0 100644 --- a/src/Modix.Web/Controllers/AutocompleteController.cs +++ b/src/Modix.Web/Controllers/AutocompleteController.cs @@ -1,7 +1,6 @@ using Discord; using Discord.WebSocket; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Utilities; using Modix.Services.Core; using Modix.Services.Utilities; @@ -21,7 +20,6 @@ public AutocompleteController(DiscordSocketClient discordSocketClient, IUserServ _userService = userService; } - [HttpGet("users/{query}")] public async Task> AutocompleteUsersAsync(string query) { diff --git a/src/Modix.Web/Controllers/CampaignController.cs b/src/Modix.Web/Controllers/CampaignController.cs index bfd86bd2..9d3fbe9a 100644 --- a/src/Modix.Web/Controllers/CampaignController.cs +++ b/src/Modix.Web/Controllers/CampaignController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Models.Core; using Modix.Services.Promotions; using Modix.Services.Utilities; @@ -11,7 +10,7 @@ namespace Modix.Web.Controllers; [Route("~/api/campaigns")] [ApiController] -[Authorize] +[Authorize(Roles = nameof(AuthorizationClaim.PromotionsRead))] public class CampaignController : ModixController { private readonly IPromotionsService _promotionsService; @@ -117,6 +116,7 @@ public async Task RejectCampaignAsync(long campaignId) } [HttpGet("{subjectId}/nextrank")] + [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] public async Task GetNextRankRoleForUserAsync(ulong subjectId) { var nextRank = await _promotionsService.GetNextRankRoleForUserAsync(subjectId); @@ -129,6 +129,7 @@ public async Task GetNextRankRoleForUserAsync(ulong subjectId) } [HttpPut("create")] + [Authorize(Roles = nameof(AuthorizationClaim.PromotionsCreateCampaign))] public async Task CreateAsync([FromBody] PromotionCreationData creationData) { await _promotionsService.CreateCampaignAsync(creationData.UserId, creationData.Comment); diff --git a/src/Modix.Web/Controllers/ClaimsController.cs b/src/Modix.Web/Controllers/ClaimsController.cs index 1ee85c87..3b0ae1c5 100644 --- a/src/Modix.Web/Controllers/ClaimsController.cs +++ b/src/Modix.Web/Controllers/ClaimsController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Models.Core; using Modix.Data.Repositories; using Modix.Models.Core; diff --git a/src/Modix.Web/Controllers/DeletedMessagesController.cs b/src/Modix.Web/Controllers/DeletedMessagesController.cs index 18c5d26e..0014fa0f 100644 --- a/src/Modix.Web/Controllers/DeletedMessagesController.cs +++ b/src/Modix.Web/Controllers/DeletedMessagesController.cs @@ -3,7 +3,6 @@ using Humanizer.Bytes; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Models; using Modix.Data.Models.Moderation; using Modix.Models.Core; diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs index d2be33a0..054e85c9 100644 --- a/src/Modix.Web/Controllers/DesignatedChannelController.cs +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Models.Core; using Modix.Services; using Modix.Web.Shared.Models.Configuration; @@ -21,7 +20,6 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer _designatedChannelService = designatedChannelService; } - [HttpGet] [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingRead))] public async Task>> GetChannelDesignationsAsync() diff --git a/src/Modix.Web/Controllers/DesignatedRoleController.cs b/src/Modix.Web/Controllers/DesignatedRoleController.cs index 3333e0c0..8bf94320 100644 --- a/src/Modix.Web/Controllers/DesignatedRoleController.cs +++ b/src/Modix.Web/Controllers/DesignatedRoleController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Models.Core; using Modix.Services.Core; using Modix.Web.Shared.Models.Configuration; diff --git a/src/Modix.Web/Controllers/GuildController.cs b/src/Modix.Web/Controllers/GuildController.cs index 017d44e2..b0bb7ec7 100644 --- a/src/Modix.Web/Controllers/GuildController.cs +++ b/src/Modix.Web/Controllers/GuildController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Web.Shared.Models; namespace Modix.Web.Controllers; diff --git a/src/Modix.Web/Controllers/GuildStatsController.cs b/src/Modix.Web/Controllers/GuildStatsController.cs index 1420b790..f2ca49b9 100644 --- a/src/Modix.Web/Controllers/GuildStatsController.cs +++ b/src/Modix.Web/Controllers/GuildStatsController.cs @@ -2,7 +2,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Services.Core; using Modix.Services.GuildStats; using Modix.Web.Models; diff --git a/src/Modix.Web/Controllers/InfractionsController.cs b/src/Modix.Web/Controllers/InfractionsController.cs index 43212ff8..d1950be5 100644 --- a/src/Modix.Web/Controllers/InfractionsController.cs +++ b/src/Modix.Web/Controllers/InfractionsController.cs @@ -1,7 +1,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Models; using Modix.Data.Models.Moderation; using Modix.Models.Core; diff --git a/src/Modix.Web/Controllers/ModixController.cs b/src/Modix.Web/Controllers/ModixController.cs index fa8f7751..c745f921 100644 --- a/src/Modix.Web/Controllers/ModixController.cs +++ b/src/Modix.Web/Controllers/ModixController.cs @@ -1,5 +1,4 @@ -#nullable enable -using System.Security.Claims; +using System.Security.Claims; using Discord.WebSocket; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; @@ -7,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Modix.Web.Models; -namespace Modix.Controllers; +namespace Modix.Web.Controllers; [Authorize] public class ModixController : Controller @@ -16,15 +15,14 @@ public class ModixController : Controller protected SocketGuildUser SocketUser { get; private set; } protected SocketGuild UserGuild => SocketUser.Guild; - protected Services.Core.IAuthorizationService ModixAuth { get; private set; } + protected Modix.Services.Core.IAuthorizationService ModixAuth { get; private set; } - public ModixController(DiscordSocketClient client, Services.Core.IAuthorizationService modixAuth) + public ModixController(DiscordSocketClient client, Modix.Services.Core.IAuthorizationService modixAuth) { DiscordSocketClient = client; ModixAuth = modixAuth; } - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { if (!DiscordSocketClient.Guilds.Any()) @@ -64,7 +62,7 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context return; } - await ModixAuth.OnAuthenticatedAsync(SocketUser.Id, SocketUser.Guild.Id, [.. SocketUser.Roles.Select(x => x.Id) ]); + await ModixAuth.OnAuthenticatedAsync(SocketUser.Id, SocketUser.Guild.Id, [.. SocketUser.Roles.Select(x => x.Id)]); await next(); } diff --git a/src/Modix.Web/Controllers/RolesController.cs b/src/Modix.Web/Controllers/RolesController.cs index 97a95f98..36b25b92 100644 --- a/src/Modix.Web/Controllers/RolesController.cs +++ b/src/Modix.Web/Controllers/RolesController.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; -using Modix.Controllers; using Modix.Web.Shared.Models.Common; namespace Modix.Web.Controllers; diff --git a/src/Modix.Web/Controllers/TagsController.cs b/src/Modix.Web/Controllers/TagsController.cs index 5181924c..e5466f46 100644 --- a/src/Modix.Web/Controllers/TagsController.cs +++ b/src/Modix.Web/Controllers/TagsController.cs @@ -2,7 +2,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Models.Tags; using Modix.Models.Core; using Modix.Services.Tags; @@ -12,7 +11,7 @@ namespace Modix.Web.Controllers; [Route("~/api/tags")] [ApiController] -[Authorize] +[Authorize(Roles = nameof(AuthorizationClaim.UseTag))] public class TagsController : ModixController { private readonly ITagService _tagService; diff --git a/src/Modix.Web/Controllers/UserInformationController.cs b/src/Modix.Web/Controllers/UserInformationController.cs index b0f305ae..75f2f3ba 100644 --- a/src/Modix.Web/Controllers/UserInformationController.cs +++ b/src/Modix.Web/Controllers/UserInformationController.cs @@ -2,7 +2,6 @@ using Discord.WebSocket; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Modix.Controllers; using Modix.Data.Models.Core; using Modix.Data.Repositories; using Modix.Services.Core; @@ -27,7 +26,6 @@ public UserInformationController(IUserService userService, IMessageRepository me _messageRepository = messageRepository; } - [HttpGet("{userIdString}")] public async Task GetUserInformationAsync(string userIdString) { From 2d7c628d78549eebb467099db49c30cb3a567156 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:27:09 +0200 Subject: [PATCH 38/46] Move DesignatedChannelType to Modix.Models project --- .../Behaviors/ModerationLoggingBehavior.cs | 2 +- .../Behaviors/PromotionLoggingHandler.cs | 1 + .../Responders/StarboardReactionResponder.cs | 2 +- .../Extensions/MessageQueryExtensions.cs | 1 + .../Core/DesignatedChannelMappingBrief.cs | 1 + .../DesignatedChannelMappingCreationData.cs | 1 + .../Core/DesignatedChannelMappingEntity.cs | 1 + .../DesignatedChannelMappingSearchCriteria.cs | 1 + .../Models/Core/DesignatedChannelType.cs | 41 ------------------- .../Core}/DesignatedChannelType.cs | 2 +- .../MessageLogging/MessageLoggingBehavior.cs | 2 +- .../Moderation/AttachmentBlacklistBehavior.cs | 2 +- .../Starboard/StarboardService.cs | 2 +- .../Configuration/DesignatedChannelData.cs | 4 +- .../DesignatedChannelController.cs | 8 ++-- .../TestData/DesignatedChannelMappings.cs | 1 + 16 files changed, 20 insertions(+), 52 deletions(-) delete mode 100644 src/Modix.Data/Models/Core/DesignatedChannelType.cs rename src/{Modix.Web.Shared/Models/Configuration => Modix.Models/Core}/DesignatedChannelType.cs (96%) diff --git a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs index bd4d866d..0ca25953 100644 --- a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs +++ b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs @@ -9,8 +9,8 @@ using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; using Modix.Data.Repositories; +using Modix.Models.Core; using Modix.Services; -using Modix.Services.Core; using Modix.Services.Moderation; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs index 205f47c3..9708839d 100644 --- a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs +++ b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs @@ -12,6 +12,7 @@ using Modix.Common.Messaging; using Modix.Data.Models.Core; using Modix.Data.Models.Promotions; +using Modix.Models.Core; using Modix.Services; using Modix.Services.Core; using Modix.Services.Promotions; diff --git a/src/Modix.Bot/Responders/StarboardReactionResponder.cs b/src/Modix.Bot/Responders/StarboardReactionResponder.cs index 79501474..7184670e 100644 --- a/src/Modix.Bot/Responders/StarboardReactionResponder.cs +++ b/src/Modix.Bot/Responders/StarboardReactionResponder.cs @@ -4,7 +4,7 @@ using MediatR; using Modix.Bot.Notifications; using Modix.Bot.Responders.MessageQuotes; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services; using Modix.Services.Starboard; using Modix.Services.Utilities; diff --git a/src/Modix.Data/Extensions/MessageQueryExtensions.cs b/src/Modix.Data/Extensions/MessageQueryExtensions.cs index b38ada2c..d8a1de81 100644 --- a/src/Modix.Data/Extensions/MessageQueryExtensions.cs +++ b/src/Modix.Data/Extensions/MessageQueryExtensions.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Linq.Expressions; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Data.Extensions { diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingBrief.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingBrief.cs index 19ca3b76..10b0ab72 100644 --- a/src/Modix.Data/Models/Core/DesignatedChannelMappingBrief.cs +++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingBrief.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs index 4a48f374..ed712da4 100644 --- a/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs +++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs @@ -1,4 +1,5 @@ using System; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs index 7c6bc695..8525fef9 100644 --- a/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs +++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Modix.Models.Core; namespace Modix.Data.Models.Core; diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingSearchCriteria.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingSearchCriteria.cs index 63cd6c1a..88ca7d24 100644 --- a/src/Modix.Data/Models/Core/DesignatedChannelMappingSearchCriteria.cs +++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingSearchCriteria.cs @@ -2,6 +2,7 @@ using System.Linq; using Modix.Data.Utilities; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedChannelType.cs b/src/Modix.Data/Models/Core/DesignatedChannelType.cs deleted file mode 100644 index 81594568..00000000 --- a/src/Modix.Data/Models/Core/DesignatedChannelType.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Modix.Data.Models.Core -{ - /// - /// Defines the possible types designations that may be assigned to channels. - /// - public enum DesignatedChannelType - { - /// - /// Defines a channel that logs actions performed by the moderation feature. - /// - ModerationLog, - /// - /// Defines a channel that logs modified and deleted messages. - /// - MessageLog, - /// - /// Defines a channel that logs actions performed by the promotions feature. - /// - PromotionLog, - /// - /// Defines a channel to send promotion campaign creation/closing notifications. - /// - PromotionNotifications, - /// - /// Defines a channel that is not subject to auto-moderation behaviors of the moderation feature. - /// - Unmoderated, - /// - /// Defines a channel to which starred messages are sent. - /// - Starboard, - /// - /// Defines a channel that should be included when calculating user participation. - /// - CountsTowardsParticipation, - /// - /// Defines a channel where messages, if starred, are not sent to the starboard - /// - IgnoredFromStarboard, - } -} diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs b/src/Modix.Models/Core/DesignatedChannelType.cs similarity index 96% rename from src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs rename to src/Modix.Models/Core/DesignatedChannelType.cs index 723db694..0acde2a8 100644 --- a/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelType.cs +++ b/src/Modix.Models/Core/DesignatedChannelType.cs @@ -1,4 +1,4 @@ -namespace Modix.Web.Shared.Models.Configuration; +namespace Modix.Models.Core; /// /// Defines the possible types designations that may be assigned to channels. diff --git a/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs b/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs index 6ad69105..681e1192 100644 --- a/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs +++ b/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; using Modix.Common.Messaging; -using Modix.Data.Models.Core; +using Modix.Models.Core; using Modix.Services.Core; using Modix.Services.Utilities; diff --git a/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs b/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs index 527781e4..d2404841 100644 --- a/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs +++ b/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs @@ -13,7 +13,7 @@ using Modix.Common.Messaging; using Modix.Services.Core; -using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Services.Moderation { diff --git a/src/Modix.Services/Starboard/StarboardService.cs b/src/Modix.Services/Starboard/StarboardService.cs index 1bb97abf..e118afa9 100644 --- a/src/Modix.Services/Starboard/StarboardService.cs +++ b/src/Modix.Services/Starboard/StarboardService.cs @@ -2,9 +2,9 @@ using System.Threading.Tasks; using Discord; using Modix.Services.Core; -using Modix.Data.Models.Core; using System.Collections.Generic; using Modix.Data.Repositories; +using Modix.Models.Core; namespace Modix.Services.Starboard { diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs index 7dbcb6fb..d244d73d 100644 --- a/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedChannelData.cs @@ -1,3 +1,5 @@ -namespace Modix.Web.Shared.Models.Configuration; +using Modix.Models.Core; + +namespace Modix.Web.Shared.Models.Configuration; public record DesignatedChannelData(long Id, ulong RoleId, DesignatedChannelType ChannelDesignation, string Name); diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs index 054e85c9..d0de6f59 100644 --- a/src/Modix.Web/Controllers/DesignatedChannelController.cs +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -22,7 +22,7 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer [HttpGet] [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingRead))] - public async Task>> GetChannelDesignationsAsync() + public async Task>> GetChannelDesignationsAsync() { var designatedChannels = await _designatedChannelService.GetDesignatedChannels(UserGuild.Id); @@ -30,7 +30,7 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer .Select(d => new DesignatedChannelData( d.Id, d.Channel.Id, - (Shared.Models.Configuration.DesignatedChannelType)(int)d.Type, + d.Type, UserGuild.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name)) .ToLookup(x => x.ChannelDesignation, x => x) .ToDictionary(x => x.Key, x => x.ToList()); @@ -38,14 +38,14 @@ public DesignatedChannelController(DesignatedChannelService designatedChannelSer [HttpPut("{channelId}/{designatedChannelType}")] [Authorize(Roles = nameof(AuthorizationClaim.DesignatedChannelMappingCreate))] - public async Task CreateDesignationAsync(ulong channelId, Shared.Models.Configuration.DesignatedChannelType designatedChannelType) + public async Task CreateDesignationAsync(ulong channelId, DesignatedChannelType designatedChannelType) { var foundChannel = UserGuild.GetChannel(channelId); if (foundChannel is not ISocketMessageChannel messageChannel) return BadRequest($"A message channel was not found with id {channelId} in guild with id {UserGuild.Id}"); - var id = await _designatedChannelService.AddDesignatedChannel(foundChannel.Guild, messageChannel, (Data.Models.Core.DesignatedChannelType)(int)designatedChannelType); + var id = await _designatedChannelService.AddDesignatedChannel(foundChannel.Guild, messageChannel, (Modix.Models.Core.DesignatedChannelType)(int)designatedChannelType); return Ok(id); } diff --git a/test/Modix.Data.Test/TestData/DesignatedChannelMappings.cs b/test/Modix.Data.Test/TestData/DesignatedChannelMappings.cs index 4b9659cf..eb717340 100644 --- a/test/Modix.Data.Test/TestData/DesignatedChannelMappings.cs +++ b/test/Modix.Data.Test/TestData/DesignatedChannelMappings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Data.Test.TestData { From 8339951494bb90993880ec6913ffed60d275901f Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:33:02 +0200 Subject: [PATCH 39/46] Move PromotionCampaignOutcome/PromotionSentiment to Modix.Models project --- .../Behaviors/PromotionLoggingHandler.cs | 1 + src/Modix.Bot/Modules/PromotionsModule.cs | 1 + .../PromotionCampaignQueryExtensions.cs | 1 + .../Promotions/PromotionCampaignBrief.cs | 1 + .../Promotions/PromotionCampaignDetails.cs | 1 + .../Promotions/PromotionCampaignEntity.cs | 1 + .../Promotions/PromotionCampaignOutcome.cs | 21 ------------------- .../PromotionCampaignSearchCriteria.cs | 1 + .../Promotions/PromotionCampaignSummary.cs | 1 + .../Promotions/PromotionCommentActionBrief.cs | 1 + .../PromotionCommentCampaignBrief.cs | 1 + .../PromotionCommentCreationData.cs | 1 + .../Promotions/PromotionCommentEntity.cs | 1 + .../PromotionCommentMutationData.cs | 4 +++- .../PromotionCommentSearchCriteria.cs | 1 + .../Promotions/PromotionCommentSummary.cs | 1 + .../PromotionCampaignRepository.cs | 1 + .../Promotions/PromotionCampaignOutcome.cs | 5 ++++- .../Promotions/PromotionSentiment.cs | 2 +- .../Promotions/PromotionsService.cs | 1 + .../Models/Promotions/CampaignCommentData.cs | 4 +++- .../Promotions/PromotionCampaignData.cs | 4 +++- .../Models/Promotions/PromotionSentiment.cs | 17 --------------- .../Components/CreateCampaignComment.razor | 3 ++- .../EditPromotionCommentDialog.razor | 3 ++- src/Modix.Web.Wasm/Pages/Promotions.razor | 1 + .../Controllers/CampaignController.cs | 15 ++++++------- 27 files changed, 41 insertions(+), 54 deletions(-) delete mode 100644 src/Modix.Data/Models/Promotions/PromotionCampaignOutcome.cs rename src/{Modix.Web.Shared/Models => Modix.Models}/Promotions/PromotionCampaignOutcome.cs (80%) rename src/{Modix.Data/Models => Modix.Models}/Promotions/PromotionSentiment.cs (93%) delete mode 100644 src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs diff --git a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs index 9708839d..71ddadeb 100644 --- a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs +++ b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs @@ -13,6 +13,7 @@ using Modix.Data.Models.Core; using Modix.Data.Models.Promotions; using Modix.Models.Core; +using Modix.Models.Promotions; using Modix.Services; using Modix.Services.Core; using Modix.Services.Promotions; diff --git a/src/Modix.Bot/Modules/PromotionsModule.cs b/src/Modix.Bot/Modules/PromotionsModule.cs index f68ebefe..60ee3bc0 100644 --- a/src/Modix.Bot/Modules/PromotionsModule.cs +++ b/src/Modix.Bot/Modules/PromotionsModule.cs @@ -21,6 +21,7 @@ using Modix.Data.Models.Promotions; using Modix.Data.Utilities; using Modix.Models.Core; +using Modix.Models.Promotions; using Modix.Services.CommandHelp; using Modix.Services.Promotions; using Modix.Services.Utilities; diff --git a/src/Modix.Data/Extensions/PromotionCampaignQueryExtensions.cs b/src/Modix.Data/Extensions/PromotionCampaignQueryExtensions.cs index 38195d00..0d08feda 100644 --- a/src/Modix.Data/Extensions/PromotionCampaignQueryExtensions.cs +++ b/src/Modix.Data/Extensions/PromotionCampaignQueryExtensions.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Linq.Expressions; using Modix.Data.Models.Promotions; +using Modix.Models.Promotions; namespace Modix.Data.Extensions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignBrief.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignBrief.cs index be6bfcbf..8a793378 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignBrief.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCampaignBrief.cs @@ -3,6 +3,7 @@ using Modix.Data.ExpandableQueries; using Modix.Data.Models.Core; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignDetails.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignDetails.cs index d1f9a32e..b05113cb 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignDetails.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCampaignDetails.cs @@ -5,6 +5,7 @@ using Modix.Data.ExpandableQueries; using Modix.Data.Models.Core; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignEntity.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignEntity.cs index f0e875c2..86bf0451 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignEntity.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCampaignEntity.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Modix.Data.Models.Core; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignOutcome.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignOutcome.cs deleted file mode 100644 index 1db2866f..00000000 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignOutcome.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Modix.Data.Models.Promotions -{ - /// - /// Defines the possible types of outcomes of a promotion campaign. - /// - public enum PromotionCampaignOutcome - { - /// - /// Describes a campaign that was accepted, for which the subject was promoted to the proposed rank. - /// - Accepted, - /// - /// Describes a campaign that was rejected, for which the subject was not promoted to the proposed rank. - /// - Rejected, - /// - /// Describes a campaign for which an error occurred during processing, that prevented the proposed promotion from being fully applied. - /// - Failed - } -} diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignSearchCriteria.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignSearchCriteria.cs index e14131e9..6578ede7 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignSearchCriteria.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCampaignSearchCriteria.cs @@ -3,6 +3,7 @@ using Modix.Data.Repositories; using Modix.Data.Utilities; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCampaignSummary.cs b/src/Modix.Data/Models/Promotions/PromotionCampaignSummary.cs index 60199507..cbc97bf9 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCampaignSummary.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCampaignSummary.cs @@ -4,6 +4,7 @@ using Modix.Data.ExpandableQueries; using Modix.Data.Models.Core; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentActionBrief.cs b/src/Modix.Data/Models/Promotions/PromotionCommentActionBrief.cs index 3b453102..3a9511ae 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentActionBrief.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentActionBrief.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentCampaignBrief.cs b/src/Modix.Data/Models/Promotions/PromotionCommentCampaignBrief.cs index 5a9323e7..c4789255 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentCampaignBrief.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentCampaignBrief.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentCreationData.cs b/src/Modix.Data/Models/Promotions/PromotionCommentCreationData.cs index f2ca6713..363761fb 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentCreationData.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentCreationData.cs @@ -1,4 +1,5 @@ using System; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentEntity.cs b/src/Modix.Data/Models/Promotions/PromotionCommentEntity.cs index b8e3b196..fec63eb1 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentEntity.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentEntity.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentMutationData.cs b/src/Modix.Data/Models/Promotions/PromotionCommentMutationData.cs index 5e326a5c..61600ec8 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentMutationData.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentMutationData.cs @@ -1,4 +1,6 @@ -namespace Modix.Data.Models.Promotions +using Modix.Models.Promotions; + +namespace Modix.Data.Models.Promotions { /// /// Describes an operation to modify a object. diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentSearchCriteria.cs b/src/Modix.Data/Models/Promotions/PromotionCommentSearchCriteria.cs index b6da35ba..1cb8d8d2 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentSearchCriteria.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentSearchCriteria.cs @@ -3,6 +3,7 @@ using Modix.Data.Repositories; using Modix.Data.Utilities; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Models/Promotions/PromotionCommentSummary.cs b/src/Modix.Data/Models/Promotions/PromotionCommentSummary.cs index ecc050dc..0123c83f 100644 --- a/src/Modix.Data/Models/Promotions/PromotionCommentSummary.cs +++ b/src/Modix.Data/Models/Promotions/PromotionCommentSummary.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Promotions; namespace Modix.Data.Models.Promotions { diff --git a/src/Modix.Data/Repositories/PromotionCampaignRepository.cs b/src/Modix.Data/Repositories/PromotionCampaignRepository.cs index d964d573..7d7a8cd7 100644 --- a/src/Modix.Data/Repositories/PromotionCampaignRepository.cs +++ b/src/Modix.Data/Repositories/PromotionCampaignRepository.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore; using Modix.Data.ExpandableQueries; using Modix.Data.Models.Promotions; +using Modix.Models.Promotions; namespace Modix.Data.Repositories { diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs b/src/Modix.Models/Promotions/PromotionCampaignOutcome.cs similarity index 80% rename from src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs rename to src/Modix.Models/Promotions/PromotionCampaignOutcome.cs index 9c2198a5..a37e93ba 100644 --- a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignOutcome.cs +++ b/src/Modix.Models/Promotions/PromotionCampaignOutcome.cs @@ -1,5 +1,8 @@ -namespace Modix.Web.Shared.Models.Promotions; +namespace Modix.Models.Promotions; +/// +/// Defines the possible types of outcomes of a promotion campaign. +/// public enum PromotionCampaignOutcome { /// diff --git a/src/Modix.Data/Models/Promotions/PromotionSentiment.cs b/src/Modix.Models/Promotions/PromotionSentiment.cs similarity index 93% rename from src/Modix.Data/Models/Promotions/PromotionSentiment.cs rename to src/Modix.Models/Promotions/PromotionSentiment.cs index 0127de69..339b0139 100644 --- a/src/Modix.Data/Models/Promotions/PromotionSentiment.cs +++ b/src/Modix.Models/Promotions/PromotionSentiment.cs @@ -1,4 +1,4 @@ -namespace Modix.Data.Models.Promotions +namespace Modix.Models.Promotions { /// /// Defines the possible opinions that a may express about a . diff --git a/src/Modix.Services/Promotions/PromotionsService.cs b/src/Modix.Services/Promotions/PromotionsService.cs index e7e4394c..41d1e46c 100644 --- a/src/Modix.Services/Promotions/PromotionsService.cs +++ b/src/Modix.Services/Promotions/PromotionsService.cs @@ -16,6 +16,7 @@ using Modix.Data.Repositories; using Modix.Data.Utilities; using Modix.Models.Core; +using Modix.Models.Promotions; using Modix.Services.Core; using Modix.Services.Utilities; diff --git a/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs b/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs index 961715ed..27d1fbbe 100644 --- a/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs +++ b/src/Modix.Web.Shared/Models/Promotions/CampaignCommentData.cs @@ -1,3 +1,5 @@ -namespace Modix.Web.Shared.Models.Promotions; +using Modix.Models.Promotions; + +namespace Modix.Web.Shared.Models.Promotions; public record CampaignCommentData(long Id, PromotionSentiment PromotionSentiment, string Content, DateTimeOffset CreatedAt, bool IsFromCurrentUser); diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs index 46dc7a40..5b98207d 100644 --- a/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs +++ b/src/Modix.Web.Shared/Models/Promotions/PromotionCampaignData.cs @@ -1,4 +1,6 @@ -namespace Modix.Web.Shared.Models.Promotions; +using Modix.Models.Promotions; + +namespace Modix.Web.Shared.Models.Promotions; public record PromotionCampaignData { diff --git a/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs b/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs deleted file mode 100644 index 42675931..00000000 --- a/src/Modix.Web.Shared/Models/Promotions/PromotionSentiment.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Modix.Web.Shared.Models.Promotions; - -public enum PromotionSentiment -{ - /// - /// Describes a comment that does not express a specific opinion about a promotion campaign. - /// - Abstain, - /// - /// Describes a comment that approves of a promotion campaign. - /// - Approve, - /// - /// Describes a comment that opposes a promotion campaign. - /// - Oppose -} diff --git a/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor b/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor index 389f456a..9b60c636 100644 --- a/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor +++ b/src/Modix.Web.Wasm/Components/CreateCampaignComment.razor @@ -1,4 +1,5 @@ -@using Modix.Web.Shared.Models.Promotions +@using Modix.Models.Promotions +@using Modix.Web.Shared.Models.Promotions @using MudBlazor;
diff --git a/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor b/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor index 54d2ed78..3f72fe9b 100644 --- a/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor +++ b/src/Modix.Web.Wasm/Components/EditPromotionCommentDialog.razor @@ -1,4 +1,5 @@ -@using Modix.Web.Shared.Models.Promotions +@using Modix.Models.Promotions +@using Modix.Web.Shared.Models.Promotions @using MudBlazor diff --git a/src/Modix.Web.Wasm/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor index 1bd35f4f..cf346761 100644 --- a/src/Modix.Web.Wasm/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -5,6 +5,7 @@ @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using Modix.Models.Core +@using Modix.Models.Promotions @using Modix.Web.Models; @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Promotions diff --git a/src/Modix.Web/Controllers/CampaignController.cs b/src/Modix.Web/Controllers/CampaignController.cs index 9d3fbe9a..d865b89b 100644 --- a/src/Modix.Web/Controllers/CampaignController.cs +++ b/src/Modix.Web/Controllers/CampaignController.cs @@ -38,10 +38,7 @@ public async IAsyncEnumerable GetCampaignsAsync() SubjectName = campaign.Subject.GetFullUsername(), TargetRoleId = campaign.TargetRole.Id, TargetRoleName = campaign.TargetRole.Name, - - // This is... very neat :) - Outcome = campaign.Outcome == null ? null : (Shared.Models.Promotions.PromotionCampaignOutcome)(int)campaign.Outcome, - + Outcome = campaign.Outcome, Created = campaign.CreateAction.Created, IsCurrentUserCampaign = campaign.Subject.Id == SocketUser.Id, ApproveCount = campaign.ApproveCount, @@ -64,7 +61,7 @@ public async Task GetCampaignCommentsAsync(long campaignId) .Select(x => new CampaignCommentData ( x.Id, - (PromotionSentiment)(int)x.Sentiment, + x.Sentiment, x.Content, x.CreateAction.Created, x.CreateAction.CreatedBy.Id == SocketUser.Id @@ -80,12 +77,12 @@ public async Task CreateCommentAsync(long campaignId, [From { var promotionActionSummary = await _promotionsService.AddCommentAsync( campaignId, - (Data.Models.Promotions.PromotionSentiment)(int)campaignCommentData.PromotionSentiment, + campaignCommentData.PromotionSentiment, campaignCommentData.Content); var newComment = promotionActionSummary.NewComment; - return new CampaignCommentData(newComment.Id, (PromotionSentiment)(int)newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + return new CampaignCommentData(newComment.Id, newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); } [HttpPatch("updatecomment")] @@ -93,12 +90,12 @@ public async Task UpdateCommentAsync([FromBody] CampaignCom { var promotionActionSummary = await _promotionsService.UpdateCommentAsync( campaignCommentData.Id, - (Data.Models.Promotions.PromotionSentiment)(int)campaignCommentData.PromotionSentiment, + campaignCommentData.PromotionSentiment, campaignCommentData.Content); var newComment = promotionActionSummary.NewComment; - return new CampaignCommentData(newComment.Id, (PromotionSentiment)(int)newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); + return new CampaignCommentData(newComment.Id, newComment.Sentiment, newComment.Content, promotionActionSummary.Created, true); } [HttpPost("{campaignId}/accept/{force}")] From 73bb395482050943af3c04da8b68b532a56f804b Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:39:00 +0200 Subject: [PATCH 40/46] Move DesignatedRoleType to Modix.Models project --- .../Models/Core/DesignatedRoleMappingBrief.cs | 1 + .../Core/DesignatedRoleMappingCreationData.cs | 1 + .../Core/DesignatedRoleMappingEntity.cs | 1 + .../DesignatedRoleMappingSearchCriteria.cs | 1 + .../Models/Core/DesignatedRoleType.cs | 21 ------------ src/Modix.Models/Core/ClaimMappingType.cs | 25 +++++++------- .../Core}/DesignatedRoleType.cs | 2 +- .../Promotions/PromotionSentiment.cs | 33 +++++++++---------- .../Configuration/DesignatedRoleData.cs | 4 ++- .../DesignatedChannelController.cs | 2 +- .../Controllers/DesignatedRoleController.cs | 8 ++--- .../TestData/DesignatedRoleMappings.cs | 1 + .../Core/DesignatedRoleServiceTests.cs | 1 + 13 files changed, 43 insertions(+), 58 deletions(-) delete mode 100644 src/Modix.Data/Models/Core/DesignatedRoleType.cs rename src/{Modix.Web.Shared/Models/Configuration => Modix.Models/Core}/DesignatedRoleType.cs (90%) diff --git a/src/Modix.Data/Models/Core/DesignatedRoleMappingBrief.cs b/src/Modix.Data/Models/Core/DesignatedRoleMappingBrief.cs index d072aaa7..9b1070dd 100644 --- a/src/Modix.Data/Models/Core/DesignatedRoleMappingBrief.cs +++ b/src/Modix.Data/Models/Core/DesignatedRoleMappingBrief.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using Modix.Data.ExpandableQueries; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedRoleMappingCreationData.cs b/src/Modix.Data/Models/Core/DesignatedRoleMappingCreationData.cs index ddf4ece1..f99e1003 100644 --- a/src/Modix.Data/Models/Core/DesignatedRoleMappingCreationData.cs +++ b/src/Modix.Data/Models/Core/DesignatedRoleMappingCreationData.cs @@ -1,4 +1,5 @@ using System; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedRoleMappingEntity.cs b/src/Modix.Data/Models/Core/DesignatedRoleMappingEntity.cs index 1fb15adb..a4006c6b 100644 --- a/src/Modix.Data/Models/Core/DesignatedRoleMappingEntity.cs +++ b/src/Modix.Data/Models/Core/DesignatedRoleMappingEntity.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedRoleMappingSearchCriteria.cs b/src/Modix.Data/Models/Core/DesignatedRoleMappingSearchCriteria.cs index 5c034480..92c1cc41 100644 --- a/src/Modix.Data/Models/Core/DesignatedRoleMappingSearchCriteria.cs +++ b/src/Modix.Data/Models/Core/DesignatedRoleMappingSearchCriteria.cs @@ -3,6 +3,7 @@ using System.Linq; using Modix.Data.Utilities; +using Modix.Models.Core; namespace Modix.Data.Models.Core { diff --git a/src/Modix.Data/Models/Core/DesignatedRoleType.cs b/src/Modix.Data/Models/Core/DesignatedRoleType.cs deleted file mode 100644 index 41692208..00000000 --- a/src/Modix.Data/Models/Core/DesignatedRoleType.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Modix.Data.Models.Core -{ - /// - /// Defines the possible types designations that may be assigned to roles. - /// - public enum DesignatedRoleType - { - /// - /// Defines a role that serves as a member of the rank hierarchy. - /// - Rank, - /// - /// Defines a role that is used by the moderation feature to mute users. - /// - ModerationMute, - /// - /// Defines a role whose mentionability is allowed throughout the guild. - /// - Pingable, - } -} diff --git a/src/Modix.Models/Core/ClaimMappingType.cs b/src/Modix.Models/Core/ClaimMappingType.cs index 48298e22..bcd0628e 100644 --- a/src/Modix.Models/Core/ClaimMappingType.cs +++ b/src/Modix.Models/Core/ClaimMappingType.cs @@ -1,17 +1,16 @@ -namespace Modix.Models.Core +namespace Modix.Models.Core; + +/// +/// Defines the possible types of claim mappings. +/// +public enum ClaimMappingType { /// - /// Defines the possible types of claim mappings. + /// Describes a claim mapping where a claim is granted to an entity. /// - public enum ClaimMappingType - { - /// - /// Describes a claim mapping where a claim is granted to an entity. - /// - Granted, - /// - /// Describes a claim mapping where a claim is denied to an entity. - /// - Denied, - } + Granted, + /// + /// Describes a claim mapping where a claim is denied to an entity. + /// + Denied, } diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs b/src/Modix.Models/Core/DesignatedRoleType.cs similarity index 90% rename from src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs rename to src/Modix.Models/Core/DesignatedRoleType.cs index 8f78d771..d49bc297 100644 --- a/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleType.cs +++ b/src/Modix.Models/Core/DesignatedRoleType.cs @@ -1,4 +1,4 @@ -namespace Modix.Web.Shared.Models.Configuration; +namespace Modix.Models.Core; /// /// Defines the possible types designations that may be assigned to roles. diff --git a/src/Modix.Models/Promotions/PromotionSentiment.cs b/src/Modix.Models/Promotions/PromotionSentiment.cs index 339b0139..f0c27eda 100644 --- a/src/Modix.Models/Promotions/PromotionSentiment.cs +++ b/src/Modix.Models/Promotions/PromotionSentiment.cs @@ -1,21 +1,20 @@ -namespace Modix.Models.Promotions +namespace Modix.Models.Promotions; + +/// +/// Defines the possible opinions that a may express about a . +/// +public enum PromotionSentiment { /// - /// Defines the possible opinions that a may express about a . + /// Describes a comment that does not express a specific opinion about a promotion campaign. /// - public enum PromotionSentiment - { - /// - /// Describes a comment that does not express a specific opinion about a promotion campaign. - /// - Abstain, - /// - /// Describes a comment that approves of a promotion campaign. - /// - Approve, - /// - /// Describes a comment that opposes a promotion campaign. - /// - Oppose - } + Abstain, + /// + /// Describes a comment that approves of a promotion campaign. + /// + Approve, + /// + /// Describes a comment that opposes a promotion campaign. + /// + Oppose } diff --git a/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs index 49d2b0ac..ae1246b5 100644 --- a/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs +++ b/src/Modix.Web.Shared/Models/Configuration/DesignatedRoleData.cs @@ -1,3 +1,5 @@ -namespace Modix.Web.Shared.Models.Configuration; +using Modix.Models.Core; + +namespace Modix.Web.Shared.Models.Configuration; public record DesignatedRoleData(long Id, ulong RoleId, DesignatedRoleType RoleDesignation, string Name); diff --git a/src/Modix.Web/Controllers/DesignatedChannelController.cs b/src/Modix.Web/Controllers/DesignatedChannelController.cs index d0de6f59..1ea0a2c8 100644 --- a/src/Modix.Web/Controllers/DesignatedChannelController.cs +++ b/src/Modix.Web/Controllers/DesignatedChannelController.cs @@ -45,7 +45,7 @@ public async Task CreateDesignationAsync(ulong channelId, Designa if (foundChannel is not ISocketMessageChannel messageChannel) return BadRequest($"A message channel was not found with id {channelId} in guild with id {UserGuild.Id}"); - var id = await _designatedChannelService.AddDesignatedChannel(foundChannel.Guild, messageChannel, (Modix.Models.Core.DesignatedChannelType)(int)designatedChannelType); + var id = await _designatedChannelService.AddDesignatedChannel(foundChannel.Guild, messageChannel, designatedChannelType); return Ok(id); } diff --git a/src/Modix.Web/Controllers/DesignatedRoleController.cs b/src/Modix.Web/Controllers/DesignatedRoleController.cs index 8bf94320..45f0039c 100644 --- a/src/Modix.Web/Controllers/DesignatedRoleController.cs +++ b/src/Modix.Web/Controllers/DesignatedRoleController.cs @@ -22,7 +22,7 @@ public DesignatedRoleController(IDesignatedRoleService designatedRoleService, Di [HttpGet] [Authorize(Roles = nameof(AuthorizationClaim.DesignatedRoleMappingRead))] - public async Task>> GetRoleDesignationsAsync() + public async Task>> GetRoleDesignationsAsync() { var designatedRoles = await _designatedRoleService.GetDesignatedRolesAsync(UserGuild.Id); @@ -30,7 +30,7 @@ public DesignatedRoleController(IDesignatedRoleService designatedRoleService, Di .Select(d => new DesignatedRoleData( d.Id, d.Role.Id, - (Shared.Models.Configuration.DesignatedRoleType)(int)d.Type, + d.Type, UserGuild.GetRole(d.Role.Id)?.Name ?? d.Role.Name)) .ToLookup(x => x.RoleDesignation, x => x) .ToDictionary(x => x.Key, x => x.ToList()); @@ -38,14 +38,14 @@ public DesignatedRoleController(IDesignatedRoleService designatedRoleService, Di [HttpPut("{roleId}/{designatedRoleType}")] [Authorize(Roles = nameof(AuthorizationClaim.DesignatedRoleMappingCreate))] - public async Task CreateDesignationAsync(ulong roleId, Shared.Models.Configuration.DesignatedRoleType designatedRoleType) + public async Task CreateDesignationAsync(ulong roleId, DesignatedRoleType designatedRoleType) { var foundRole = UserGuild.GetRole(roleId); if (foundRole is null) return BadRequest($"A role was not found with id {roleId} in guild with id {ModixAuth.CurrentGuildId}"); - var id = await _designatedRoleService.AddDesignatedRoleAsync(UserGuild.Id, roleId, (Data.Models.Core.DesignatedRoleType)(int)designatedRoleType); + var id = await _designatedRoleService.AddDesignatedRoleAsync(UserGuild.Id, roleId, (DesignatedRoleType)(int)designatedRoleType); return Ok(id); } diff --git a/test/Modix.Data.Test/TestData/DesignatedRoleMappings.cs b/test/Modix.Data.Test/TestData/DesignatedRoleMappings.cs index 4130a501..498265ae 100644 --- a/test/Modix.Data.Test/TestData/DesignatedRoleMappings.cs +++ b/test/Modix.Data.Test/TestData/DesignatedRoleMappings.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Modix.Data.Models.Core; +using Modix.Models.Core; namespace Modix.Data.Test.TestData { diff --git a/test/Modix.Services.Test/Core/DesignatedRoleServiceTests.cs b/test/Modix.Services.Test/Core/DesignatedRoleServiceTests.cs index 0dc9b049..75c85284 100644 --- a/test/Modix.Services.Test/Core/DesignatedRoleServiceTests.cs +++ b/test/Modix.Services.Test/Core/DesignatedRoleServiceTests.cs @@ -14,6 +14,7 @@ using Modix.Services.Core; using Modix.Common.Test; +using Modix.Models.Core; namespace Modix.Services.Test.Core { From a5b33aa28847a3b2f9173692e55ba26b6d13ed99 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:44:15 +0200 Subject: [PATCH 41/46] Move InfractionType to Modix.Models project --- .../Behaviors/ModerationLoggingBehavior.cs | 1 + src/Modix.Bot/Modules/InfractionModule.cs | 1 + src/Modix.Bot/Modules/ModerationModule.cs | 2 +- .../Responders/AuditLogCreatedResponder.cs | 2 +- .../Responders/MutedUserJoinedResponder.cs | 2 +- .../Models/Moderation/InfractionBrief.cs | 1 + .../Moderation/InfractionCreationData.cs | 1 + .../Models/Moderation/InfractionEntity.cs | 1 + .../Moderation/InfractionSearchCriteria.cs | 1 + .../Models/Moderation/InfractionSummary.cs | 1 + .../Models/Moderation/InfractionType.cs | 25 ------------------- .../Repositories/InfractionRepository.cs | 1 + .../Moderation}/InfractionType.cs | 2 +- .../Moderation/ModerationService.cs | 1 + .../Utilities/FormatUtilities.cs | 3 +-- .../Infractions/InfractionCreationData.cs | 4 ++- .../Models/Infractions/InfractionData.cs | 4 ++- .../Models/Infractions/TableFilter.cs | 4 ++- .../Components/Infractions/Infractions.razor | 1 + .../Controllers/DesignatedRoleController.cs | 2 +- .../Controllers/InfractionsController.cs | 10 +++----- 21 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 src/Modix.Data/Models/Moderation/InfractionType.cs rename src/{Modix.Web.Shared/Models/Infractions => Modix.Models/Moderation}/InfractionType.cs (93%) diff --git a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs index 0ca25953..4d881ad8 100644 --- a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs +++ b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs @@ -10,6 +10,7 @@ using Modix.Data.Models.Moderation; using Modix.Data.Repositories; using Modix.Models.Core; +using Modix.Models.Moderation; using Modix.Services; using Modix.Services.Moderation; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Modules/InfractionModule.cs b/src/Modix.Bot/Modules/InfractionModule.cs index 4d9ef32d..2a1f832d 100644 --- a/src/Modix.Bot/Modules/InfractionModule.cs +++ b/src/Modix.Bot/Modules/InfractionModule.cs @@ -16,6 +16,7 @@ using Modix.Data.Models.Core; using Modix.Data.Models.Moderation; using Modix.Models.Core; +using Modix.Models.Moderation; using Modix.Services.CommandHelp; using Modix.Services.Moderation; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Modules/ModerationModule.cs b/src/Modix.Bot/Modules/ModerationModule.cs index 93bab627..22a26bf3 100644 --- a/src/Modix.Bot/Modules/ModerationModule.cs +++ b/src/Modix.Bot/Modules/ModerationModule.cs @@ -12,7 +12,7 @@ using Modix.Bot.Extensions; using Modix.Common.Extensions; using Modix.Data.Models.Core; -using Modix.Data.Models.Moderation; +using Modix.Models.Moderation; using Modix.Services.CommandHelp; using Modix.Services.Core; using Modix.Services.Moderation; diff --git a/src/Modix.Bot/Responders/AuditLogCreatedResponder.cs b/src/Modix.Bot/Responders/AuditLogCreatedResponder.cs index 7db9beb5..a33d95b6 100644 --- a/src/Modix.Bot/Responders/AuditLogCreatedResponder.cs +++ b/src/Modix.Bot/Responders/AuditLogCreatedResponder.cs @@ -5,7 +5,7 @@ using Discord.WebSocket; using MediatR; using Modix.Bot.Notifications; -using Modix.Data.Models.Moderation; +using Modix.Models.Moderation; using Modix.Services.Core; using Modix.Services.Moderation; using Modix.Services.Utilities; diff --git a/src/Modix.Bot/Responders/MutedUserJoinedResponder.cs b/src/Modix.Bot/Responders/MutedUserJoinedResponder.cs index da2c0892..5feed44d 100644 --- a/src/Modix.Bot/Responders/MutedUserJoinedResponder.cs +++ b/src/Modix.Bot/Responders/MutedUserJoinedResponder.cs @@ -4,7 +4,7 @@ using Discord.WebSocket; using MediatR; using Modix.Bot.Notifications; -using Modix.Data.Models.Moderation; +using Modix.Models.Moderation; using Modix.Services.Moderation; using Serilog; diff --git a/src/Modix.Data/Models/Moderation/InfractionBrief.cs b/src/Modix.Data/Models/Moderation/InfractionBrief.cs index d90c9e00..8f223abe 100644 --- a/src/Modix.Data/Models/Moderation/InfractionBrief.cs +++ b/src/Modix.Data/Models/Moderation/InfractionBrief.cs @@ -3,6 +3,7 @@ using Modix.Data.ExpandableQueries; using Modix.Data.Models.Core; +using Modix.Models.Moderation; namespace Modix.Data.Models.Moderation { diff --git a/src/Modix.Data/Models/Moderation/InfractionCreationData.cs b/src/Modix.Data/Models/Moderation/InfractionCreationData.cs index f9491cef..5580cad2 100644 --- a/src/Modix.Data/Models/Moderation/InfractionCreationData.cs +++ b/src/Modix.Data/Models/Moderation/InfractionCreationData.cs @@ -1,4 +1,5 @@ using System; +using Modix.Models.Moderation; namespace Modix.Data.Models.Moderation { diff --git a/src/Modix.Data/Models/Moderation/InfractionEntity.cs b/src/Modix.Data/Models/Moderation/InfractionEntity.cs index b0bb8c49..c1681fc4 100644 --- a/src/Modix.Data/Models/Moderation/InfractionEntity.cs +++ b/src/Modix.Data/Models/Moderation/InfractionEntity.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Modix.Data.Models.Core; +using Modix.Models.Moderation; namespace Modix.Data.Models.Moderation { diff --git a/src/Modix.Data/Models/Moderation/InfractionSearchCriteria.cs b/src/Modix.Data/Models/Moderation/InfractionSearchCriteria.cs index f4b4177c..437b1871 100644 --- a/src/Modix.Data/Models/Moderation/InfractionSearchCriteria.cs +++ b/src/Modix.Data/Models/Moderation/InfractionSearchCriteria.cs @@ -5,6 +5,7 @@ using Modix.Data.Models.Core; using Modix.Data.Repositories; using Modix.Data.Utilities; +using Modix.Models.Moderation; namespace Modix.Data.Models.Moderation { diff --git a/src/Modix.Data/Models/Moderation/InfractionSummary.cs b/src/Modix.Data/Models/Moderation/InfractionSummary.cs index 60ca0a5f..ac7b7fc9 100644 --- a/src/Modix.Data/Models/Moderation/InfractionSummary.cs +++ b/src/Modix.Data/Models/Moderation/InfractionSummary.cs @@ -4,6 +4,7 @@ using Modix.Data.ExpandableQueries; using Modix.Data.Models.Core; +using Modix.Models.Moderation; namespace Modix.Data.Models.Moderation { diff --git a/src/Modix.Data/Models/Moderation/InfractionType.cs b/src/Modix.Data/Models/Moderation/InfractionType.cs deleted file mode 100644 index f751801d..00000000 --- a/src/Modix.Data/Models/Moderation/InfractionType.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Modix.Data.Models.Moderation -{ - /// - /// Defines the possible types of infractions that can be recorded for a user. - /// - public enum InfractionType - { - /// - /// Describes an "infraction" that is really just a note for communication between moderators. - /// - Notice, - /// - /// Describes an infraction where the user was issued a warning. - /// - Warning, - /// - /// Describes an infraction where the user was muted, preventing them from sending messages in text channels or speaking in voice channels. - /// - Mute, - /// - /// Describes an infraction where the user was banned from a guild. - /// - Ban, - } -} diff --git a/src/Modix.Data/Repositories/InfractionRepository.cs b/src/Modix.Data/Repositories/InfractionRepository.cs index 8e00b4b2..59b1d5df 100644 --- a/src/Modix.Data/Repositories/InfractionRepository.cs +++ b/src/Modix.Data/Repositories/InfractionRepository.cs @@ -9,6 +9,7 @@ using Modix.Data.Models; using Modix.Data.Models.Moderation; using Modix.Data.Utilities; +using Modix.Models.Moderation; namespace Modix.Data.Repositories { diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionType.cs b/src/Modix.Models/Moderation/InfractionType.cs similarity index 93% rename from src/Modix.Web.Shared/Models/Infractions/InfractionType.cs rename to src/Modix.Models/Moderation/InfractionType.cs index 52761fa2..57759420 100644 --- a/src/Modix.Web.Shared/Models/Infractions/InfractionType.cs +++ b/src/Modix.Models/Moderation/InfractionType.cs @@ -1,4 +1,4 @@ -namespace Modix.Web.Shared.Models.Infractions; +namespace Modix.Models.Moderation; /// /// Defines the possible types of infractions that can be recorded for a user. diff --git a/src/Modix.Services/Moderation/ModerationService.cs b/src/Modix.Services/Moderation/ModerationService.cs index f9a7b453..f895aea6 100644 --- a/src/Modix.Services/Moderation/ModerationService.cs +++ b/src/Modix.Services/Moderation/ModerationService.cs @@ -15,6 +15,7 @@ using Modix.Services.Core; using Modix.Services.Utilities; using Serilog; +using Modix.Models.Moderation; namespace Modix.Services.Moderation; diff --git a/src/Modix.Services/Utilities/FormatUtilities.cs b/src/Modix.Services/Utilities/FormatUtilities.cs index a2fbe278..bee73be4 100644 --- a/src/Modix.Services/Utilities/FormatUtilities.cs +++ b/src/Modix.Services/Utilities/FormatUtilities.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.Globalization; using System.Linq; -using System.Net; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; @@ -11,7 +10,7 @@ using Discord; using Humanizer; using Humanizer.Localisation; -using Modix.Data.Models.Moderation; +using Modix.Models.Moderation; namespace Modix.Services.Utilities { diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs index 96949d02..1f812b64 100644 --- a/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionCreationData.cs @@ -1,3 +1,5 @@ -namespace Modix.Web.Shared.Models.Infractions; +using Modix.Models.Moderation; + +namespace Modix.Web.Shared.Models.Infractions; public record InfractionCreationData(InfractionType Type, ulong SubjectId, string Reason, TimeSpan? Duration); diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs index 82c93633..b0045f2f 100644 --- a/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionData.cs @@ -1,4 +1,6 @@ -namespace Modix.Web.Shared.Models.Infractions; +using Modix.Models.Moderation; + +namespace Modix.Web.Shared.Models.Infractions; public record InfractionData( long Id, diff --git a/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs b/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs index 03804dbd..27015331 100644 --- a/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs +++ b/src/Modix.Web.Shared/Models/Infractions/TableFilter.cs @@ -1,4 +1,6 @@ -namespace Modix.Web.Shared.Models.Infractions; +using Modix.Models.Moderation; + +namespace Modix.Web.Shared.Models.Infractions; public class TableFilter { diff --git a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index 98a13023..a7cae3d2 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -7,6 +7,7 @@ @using Modix.Web.Wasm.Components @using MudBlazor; @using System.Security.Claims; +@using Modix.Models.Moderation; Modix - Infractions diff --git a/src/Modix.Web/Controllers/DesignatedRoleController.cs b/src/Modix.Web/Controllers/DesignatedRoleController.cs index 45f0039c..f4db94f1 100644 --- a/src/Modix.Web/Controllers/DesignatedRoleController.cs +++ b/src/Modix.Web/Controllers/DesignatedRoleController.cs @@ -45,7 +45,7 @@ public async Task CreateDesignationAsync(ulong roleId, Designated if (foundRole is null) return BadRequest($"A role was not found with id {roleId} in guild with id {ModixAuth.CurrentGuildId}"); - var id = await _designatedRoleService.AddDesignatedRoleAsync(UserGuild.Id, roleId, (DesignatedRoleType)(int)designatedRoleType); + var id = await _designatedRoleService.AddDesignatedRoleAsync(UserGuild.Id, roleId, designatedRoleType); return Ok(id); } diff --git a/src/Modix.Web/Controllers/InfractionsController.cs b/src/Modix.Web/Controllers/InfractionsController.cs index d1950be5..3f69843e 100644 --- a/src/Modix.Web/Controllers/InfractionsController.cs +++ b/src/Modix.Web/Controllers/InfractionsController.cs @@ -52,7 +52,7 @@ public async Task GetInfractionsAsync(Intermediate inter) { GuildId = UserGuild.Id, Id = tableFilter.Id, - Types = tableFilter.Types?.Cast().ToArray(), + Types = tableFilter.Types, Subject = tableFilter.Subject, SubjectId = tableFilter.SubjectId, Creator = tableFilter.Creator, @@ -78,8 +78,7 @@ public async Task GetInfractionsAsync(Intermediate inter) .Select(x => new InfractionData( x.Id, x.GuildId, - // TODO: - (Shared.Models.Infractions.InfractionType)(int)x.Type, + x.Type, x.Reason, x.Duration, x.Subject.Username, @@ -91,7 +90,7 @@ public async Task GetInfractionsAsync(Intermediate inter) x.RescindAction is null && x.DeleteAction is null - && (x.Type == Data.Models.Moderation.InfractionType.Mute || x.Type == Data.Models.Moderation.InfractionType.Ban) + && (x.Type == Modix.Models.Moderation.InfractionType.Mute || x.Type == Modix.Models.Moderation.InfractionType.Ban) && outranksValues[x.Subject.Id], x.DeleteAction is null @@ -121,8 +120,7 @@ public async Task CreateInfractionAsync(Shared.Models.Infractions.InfractionCrea await _moderationService.CreateInfractionAsync( UserGuild.Id, SocketUser.Id, - // TODO: - (Data.Models.Moderation.InfractionType)(int)creationData.Type, + creationData.Type, creationData.SubjectId, creationData.Reason, creationData.Duration); From d852a9eaf49421557c23f413db51a2daef91af89 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:53:10 +0200 Subject: [PATCH 42/46] Move PromotionCampaignEntityExtensions to Modix.Models project --- src/Modix.Bot/Modules/PromotionsModule.cs | 5 +++-- .../PromotionCampaignEntityExtensions.cs | 16 ---------------- .../PromotionCampaignEntityExtensions.cs | 12 ++++++++++++ .../Promotions/PromotionsService.cs | 1 + src/Modix.Web.Wasm/Pages/Promotions.razor | 9 +++------ 5 files changed, 19 insertions(+), 24 deletions(-) delete mode 100644 src/Modix.Data/Utilities/PromotionCampaignEntityExtensions.cs create mode 100644 src/Modix.Models/Utilities/PromotionCampaignEntityExtensions.cs diff --git a/src/Modix.Bot/Modules/PromotionsModule.cs b/src/Modix.Bot/Modules/PromotionsModule.cs index 60ee3bc0..4daa95dd 100644 --- a/src/Modix.Bot/Modules/PromotionsModule.cs +++ b/src/Modix.Bot/Modules/PromotionsModule.cs @@ -22,6 +22,7 @@ using Modix.Data.Utilities; using Modix.Models.Core; using Modix.Models.Promotions; +using Modix.Models.Utilities; using Modix.Services.CommandHelp; using Modix.Services.Promotions; using Modix.Services.Utilities; @@ -336,7 +337,7 @@ public async Task AcceptAsync( return; } - var timeRemaining = campaign.GetTimeUntilCampaignCanBeClosed(); + var timeRemaining = campaign.CreateAction.Created.GetTimeUntilCampaignCanBeClosed(); if (timeRemaining.TotalMinutes > 1) { @@ -443,7 +444,7 @@ await message.ModifyAsync(x => _ => Color.Red, }; - var expectedCloseTime = campaign.GetExpectedCampaignCloseTimeStamp(); + var expectedCloseTime = campaign.CreateAction.Created.GetExpectedCampaignCloseTimeStamp(); var timeRemainingLabel = campaign.Outcome switch { diff --git a/src/Modix.Data/Utilities/PromotionCampaignEntityExtensions.cs b/src/Modix.Data/Utilities/PromotionCampaignEntityExtensions.cs deleted file mode 100644 index d9848127..00000000 --- a/src/Modix.Data/Utilities/PromotionCampaignEntityExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Modix.Data.Models.Promotions; - -namespace Modix.Data.Utilities -{ - public static class PromotionCampaignEntityExtensions - { - public static readonly TimeSpan CampaignAcceptCooldown = TimeSpan.FromHours(48); - - public static TimeSpan GetTimeUntilCampaignCanBeClosed(this PromotionCampaignSummary campaign) - => campaign.CreateAction.Created.Add(CampaignAcceptCooldown) - DateTimeOffset.UtcNow; - - public static DateTimeOffset GetExpectedCampaignCloseTimeStamp(this PromotionCampaignSummary campaign) - => campaign.CreateAction.Created.Add(CampaignAcceptCooldown); - } -} diff --git a/src/Modix.Models/Utilities/PromotionCampaignEntityExtensions.cs b/src/Modix.Models/Utilities/PromotionCampaignEntityExtensions.cs new file mode 100644 index 00000000..93291f62 --- /dev/null +++ b/src/Modix.Models/Utilities/PromotionCampaignEntityExtensions.cs @@ -0,0 +1,12 @@ +namespace Modix.Models.Utilities; + +public static class PromotionCampaignEntityExtensions +{ + public static readonly TimeSpan CampaignAcceptCooldown = TimeSpan.FromHours(48); + + public static TimeSpan GetTimeUntilCampaignCanBeClosed(this DateTimeOffset campaignCreationDate) + => campaignCreationDate.Add(CampaignAcceptCooldown) - DateTimeOffset.UtcNow; + + public static DateTimeOffset GetExpectedCampaignCloseTimeStamp(this DateTimeOffset campaignCreationDate) + => campaignCreationDate.Add(CampaignAcceptCooldown); +} diff --git a/src/Modix.Services/Promotions/PromotionsService.cs b/src/Modix.Services/Promotions/PromotionsService.cs index 41d1e46c..a895eeb1 100644 --- a/src/Modix.Services/Promotions/PromotionsService.cs +++ b/src/Modix.Services/Promotions/PromotionsService.cs @@ -17,6 +17,7 @@ using Modix.Data.Utilities; using Modix.Models.Core; using Modix.Models.Promotions; +using Modix.Models.Utilities; using Modix.Services.Core; using Modix.Services.Utilities; diff --git a/src/Modix.Web.Wasm/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor index cf346761..18040a6c 100644 --- a/src/Modix.Web.Wasm/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -6,6 +6,7 @@ @using Microsoft.AspNetCore.Components.Authorization @using Modix.Models.Core @using Modix.Models.Promotions +@using Modix.Models.Utilities @using Modix.Web.Models; @using Modix.Web.Shared.Models.Common @using Modix.Web.Shared.Models.Promotions @@ -318,13 +319,9 @@ var username = campaign.SubjectName; bool force = false; - // TODO: Fix this, this should not be hardcoded - // if (timeSince < PromotionCampaignEntityExtensions.CampaignAcceptCooldown) - var campaignAcceptCooldown = TimeSpan.FromHours(48); - if (timeSince < campaignAcceptCooldown) + if (timeSince < PromotionCampaignEntityExtensions.CampaignAcceptCooldown) { - var timeLeft = campaign.Created.Add(campaignAcceptCooldown) - DateTimeOffset.UtcNow; - var timeLeftHumanized = timeLeft.Humanize(3); + var timeLeftHumanized = campaign.Created.GetTimeUntilCampaignCanBeClosed().Humanize(3); var dialogParams = new DialogParameters { { x => x.Content, $"There is {timeLeftHumanized} left on the campaign. Do you want to force accept the campaign for {username}?" } From 882fb4b6c2da0367bb3b09d06f7c9473ff6b96c4 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:03:28 +0200 Subject: [PATCH 43/46] Refactor table query models in Infractions/DeletedMessages --- .../Models/DeletedMessages/DeletedMessagesQuery.cs | 5 +++++ .../Models/Infractions/InfractionsQuery.cs | 4 ++++ src/Modix.Web.Shared/Modix.Web.Shared.csproj | 4 ++++ .../Components/Infractions/DeletedMessages.razor | 2 +- .../Components/Infractions/Infractions.razor | 2 +- .../Controllers/DeletedMessagesController.cs | 14 +++----------- src/Modix.Web/Controllers/InfractionsController.cs | 13 +++---------- 7 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessagesQuery.cs create mode 100644 src/Modix.Web.Shared/Models/Infractions/InfractionsQuery.cs diff --git a/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessagesQuery.cs b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessagesQuery.cs new file mode 100644 index 00000000..077a182f --- /dev/null +++ b/src/Modix.Web.Shared/Models/DeletedMessages/DeletedMessagesQuery.cs @@ -0,0 +1,5 @@ +using MudBlazor; + +namespace Modix.Web.Shared.Models.DeletedMessages; + +public record DeletedMessagesQuery(TableFilter Filter, TableState TableState); diff --git a/src/Modix.Web.Shared/Models/Infractions/InfractionsQuery.cs b/src/Modix.Web.Shared/Models/Infractions/InfractionsQuery.cs new file mode 100644 index 00000000..96ec600b --- /dev/null +++ b/src/Modix.Web.Shared/Models/Infractions/InfractionsQuery.cs @@ -0,0 +1,4 @@ +using MudBlazor; + +namespace Modix.Web.Shared.Models.Infractions; +public record InfractionsQuery(TableFilter Filter, TableState TableState); diff --git a/src/Modix.Web.Shared/Modix.Web.Shared.csproj b/src/Modix.Web.Shared/Modix.Web.Shared.csproj index c13714a1..680ff400 100644 --- a/src/Modix.Web.Shared/Modix.Web.Shared.csproj +++ b/src/Modix.Web.Shared/Modix.Web.Shared.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor index b6367f9c..4a94eae5 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor @@ -172,7 +172,7 @@ private async Task> LoadDeletedMessages(TableState tableState) { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync("api/deletedmessages", new { tableFilter = _tableFilter, tableState = tableState }); + var response = await client.PutAsJsonAsync("api/deletedmessages", new DeletedMessagesQuery(_tableFilter, tableState)); response.EnsureSuccessStatusCode(); diff --git a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index a7cae3d2..cb59e9b2 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -416,7 +416,7 @@ { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync("api/infractions", new { tableFilter = _tableFilter, tableState = tableState }); + var response = await client.PutAsJsonAsync("api/infractions", new InfractionsQuery(_tableFilter, tableState)); response.EnsureSuccessStatusCode(); var infractions = await response.Content.ReadFromJsonAsync(); diff --git a/src/Modix.Web/Controllers/DeletedMessagesController.cs b/src/Modix.Web/Controllers/DeletedMessagesController.cs index 0014fa0f..782b2032 100644 --- a/src/Modix.Web/Controllers/DeletedMessagesController.cs +++ b/src/Modix.Web/Controllers/DeletedMessagesController.cs @@ -26,19 +26,11 @@ public DeletedMessagesController(ModerationService moderationService, DiscordSoc _moderationService = moderationService; } - // TODO: Refactor this - public class Intermediate - { - public TableFilter tableFilter { get; set; } - public TableState tableState { get; set; } - } - - [HttpPut] - public async Task GetDeletedMessagesBatchAsync(Intermediate inter) + public async Task GetDeletedMessagesBatchAsync(DeletedMessagesQuery deletedMessagesQuery) { - var tableState = inter.tableState; - var tableFilter = inter.tableFilter; + var tableState = deletedMessagesQuery.TableState; + var tableFilter = deletedMessagesQuery.Filter; var searchCriteria = new DeletedMessageSearchCriteria { diff --git a/src/Modix.Web/Controllers/InfractionsController.cs b/src/Modix.Web/Controllers/InfractionsController.cs index 3f69843e..8fe8588e 100644 --- a/src/Modix.Web/Controllers/InfractionsController.cs +++ b/src/Modix.Web/Controllers/InfractionsController.cs @@ -23,19 +23,12 @@ public InfractionsController(ModerationService moderationService, DiscordSocketC _moderationService = moderationService; } - // TODO: Refactor this - public class Intermediate - { - public TableFilter tableFilter { get; set; } - public TableState tableState { get; set; } - } - [HttpPut] [Authorize(Roles = nameof(AuthorizationClaim.ModerationRead))] - public async Task GetInfractionsAsync(Intermediate inter) + public async Task GetInfractionsAsync(InfractionsQuery infractionsQuery) { - var tableState = inter.tableState; - var tableFilter = inter.tableFilter; + var tableState = infractionsQuery.TableState; + var tableFilter = infractionsQuery.Filter; var sortingCriteria = new[] { From ceaae6a152d0f7b3bd956b6d2d6b4e5a3535ec13 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:05:24 +0200 Subject: [PATCH 44/46] Dispose HttpResponseMessage --- .../Components/Infractions/DeletedMessages.razor | 2 +- .../Components/Infractions/Infractions.razor | 8 ++++---- src/Modix.Web.Wasm/Pages/CreatePromotion.razor | 2 +- src/Modix.Web.Wasm/Pages/Promotions.razor | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor index 4a94eae5..d4be8be5 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/DeletedMessages.razor @@ -172,7 +172,7 @@ private async Task> LoadDeletedMessages(TableState tableState) { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync("api/deletedmessages", new DeletedMessagesQuery(_tableFilter, tableState)); + using var response = await client.PutAsJsonAsync("api/deletedmessages", new DeletedMessagesQuery(_tableFilter, tableState)); response.EnsureSuccessStatusCode(); diff --git a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index cb59e9b2..2dbb96b2 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -289,7 +289,7 @@ } using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PostAsync($"api/infractions/rescind/{infraction.Id}", default); + using var response = await client.PostAsync($"api/infractions/rescind/{infraction.Id}", default); response.EnsureSuccessStatusCode(); @@ -320,7 +320,7 @@ } using var client = HttpClientFactory.CreateClient("api"); - var response = await client.DeleteAsync($"api/infractions/{infraction.Id}"); + using var response = await client.DeleteAsync($"api/infractions/{infraction.Id}"); response.EnsureSuccessStatusCode(); @@ -346,7 +346,7 @@ try { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PostAsJsonAsync("api/infractions/create", new InfractionCreationData(_infractionType, _selectedUser!.UserId, _infractionReason!, duration)); + using var response = await client.PostAsJsonAsync("api/infractions/create", new InfractionCreationData(_infractionType, _selectedUser!.UserId, _infractionReason!, duration)); response.EnsureSuccessStatusCode(); @@ -416,7 +416,7 @@ { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync("api/infractions", new InfractionsQuery(_tableFilter, tableState)); + using var response = await client.PutAsJsonAsync("api/infractions", new InfractionsQuery(_tableFilter, tableState)); response.EnsureSuccessStatusCode(); var infractions = await response.Content.ReadFromJsonAsync(); diff --git a/src/Modix.Web.Wasm/Pages/CreatePromotion.razor b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor index 35e51651..c42d995c 100644 --- a/src/Modix.Web.Wasm/Pages/CreatePromotion.razor +++ b/src/Modix.Web.Wasm/Pages/CreatePromotion.razor @@ -104,7 +104,7 @@ { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync("api/campaigns/create", new PromotionCreationData(_selectedUser!.UserId, _promotionComment)); + using var response = await client.PutAsJsonAsync("api/campaigns/create", new PromotionCreationData(_selectedUser!.UserId, _promotionComment)); response.EnsureSuccessStatusCode(); diff --git a/src/Modix.Web.Wasm/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor index 18040a6c..66a84639 100644 --- a/src/Modix.Web.Wasm/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -257,7 +257,7 @@ try { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PutAsJsonAsync($"api/campaigns/{campaignId}/createcomment", new CampaignCommentData(default, sentiment, content, default, default)); + using var response = await client.PutAsJsonAsync($"api/campaigns/{campaignId}/createcomment", new CampaignCommentData(default, sentiment, content, default, default)); response.EnsureSuccessStatusCode(); @@ -294,7 +294,7 @@ try { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PatchAsJsonAsync("api/campaigns/updatecomment", new CampaignCommentData(commentId, newPromotionSentiment, newContent, default, default)); + using var response = await client.PatchAsJsonAsync("api/campaigns/updatecomment", new CampaignCommentData(commentId, newPromotionSentiment, newContent, default, default)); response.EnsureSuccessStatusCode(); @@ -342,7 +342,7 @@ try { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PostAsync($"api/campaigns/{campaign.Id}/accept/{force}", default); + using var response = await client.PostAsync($"api/campaigns/{campaign.Id}/accept/{force}", default); response.EnsureSuccessStatusCode(); @@ -362,7 +362,7 @@ try { using var client = HttpClientFactory.CreateClient("api"); - var response = await client.PostAsync($"api/campaigns/{campaign.Id}/reject", default); + using var response = await client.PostAsync($"api/campaigns/{campaign.Id}/reject", default); response.EnsureSuccessStatusCode(); From 83ce8524dc27e36d910f125778f13a84eba74de9 Mon Sep 17 00:00:00 2001 From: calledude <22471295+calledude@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:15:36 +0200 Subject: [PATCH 45/46] Stop using CascadingAuthenticationState (components will use AuthenticationStateProvider/AuthState task as needed) --- .../Components/Configuration/Channels.razor | 138 ++++---- .../Configuration/IndividualDesignation.razor | 84 +++-- .../Components/Configuration/Roles.razor | 142 ++++---- .../Components/Infractions/Infractions.razor | 304 +++++++++--------- src/Modix.Web.Wasm/Pages/Configuration.razor | 70 ++-- src/Modix.Web.Wasm/Pages/Logs.razor | 54 ++-- src/Modix.Web.Wasm/Pages/Promotions.razor | 228 +++++++------ src/Modix.Web.Wasm/Pages/Tags.razor | 138 ++++---- 8 files changed, 567 insertions(+), 591 deletions(-) diff --git a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor index 59a72156..7ce20995 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/Channels.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Channels.razor @@ -9,81 +9,79 @@ Modix - Channels Channel Designations - - @if (DesignatedChannelMappings is not null && DesignatedChannelTypes is not null) - { - - - Assign a Channel - - - - Designation - - @foreach (var designation in DesignatedChannelTypes) - { - - } - - - - - Assign - - - Cancel - - - - - - @foreach (var designatedChannelType in DesignatedChannelTypes.OrderBy(x => x.ToString())) +@if (DesignatedChannelMappings is not null && DesignatedChannelTypes is not null) +{ + + + Assign a Channel + + + + Designation + + @foreach (var designation in DesignatedChannelTypes) { - -
-
- - @designatedChannelType.ToString().Titleize() + + } + + + + + Assign + + + Cancel + + + + + + @foreach (var designatedChannelType in DesignatedChannelTypes.OrderBy(x => x.ToString())) + { + +
+
+ + @designatedChannelType.ToString().Titleize() + + @if (!DesignatedChannelMappings.TryGetValue(designatedChannelType, out var channelDesignations) || !channelDesignations.Any()) + { + + NONE ASSIGNED - @if (!DesignatedChannelMappings.TryGetValue(designatedChannelType, out var channelDesignations) || !channelDesignations.Any()) - { - - NONE ASSIGNED - - } - else + } + else + { + @foreach (var designatedChannelMapping in channelDesignations) { - @foreach (var designatedChannelMapping in channelDesignations) - { - - } + } -
- -
- - - -
+ }
-
- - } -
-
- } - + +
+ + + +
+
+ + + } + + +} @code { [Inject] diff --git a/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor b/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor index d04d7a5a..82ef7baf 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/IndividualDesignation.razor @@ -1,49 +1,47 @@ @using Microsoft.AspNetCore.Components.Authorization @using MudBlazor - - - - @NamePrefix@Name - - - @if (!_showConfirm) - { - - X - - } - else - { - Remove Designation? - - - Yes - - - No - - } - - - + + + @NamePrefix@Name + + + @if (!_showConfirm) + { + + X + + } + else + { + Remove Designation? + + + Yes + + + No + + } + + @code { [Parameter, EditorRequired] diff --git a/src/Modix.Web.Wasm/Components/Configuration/Roles.razor b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor index 947c8efe..9b3433e2 100644 --- a/src/Modix.Web.Wasm/Components/Configuration/Roles.razor +++ b/src/Modix.Web.Wasm/Components/Configuration/Roles.razor @@ -8,83 +8,81 @@ Modix - Roles Role Designations - - @if (DesignatedRoleMappings is not null && DesignatedRoleTypes is not null) - { - - - Assign a Role - - - - - Designation - - @foreach (var designation in DesignatedRoleTypes) - { - - } - - - - - Assign - - - Cancel - - - - - - @foreach (var designatedRoleType in DesignatedRoleTypes.OrderBy(x => x.ToString())) +@if (DesignatedRoleMappings is not null && DesignatedRoleTypes is not null) +{ + + + Assign a Role + + + + + Designation + + @foreach (var designation in DesignatedRoleTypes) { - -
-
- - @designatedRoleType.ToString().Titleize() + + } + + + + + Assign + + + Cancel + + + + + + @foreach (var designatedRoleType in DesignatedRoleTypes.OrderBy(x => x.ToString())) + { + +
+
+ + @designatedRoleType.ToString().Titleize() + + @if (!DesignatedRoleMappings.TryGetValue(designatedRoleType, out var roleDesignations) || !roleDesignations.Any()) + { + + NONE ASSIGNED - @if (!DesignatedRoleMappings.TryGetValue(designatedRoleType, out var roleDesignations) || !roleDesignations.Any()) - { - - NONE ASSIGNED - - } - else + } + else + { + @foreach (var designatedRoleMapping in roleDesignations) { - @foreach (var designatedRoleMapping in roleDesignations) - { - - } + } -
- -
- - - -
+ }
-
- - } -
-
- } - + +
+ + + +
+
+ + + } + + +} @code { [Inject] diff --git a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor index 2dbb96b2..c333ae0a 100644 --- a/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor +++ b/src/Modix.Web.Wasm/Components/Infractions/Infractions.razor @@ -11,169 +11,167 @@ Modix - Infractions - - - - - Create Infraction - - - - - - @user.Name - - - - Infraction -
-
- - - - - - - - - - - - - - -
- + + + Create Infraction + + + + + + @user.Name + + + + Infraction +
+
+ + + + + + + + + + + + + +
+ +
- @if (_infractionType == InfractionType.Mute) - { - Duration -
- - - - - -
- } -
- - - Save - - - Cancel - -
- - - - -
-
- Create - Refresh -
- -
- - + @if (_infractionType == InfractionType.Mute) + { + Duration +
+ + + + +
+ } + + + + Save + + + Cancel + + + + + + +
+
+ Create + Refresh
- - - - - Id - - - - Type - - @foreach (var infractionType in Enum.GetValues()) - { - - } - - - - Created On - - - Subject - - - - Creator - - - Reason - @if (_showState) - { - State - } - @if (_canDeleteInfractions || _canRescind) + +
+ + +
+
+ + + + + Id + + + + Type + + @foreach (var infractionType in Enum.GetValues()) + { + + } + + + + Created On + + + Subject + + + + Creator + + + Reason + @if (_showState) + { + State + } + @if (_canDeleteInfractions || _canRescind) + { + Actions + } + + + @infraction.Id + @infraction.Type + @infraction.Created.ToString("MM/dd/yy, h:mm:ss tt") + @infraction.SubjectName + @infraction.CreatedBy + @infraction.Reason + @if (_showState) + { + if(!infraction.IsRescinded && !infraction.IsDeleted) { - Actions + Active } - - - @infraction.Id - @infraction.Type - @infraction.Created.ToString("MM/dd/yy, h:mm:ss tt") - @infraction.SubjectName - @infraction.CreatedBy - @infraction.Reason - @if (_showState) + else if (infraction.IsDeleted) { - if(!infraction.IsRescinded && !infraction.IsDeleted) - { - Active - } - else if (infraction.IsDeleted) - { - Deleted - } - else - { - Rescinded - } + Deleted } - @if (_canDeleteInfractions || _canRescind) + else { - -
- @if (infraction.CanBeDeleted) - { - Delete - } - @if (infraction.CanBeRescinded) - { - Rescind - } -
-
+ Rescinded } -
- - - -
-
- + } + @if (_canDeleteInfractions || _canRescind) + { + +
+ @if (infraction.CanBeDeleted) + { + Delete + } + @if (infraction.CanBeRescinded) + { + Rescind + } +
+
+ } + + + + + + + @code { [Parameter] diff --git a/src/Modix.Web.Wasm/Pages/Configuration.razor b/src/Modix.Web.Wasm/Pages/Configuration.razor index a4ae3360..183c443b 100644 --- a/src/Modix.Web.Wasm/Pages/Configuration.razor +++ b/src/Modix.Web.Wasm/Pages/Configuration.razor @@ -15,44 +15,40 @@ Modix - Configuration - - -
-
- Configuration - - - - - - - - - -
-
- @if (SubPage == "roles") - { - - - - } - else if (SubPage == "channels") - { - - - - } - else if (SubPage == "claims") - { - - - - } -
+
+
+ Configuration + + + + + + + + +
- - +
+ @if (SubPage == "roles") + { + + + + } + else if (SubPage == "channels") + { + + + + } + else if (SubPage == "claims") + { + + + + } +
+
@code { diff --git a/src/Modix.Web.Wasm/Pages/Logs.razor b/src/Modix.Web.Wasm/Pages/Logs.razor index 6405a40f..d4dbd755 100644 --- a/src/Modix.Web.Wasm/Pages/Logs.razor +++ b/src/Modix.Web.Wasm/Pages/Logs.razor @@ -12,36 +12,32 @@ Modix - Logs - - -
-
- Logs - - - - - - - -
-
- @if (SubPage == "infractions") - { - - - - } - else if (SubPage == "deletedMessages") - { - - - - } -
+
+
+ Logs + + + + + + +
- - +
+ @if (SubPage == "infractions") + { + + + + } + else if (SubPage == "deletedMessages") + { + + + + } +
+
@code { [Parameter] diff --git a/src/Modix.Web.Wasm/Pages/Promotions.razor b/src/Modix.Web.Wasm/Pages/Promotions.razor index 66a84639..d38b010b 100644 --- a/src/Modix.Web.Wasm/Pages/Promotions.razor +++ b/src/Modix.Web.Wasm/Pages/Promotions.razor @@ -18,132 +18,128 @@ Modix - Promotions - - - - Promotion Campaigns -
- - Start One -
- - @foreach (var (roleColor, campaign) in Campaigns - .Where(x => _showInactive ? true : !x.Campaign.IsClosed) - .OrderByDescending(x => !x.Campaign.IsClosed) - .ThenByDescending(x => x.Campaign.Created)) + + Promotion Campaigns +
+ + Start One +
+ + @foreach (var (roleColor, campaign) in Campaigns + .Where(x => _showInactive ? true : !x.Campaign.IsClosed) + .OrderByDescending(x => !x.Campaign.IsClosed) + .ThenByDescending(x => x.Campaign.Created)) + { + var icon = campaign.Outcome switch { - var icon = campaign.Outcome switch - { - PromotionCampaignOutcome.Accepted => Icons.Material.Filled.Check, - PromotionCampaignOutcome.Rejected => Icons.Material.Filled.NotInterested, - PromotionCampaignOutcome.Failed => Icons.Material.Filled.Error, - _ => Icons.Material.Filled.HowToVote - }; - - var sentimentRatio = campaign.IsCurrentUserCampaign ? 0d : (double)campaign.ApproveCount / (campaign.ApproveCount + campaign.OpposeCount); - var sentimentColor = sentimentRatio switch - { - _ when campaign.IsCurrentUserCampaign => Color.Transparent, - > 0.67 => Color.Success, - > 0.33 => Color.Warning, - _ => Color.Error - }; - - - -
-
- - - - - @campaign.SubjectName - - - @campaign.TargetRoleName -
+ PromotionCampaignOutcome.Accepted => Icons.Material.Filled.Check, + PromotionCampaignOutcome.Rejected => Icons.Material.Filled.NotInterested, + PromotionCampaignOutcome.Failed => Icons.Material.Filled.Error, + _ => Icons.Material.Filled.HowToVote + }; -
- @if (campaign.Outcome is null) - { - - - - - - - } - -
+ var sentimentRatio = campaign.IsCurrentUserCampaign ? 0d : (double)campaign.ApproveCount / (campaign.ApproveCount + campaign.OpposeCount); + var sentimentColor = sentimentRatio switch + { + _ when campaign.IsCurrentUserCampaign => Color.Transparent, + > 0.67 => Color.Success, + > 0.33 => Color.Warning, + _ => Color.Error + }; -
-
-
- - @(campaign.IsCurrentUserCampaign ? "?" : campaign.ApproveCount.ToString()) -
-
- - @(campaign.IsCurrentUserCampaign ? "?" : campaign.OpposeCount.ToString()) -
+ + +
+
+ + + + + @campaign.SubjectName + + + @campaign.TargetRoleName +
+ +
+ @if (campaign.Outcome is null) + { + + + + + + + } + +
+ +
+
+
+ + @(campaign.IsCurrentUserCampaign ? "?" : campaign.ApproveCount.ToString()) +
+
+ + @(campaign.IsCurrentUserCampaign ? "?" : campaign.OpposeCount.ToString())
-
+
- - - Campaign started @campaign.Created.ToString("MM/dd/yy, h:mm:ss tt") - - @if (campaign.IsCurrentUserCampaign) +
+
+ + Campaign started @campaign.Created.ToString("MM/dd/yy, h:mm:ss tt") + + @if (campaign.IsCurrentUserCampaign) + { + + Sorry, you aren't allowed to see comments on your own campaign. + + } + else if (!CampaignCommentData.ContainsKey(campaign.Id)) + { + + } + else + { + foreach (var comment in CampaignCommentData[campaign.Id].Values.OrderByDescending(x => x.CreatedAt)) { - - Sorry, you aren't allowed to see comments on your own campaign. - - } - else if (!CampaignCommentData.ContainsKey(campaign.Id)) - { - + var sentimentIcon = comment.PromotionSentiment == PromotionSentiment.Approve ? Icons.Material.Filled.ThumbUp : Icons.Material.Filled.ThumbDown; +
+ + @comment.Content + + @if (comment.IsFromCurrentUser && !campaign.IsClosed) + { + + Edit + + } + @comment.CreatedAt.ToString("MM/dd/yy, h:mm:ss tt") +
+ } - else - { - foreach (var comment in CampaignCommentData[campaign.Id].Values.OrderByDescending(x => x.CreatedAt)) - { - var sentimentIcon = comment.PromotionSentiment == PromotionSentiment.Approve ? Icons.Material.Filled.ThumbUp : Icons.Material.Filled.ThumbDown; -
- - @comment.Content - - @if (comment.IsFromCurrentUser && !campaign.IsClosed) - { - - Edit - - } - @comment.CreatedAt.ToString("MM/dd/yy, h:mm:ss tt") -
- - } - if (!campaign.IsClosed && !CampaignCommentData[campaign.Id].Any(x => x.Value.IsFromCurrentUser)) - { - - } + if (!campaign.IsClosed && !CampaignCommentData[campaign.Id].Any(x => x.Value.IsFromCurrentUser)) + { + } -
-
- } - - - - + } + + + } + +