From 42e9f18fb69fea5d62cbc30ab438f601af854589 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 22 Jun 2024 11:28:58 +0200 Subject: [PATCH 01/16] Made moonlight able to compile and somehwat run with MoonCore.Blazor and the new logging system --- Moonlight/Core/CoreFeature.cs | 44 ++++---- Moonlight/Core/Events/CoreEvents.cs | 11 +- Moonlight/Core/Helpers/HostSystemHelper.cs | 20 +++- .../Core/Http/Controllers/AssetController.cs | 9 +- .../Core/Http/Controllers/AvatarController.cs | 10 +- .../Http/Middleware/DebugLogMiddleware.cs | 6 +- .../UserCount.cs | 4 +- .../GreetingMessages.cs | 4 +- .../Abstractions/Feature/PreInitContext.cs | 1 + .../Feature/SessionInitContext.cs | 2 +- .../Core/Models/Forms/ChangePasswordForm.cs | 5 +- .../Core/Models/Forms/Users/UpdateUserForm.cs | 3 +- Moonlight/Core/Models/Session.cs | 2 +- Moonlight/Core/Services/FeatureService.cs | 38 +++---- Moonlight/Core/Services/HotKeyService.cs | 6 +- Moonlight/Core/Services/IdentityService.cs | 7 +- Moonlight/Core/Services/MoonlightService.cs | 10 +- Moonlight/Core/Services/PluginService.cs | 16 +-- Moonlight/Core/Services/StartupJobService.cs | 9 +- Moonlight/Core/Services/UnloadService.cs | 11 +- Moonlight/Core/UI/Components/Auth/Login.razor | 3 +- .../Core/UI/Components/Auth/Register.razor | 4 +- .../UI/Components/Auth/TwoFactorWizard.razor | 2 +- .../Cards/TimeBasedGreetingMessages.razor | 1 + .../UI/Components/Partials/AppHeader.razor | 3 +- .../Partials/CookieConsentBanner.razor | 1 + .../Partials/SoftErrorHandler.razor | 4 +- Moonlight/Core/UI/Layouts/MainLayout.razor | 5 + Moonlight/Core/UI/Views/Account/Index.razor | 3 +- .../Core/UI/Views/Account/Security.razor | 1 - Moonlight/Core/UI/Views/Admin/Api/Index.razor | 14 +-- Moonlight/Core/UI/Views/Admin/Api/Keys.razor | 33 +++--- .../Core/UI/Views/Admin/Sys/Diagnose.razor | 5 +- Moonlight/Core/UI/Views/Admin/Sys/Index.razor | 3 +- .../Core/UI/Views/Admin/Sys/Permissions.razor | 12 +- .../Core/UI/Views/Admin/Sys/Settings.razor | 8 +- .../Core/UI/Views/Admin/Users/Index.razor | 27 +++-- .../Core/UI/Views/Admin/Users/Sessions.razor | 91 +++++++-------- .../FileManager/FileManagerFeature.cs | 8 +- .../Http/Controllers/DownloadController.cs | 6 +- .../Http/Controllers/UploadController.cs | 6 +- .../Implementations/ArchiveContextAction.cs | 66 +++++------ .../Implementations/ArchiveSelectionAction.cs | 61 +++++----- .../Implementations/CreateFileAction.cs | 29 ++--- .../Implementations/CreateFolderAction.cs | 17 +-- .../Implementations/DeleteContextAction.cs | 3 +- .../Implementations/DeleteSelectionAction.cs | 30 ++--- .../Implementations/DownloadContextAction.cs | 15 +-- .../Implementations/ExtractContextAction.cs | 9 +- .../Implementations/MoveContextAction.cs | 3 +- .../Implementations/MoveSelectionAction.cs | 7 +- .../Implementations/RenameContextAction.cs | 18 +-- .../Services/FileManagerInteropService.cs | 17 ++- .../UI/Components/FileEditor.razor | 5 +- .../UI/Components/FileManager.razor | 88 ++------------- .../UI/Components/FolderSelectModal.razor | 61 ++++++++++ .../Features/Servers/Events/ServerEvents.cs | 11 +- .../Servers/Extensions/NodeExtensions.cs | 9 +- .../Features/Servers/Helpers/ServerConsole.cs | 28 +++-- .../Http/Controllers/ServersController.cs | 15 ++- .../Columns}/ServerCount.cs | 4 +- .../Components}/NodeOverview.cs | 4 +- .../Components/UserDashboardServerCount.cs | 5 +- .../Forms/Admin/Servers/CreateServerForm.cs | 20 ++-- .../Forms/Users/Networks/CreateNetworkForm.cs | 3 +- Moonlight/Features/Servers/ServersFeature.cs | 8 +- .../Features/Servers/Services/NodeService.cs | 7 +- .../Servers/Services/ServerBackupService.cs | 3 +- .../Servers/Services/ServerScheduleService.cs | 9 +- .../Servers/Services/ServerService.cs | 7 +- .../Servers/UI/Components/Terminal.razor | 2 +- .../VariableViews/NumberVariableView.razor | 1 - .../VariableViews/SelectVariableView.razor | 2 +- .../VariableViews/TextVariableView.razor | 2 +- .../VariableViews/ToggleVariableView.razor | 2 +- .../UI/ImageComponents/ImageDetails.razor | 5 +- .../ImageComponents/ImageDockerImages.razor | 6 +- .../UI/ImageComponents/ImageInstall.razor | 4 +- .../UI/ImageComponents/ImagePower.razor | 4 +- .../UI/ImageComponents/ImageVariables.razor | 4 +- .../Servers/UI/Layouts/UserLayout.razor | 90 ++++++++------- .../UI/NodeComponents/NodeAllocations.razor | 42 +++---- .../Servers/UI/NodeComponents/NodeLogs.razor | 6 +- .../Servers/UI/NodeComponents/NodeSetup.razor | 1 + .../Servers/UI/UserViews/Backups.razor | 27 ++--- .../Servers/UI/UserViews/Console.razor | 2 +- .../Features/Servers/UI/UserViews/Files.razor | 2 +- .../Servers/UI/UserViews/Network.razor | 24 ++-- .../Features/Servers/UI/UserViews/Reset.razor | 63 +++++------ .../Servers/UI/UserViews/Schedules.razor | 72 ++++++------ .../Servers/UI/UserViews/Variables.razor | 6 +- .../Servers/UI/Views/Admin/Images/Index.razor | 106 +++++++++--------- .../Servers/UI/Views/Admin/Images/View.razor | 2 +- .../Servers/UI/Views/Admin/Index.razor | 31 +++-- .../Servers/UI/Views/Admin/Manager.razor | 47 ++++---- .../Servers/UI/Views/Admin/Nodes/Index.razor | 31 ++--- .../Servers/UI/Views/Servers/Index.razor | 7 +- .../Servers/UI/Views/Servers/Networks.razor | 14 +-- Moonlight/Moonlight.csproj | 4 +- Moonlight/Pages/_Host.cshtml | 3 + Moonlight/Program.cs | 75 ++++++++----- Moonlight/_Imports.razor | 8 +- 102 files changed, 913 insertions(+), 802 deletions(-) rename Moonlight/Core/Implementations/{UI/Admin/AdminColumns => AdminDashboard}/UserCount.cs (81%) rename Moonlight/Core/Implementations/{UI/Index => UserDashboard}/GreetingMessages.cs (83%) create mode 100644 Moonlight/Features/FileManager/UI/Components/FolderSelectModal.razor rename Moonlight/Features/Servers/Implementations/{UI/Admin/AdminColumns => AdminDashboard/Columns}/ServerCount.cs (80%) rename Moonlight/Features/Servers/Implementations/{UI/Admin/AdminComponents => AdminDashboard/Components}/NodeOverview.cs (79%) rename Moonlight/Features/Servers/Implementations/{UI => }/UserDashboard/Components/UserDashboardServerCount.cs (72%) diff --git a/Moonlight/Core/CoreFeature.cs b/Moonlight/Core/CoreFeature.cs index 7165ee44..6743a11f 100644 --- a/Moonlight/Core/CoreFeature.cs +++ b/Moonlight/Core/CoreFeature.cs @@ -3,14 +3,10 @@ using MoonCore.Abstractions; using MoonCore.Helpers; using MoonCore.Services; -using MoonCoreUI.Extensions; -using MoonCoreUI.Services; using Moonlight.Core.Configuration; using Moonlight.Core.Database; using Moonlight.Core.Database.Entities; using Moonlight.Core.Implementations.Diagnose; -using Moonlight.Core.Implementations.UI.Admin.AdminColumns; -using Moonlight.Core.Implementations.UI.Index; using Moonlight.Core.Interfaces; using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Interfaces.UI.User; @@ -21,10 +17,16 @@ using Moonlight.Core.Repositories; using Moonlight.Core.Services; using Microsoft.OpenApi.Models; +using MoonCore.Blazor.Extensions; +using MoonCore.Blazor.Services; +using MoonCore.Extensions; using Moonlight.Core.Attributes; using Moonlight.Core.Http.Middleware; +using Moonlight.Core.Implementations.AdminDashboard; using Moonlight.Core.Implementations.ApiDefinition; +using Moonlight.Core.Implementations.UserDashboard; using Swashbuckle.AspNetCore.SwaggerGen; +using IdentityService = Moonlight.Core.Services.IdentityService; namespace Moonlight.Core; @@ -54,25 +56,21 @@ public override Task OnPreInitialized(PreInitContext context) builder.Services.AddDbContext(); // - builder.Services.AddSingleton(new JwtService(config.Security.Token)); + builder.Services.AddSingleton(new JwtService( + config.Security.Token, + context.LoggerFactory.CreateLogger>() + ) + ); // Mooncore services builder.Services.AddScoped(typeof(Repository<>), typeof(GenericRepository<>)); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - - builder.Services.AddMoonCoreUi(configuration => + + builder.Services.AddMoonCore(configuration => { - configuration.ToastJavascriptPrefix = "moonlight.toasts"; - configuration.ModalJavascriptPrefix = "moonlight.modals"; - configuration.AlertJavascriptPrefix = "moonlight.alerts"; - configuration.ClipboardJavascriptPrefix = "moonlight.clipboard"; - configuration.FileDownloadJavascriptPrefix = "moonlight.utils"; + configuration.Identity.Token = config.Security.Token; }); + + builder.Services.AddMoonCoreBlazor(); // Add external services and blazor/asp.net stuff builder.Services.AddRazorPages(); @@ -183,7 +181,7 @@ public override async Task OnInitialized(InitContext context) Name = "Manage admin api access", Description = "Allows access to manage api keys and their permissions" }); - + await permissionService.Register(9999, new() { Name = "Manage system", @@ -218,9 +216,9 @@ await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), { using var scope = provider.CreateScope(); - var configService = scope.ServiceProvider.GetRequiredService>(); var userRepo = scope.ServiceProvider.GetRequiredService>(); var authenticationProvider = scope.ServiceProvider.GetRequiredService(); + var logger = scope.ServiceProvider.GetRequiredService>(); if (!configService.Get().Authentication.UseDefaultAuthentication) return; @@ -238,7 +236,7 @@ await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), if (registeredUser == null) { - Logger.Warn("Unable to create default user. Register function returned null"); + logger.LogWarning("Unable to create default user. Register function returned null"); return; } @@ -247,7 +245,7 @@ await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), user.Permissions = 9999; userRepo.Update(user); - Logger.Info($"Default login: Email: '{email}' Password: '{password}'"); + logger.LogInformation("Default login: Email: '{email}' Password: '{password}'", email, password); }); // Api @@ -255,7 +253,7 @@ await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), app.MapSwagger("/api/core/reference/openapi/{documentName}"); app.UseMiddleware(); - + await pluginService.RegisterImplementation(new InternalApiDefinition()); } diff --git a/Moonlight/Core/Events/CoreEvents.cs b/Moonlight/Core/Events/CoreEvents.cs index 5a02cfa6..cd3ece06 100644 --- a/Moonlight/Core/Events/CoreEvents.cs +++ b/Moonlight/Core/Events/CoreEvents.cs @@ -1,8 +1,15 @@ -using MoonCore.Helpers; +using MoonCore.Attributes; +using MoonCore.Helpers; namespace Moonlight.Core.Events; +[Singleton] public class CoreEvents { - public static SmartEventHandler OnMoonlightRestart { get; set; } = new(); + public CoreEvents(ILogger logger) + { + OnMoonlightRestart = new(logger); + } + + public SmartEventHandler OnMoonlightRestart { get; set; } } \ No newline at end of file diff --git a/Moonlight/Core/Helpers/HostSystemHelper.cs b/Moonlight/Core/Helpers/HostSystemHelper.cs index 0e33ffc9..8b8ec092 100644 --- a/Moonlight/Core/Helpers/HostSystemHelper.cs +++ b/Moonlight/Core/Helpers/HostSystemHelper.cs @@ -1,12 +1,21 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using MoonCore.Attributes; using MoonCore.Helpers; namespace Moonlight.Core.Helpers; -public static class HostSystemHelper +[Singleton] +public class HostSystemHelper { - public static Task GetOsName() + private readonly ILogger Logger; + + public HostSystemHelper(ILogger logger) + { + Logger = logger; + } + + public Task GetOsName() { try { @@ -48,21 +57,20 @@ public static Task GetOsName() } catch (Exception e) { - Logger.Warn("Error retrieving os information"); - Logger.Warn(e); + Logger.LogWarning("Error retrieving os information: {e}", e); return Task.FromResult("N/A"); } } - public static Task GetMemoryUsage() + public Task GetMemoryUsage() { var process = Process.GetCurrentProcess(); var bytes = process.PrivateMemorySize64; return Task.FromResult(bytes); } - public static Task GetCpuUsage() + public Task GetCpuUsage() { var process = Process.GetCurrentProcess(); var cpuTime = process.TotalProcessorTime; diff --git a/Moonlight/Core/Http/Controllers/AssetController.cs b/Moonlight/Core/Http/Controllers/AssetController.cs index 573f0b29..4f5b7384 100644 --- a/Moonlight/Core/Http/Controllers/AssetController.cs +++ b/Moonlight/Core/Http/Controllers/AssetController.cs @@ -9,13 +9,20 @@ namespace Moonlight.Core.Http.Controllers; [Route("api/core/asset")] public class AssetController : Controller { + private readonly ILogger Logger; + + public AssetController(ILogger logger) + { + Logger = logger; + } + [HttpGet("{name}/{*path}")] public async Task Get(string name, string path) { // Check for path transversal attacks if (path.Contains("..") || name.Contains("..")) { - Logger.Warn($"{HttpContext.Connection.RemoteIpAddress} tried to use path transversal attack: {name}/{path}"); + Logger.LogWarning("{remoteIp} tried to use path transversal attack: {name}/{path}", HttpContext.Connection.RemoteIpAddress, name, path); return NotFound(); } diff --git a/Moonlight/Core/Http/Controllers/AvatarController.cs b/Moonlight/Core/Http/Controllers/AvatarController.cs index 5a77a6d4..530199f2 100644 --- a/Moonlight/Core/Http/Controllers/AvatarController.cs +++ b/Moonlight/Core/Http/Controllers/AvatarController.cs @@ -8,6 +8,7 @@ using Moonlight.Core.Configuration; using Moonlight.Core.Database.Entities; using Moonlight.Core.Services; +using IdentityService = Moonlight.Core.Services.IdentityService; namespace Moonlight.Core.Http.Controllers; @@ -19,15 +20,17 @@ public class AvatarController : Controller private readonly Repository UserRepository; private readonly ConfigService ConfigService; private readonly IdentityService IdentityService; + private readonly ILogger Logger; public AvatarController( Repository userRepository, IdentityService identityService, - ConfigService configService) + ConfigService configService, ILogger logger) { UserRepository = userRepository; IdentityService = identityService; ConfigService = configService; + Logger = logger; } [HttpGet] @@ -88,11 +91,10 @@ private async Task GetAvatar(User user) catch (Exception e) { if(e is HttpRequestException requestException && requestException.InnerException is IOException ioException) - Logger.Warn($"Unable to fetch gravatar for user {user.Id}. Is moonlight inside a proxy requiring network?: {ioException.Message}"); + Logger.LogWarning("Unable to fetch gravatar for user {userId}. Is moonlight inside a proxy requiring network?: {message}", user.Id, ioException.Message); else { - Logger.Warn($"Unable to fetch gravatar for user {user.Id}"); - Logger.Warn(e); + Logger.LogWarning("Unable to fetch gravatar for user {userId}: {e}", user.Id, e); } return new MemoryStream(); diff --git a/Moonlight/Core/Http/Middleware/DebugLogMiddleware.cs b/Moonlight/Core/Http/Middleware/DebugLogMiddleware.cs index 3a679271..c55b78b1 100644 --- a/Moonlight/Core/Http/Middleware/DebugLogMiddleware.cs +++ b/Moonlight/Core/Http/Middleware/DebugLogMiddleware.cs @@ -4,16 +4,18 @@ namespace Moonlight.Core.Http.Middleware; public class DebugLogMiddleware { + private readonly ILogger Logger; private RequestDelegate Next; - public DebugLogMiddleware(RequestDelegate next) + public DebugLogMiddleware(RequestDelegate next, ILogger logger) { Next = next; + Logger = logger; } public async Task Invoke(HttpContext context) { - Logger.Debug($"[{context.Request.Method.ToUpper()}] {context.Request.Path}"); + Logger.LogDebug("[{method}] {path}", context.Request.Method.ToUpper(), context.Request.Path); await Next(context); } diff --git a/Moonlight/Core/Implementations/UI/Admin/AdminColumns/UserCount.cs b/Moonlight/Core/Implementations/AdminDashboard/UserCount.cs similarity index 81% rename from Moonlight/Core/Implementations/UI/Admin/AdminColumns/UserCount.cs rename to Moonlight/Core/Implementations/AdminDashboard/UserCount.cs index 328cb078..456fca73 100644 --- a/Moonlight/Core/Implementations/UI/Admin/AdminColumns/UserCount.cs +++ b/Moonlight/Core/Implementations/AdminDashboard/UserCount.cs @@ -1,9 +1,9 @@ -using MoonCoreUI.Helpers; +using MoonCore.Blazor.Helpers; using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Models.Abstractions; using Moonlight.Core.UI.Components.Cards; -namespace Moonlight.Core.Implementations.UI.Admin.AdminColumns; +namespace Moonlight.Core.Implementations.AdminDashboard; public class UserCount : IAdminDashboardColumn { diff --git a/Moonlight/Core/Implementations/UI/Index/GreetingMessages.cs b/Moonlight/Core/Implementations/UserDashboard/GreetingMessages.cs similarity index 83% rename from Moonlight/Core/Implementations/UI/Index/GreetingMessages.cs rename to Moonlight/Core/Implementations/UserDashboard/GreetingMessages.cs index 552ec27f..b78d9c77 100644 --- a/Moonlight/Core/Implementations/UI/Index/GreetingMessages.cs +++ b/Moonlight/Core/Implementations/UserDashboard/GreetingMessages.cs @@ -1,9 +1,9 @@ -using MoonCoreUI.Helpers; +using MoonCore.Blazor.Helpers; using Moonlight.Core.Interfaces.UI.User; using Moonlight.Core.Models.Abstractions; using Moonlight.Core.UI.Components.Cards; -namespace Moonlight.Core.Implementations.UI.Index; +namespace Moonlight.Core.Implementations.UserDashboard; public class GreetingMessages : IUserDashboardComponent { diff --git a/Moonlight/Core/Models/Abstractions/Feature/PreInitContext.cs b/Moonlight/Core/Models/Abstractions/Feature/PreInitContext.cs index dee6415f..957a1f18 100644 --- a/Moonlight/Core/Models/Abstractions/Feature/PreInitContext.cs +++ b/Moonlight/Core/Models/Abstractions/Feature/PreInitContext.cs @@ -9,6 +9,7 @@ public class PreInitContext public List DiAssemblies { get; set; } = new(); public Dictionary> Assets { get; set; } = new(); public PluginService Plugins { get; set; } + public ILoggerFactory LoggerFactory { get; set; } public void EnableDependencyInjection() { diff --git a/Moonlight/Core/Models/Abstractions/Feature/SessionInitContext.cs b/Moonlight/Core/Models/Abstractions/Feature/SessionInitContext.cs index 75a94c58..85bc013e 100644 --- a/Moonlight/Core/Models/Abstractions/Feature/SessionInitContext.cs +++ b/Moonlight/Core/Models/Abstractions/Feature/SessionInitContext.cs @@ -1,4 +1,4 @@ -using MoonCoreUI.Components; +using MoonCore.Blazor.Components; namespace Moonlight.Core.Models.Abstractions.Feature; diff --git a/Moonlight/Core/Models/Forms/ChangePasswordForm.cs b/Moonlight/Core/Models/Forms/ChangePasswordForm.cs index cd26be4e..9db305b0 100644 --- a/Moonlight/Core/Models/Forms/ChangePasswordForm.cs +++ b/Moonlight/Core/Models/Forms/ChangePasswordForm.cs @@ -1,5 +1,4 @@ using System.ComponentModel.DataAnnotations; -using MoonCoreUI.Attributes; namespace Moonlight.Core.Models.Forms; @@ -8,12 +7,12 @@ public class ChangePasswordForm [Required(ErrorMessage = "You need to provide a password")] [MinLength(8, ErrorMessage = "The password must be at least 8 characters long")] [MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")] - [CustomFormType(Type = "password")] + //TODO: [CustomFormType(Type = "password")] public string Password { get; set; } [Required(ErrorMessage = "You need to provide a password")] [MinLength(8, ErrorMessage = "The password must be at least 8 characters long")] [MaxLength(256, ErrorMessage = "The password must not be longer than 256 characters")] - [CustomFormType(Type = "password")] + //TODO: [CustomFormType(Type = "password")] public string RepeatedPassword { get; set; } } \ No newline at end of file diff --git a/Moonlight/Core/Models/Forms/Users/UpdateUserForm.cs b/Moonlight/Core/Models/Forms/Users/UpdateUserForm.cs index 2db50fec..342d1fa2 100644 --- a/Moonlight/Core/Models/Forms/Users/UpdateUserForm.cs +++ b/Moonlight/Core/Models/Forms/Users/UpdateUserForm.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using MoonCoreUI.Attributes; namespace Moonlight.Core.Models.Forms.Users; @@ -17,7 +16,7 @@ public class UpdateUserForm public string Email { get; set; } [Description("This toggles the use of the two factor authentication")] - [RadioButtonBool("Enabled", "Disabled", TrueIcon = "bx-lock-alt", FalseIcon = "bx-lock-open-alt")] + //TODO: [RadioButtonBool("Enabled", "Disabled", TrueIcon = "bx-lock-alt", FalseIcon = "bx-lock-open-alt")] [DisplayName("Two factor authentication")] public bool Totp { get; set; } = false; } \ No newline at end of file diff --git a/Moonlight/Core/Models/Session.cs b/Moonlight/Core/Models/Session.cs index e36759a5..ba2670b0 100644 --- a/Moonlight/Core/Models/Session.cs +++ b/Moonlight/Core/Models/Session.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Components; -using MoonCoreUI.Services; +using MoonCore.Blazor.Services; using Moonlight.Core.Services; namespace Moonlight.Core.Models; diff --git a/Moonlight/Core/Services/FeatureService.cs b/Moonlight/Core/Services/FeatureService.cs index 5e9f3445..0e970f01 100644 --- a/Moonlight/Core/Services/FeatureService.cs +++ b/Moonlight/Core/Services/FeatureService.cs @@ -1,8 +1,7 @@ using System.Reflection; +using MoonCore.Blazor.Components; using MoonCore.Extensions; -using MoonCore.Helpers; using MoonCore.Services; -using MoonCoreUI.Components; using Moonlight.Core.Configuration; using Moonlight.Core.Models.Abstractions.Feature; @@ -15,15 +14,18 @@ public class FeatureService private readonly List Features = new(); private readonly ConfigService ConfigService; + + private readonly ILogger Logger; - public FeatureService(ConfigService configService) + public FeatureService(ConfigService configService, ILogger logger) { ConfigService = configService; + Logger = logger; } public Task Load() { - Logger.Info("Loading features"); + Logger.LogInformation("Loading features"); // TODO: Add dll loading here as well @@ -43,24 +45,25 @@ public Task Load() if (feature == null) { - Logger.Warn($"Unable to construct {featureType.FullName} feature"); + Logger.LogWarning("Unable to construct '{name}' feature", featureType.FullName); continue; } Features.Add(feature); - Logger.Info($"Loaded feature '{feature.Name}' by '{feature.Author}'"); + Logger.LogInformation("Loaded feature '{name}' by '{author}'", feature.Name, feature.Author); } return Task.CompletedTask; } - public async Task PreInit(WebApplicationBuilder builder, PluginService pluginService) + public async Task PreInit(WebApplicationBuilder builder, PluginService pluginService, ILoggerFactory preRunLoggerFactory) { - Logger.Info("Pre-initializing features"); + Logger.LogInformation("Pre-initializing features"); PreInitContext.Builder = builder; PreInitContext.Plugins = pluginService; + PreInitContext.LoggerFactory = preRunLoggerFactory; foreach (var feature in Features) { @@ -70,8 +73,7 @@ public async Task PreInit(WebApplicationBuilder builder, PluginService pluginSer } catch (Exception e) { - Logger.Error($"An error occured while performing pre init for feature '{feature.Name}'"); - Logger.Error(e); + Logger.LogError("An error occured while performing pre init for feature '{name}': {e}", feature.Name, e); } } @@ -82,7 +84,7 @@ public async Task PreInit(WebApplicationBuilder builder, PluginService pluginSer public async Task Init(WebApplication application) { - Logger.Info("Initializing features"); + Logger.LogInformation("Initializing features"); var initContext = new InitContext() { @@ -97,15 +99,14 @@ public async Task Init(WebApplication application) } catch (Exception e) { - Logger.Error($"An error occured while performing init for feature '{feature.Name}'"); - Logger.Error(e); + Logger.LogError("An error occured while performing init for feature '{name}': {e}", feature.Name, e); } } } public async Task UiInit() { - Logger.Info("Initializing feature uis"); + Logger.LogInformation("Initializing feature uis"); foreach (var feature in Features) { @@ -115,8 +116,7 @@ public async Task UiInit() } catch (Exception e) { - Logger.Error($"An error occured while performing ui init for feature '{feature.Name}'"); - Logger.Error(e); + Logger.LogError("An error occured while performing ui init for feature '{name}': {e}", feature.Name, e); } } } @@ -138,8 +138,7 @@ public async Task SessionInit(IServiceProvider provider, LazyLoader lazyLoader) } catch (Exception e) { - Logger.Error($"An error occured while performing session init for feature '{feature.Name}'"); - Logger.Error(e); + Logger.LogError("An error occured while performing session init for feature '{name}': {e}", feature.Name, e); } } } @@ -159,8 +158,7 @@ public async Task SessionDispose(ScopedStorageService scopedStorageService) } catch (Exception e) { - Logger.Error($"An error occured while performing session dispose for feature '{feature.Name}'"); - Logger.Error(e); + Logger.LogError("An error occured while performing session dispose for feature '{name}': {e}", feature.Name, e); } } } diff --git a/Moonlight/Core/Services/HotKeyService.cs b/Moonlight/Core/Services/HotKeyService.cs index 2655fb93..25c8c527 100644 --- a/Moonlight/Core/Services/HotKeyService.cs +++ b/Moonlight/Core/Services/HotKeyService.cs @@ -11,11 +11,13 @@ public class HotKeyService private readonly IJSRuntime JsRuntime; private readonly List HotKeys = new(); - public SmartEventHandler HotKeyPressed { get; set; } = new(); + public SmartEventHandler HotKeyPressed { get; set; } - public HotKeyService(IJSRuntime jsRuntime) + public HotKeyService(IJSRuntime jsRuntime, ILogger eventHandlerLogger) { JsRuntime = jsRuntime; + + HotKeyPressed = new(eventHandlerLogger); } public async Task RegisterHotkey(string key, string modifier, string action) diff --git a/Moonlight/Core/Services/IdentityService.cs b/Moonlight/Core/Services/IdentityService.cs index 4c5aec69..e6ea8cc2 100644 --- a/Moonlight/Core/Services/IdentityService.cs +++ b/Moonlight/Core/Services/IdentityService.cs @@ -15,7 +15,7 @@ public class IdentityService : IDisposable public User? CurrentUserNullable { get; private set; } public User CurrentUser => CurrentUserNullable!; public bool IsLoggedIn => CurrentUserNullable != null; - public SmartEventHandler OnAuthenticationStateChanged { get; set; } = new(); + public SmartEventHandler OnAuthenticationStateChanged { get; set; } private string Token = ""; @@ -26,11 +26,14 @@ public class IdentityService : IDisposable public IdentityService( JwtService jwtService, ConfigService configService, - Repository userRepository) + Repository userRepository, + ILogger eventHandlerLogger) { JwtService = jwtService; ConfigService = configService; UserRepository = userRepository; + + OnAuthenticationStateChanged = new(eventHandlerLogger); } public async Task Authenticate(User user) diff --git a/Moonlight/Core/Services/MoonlightService.cs b/Moonlight/Core/Services/MoonlightService.cs index 3ffa5f84..35238f32 100644 --- a/Moonlight/Core/Services/MoonlightService.cs +++ b/Moonlight/Core/Services/MoonlightService.cs @@ -10,10 +10,18 @@ public class MoonlightService public WebApplication Application { get; set; } // Do NOT modify using a plugin private readonly DateTime StartTimestamp = DateTime.UtcNow; + private readonly CoreEvents CoreEvents; + private readonly ILogger Logger; + + public MoonlightService(CoreEvents coreEvents, ILogger logger) + { + CoreEvents = coreEvents; + Logger = logger; + } public async Task Restart() { - Logger.Info("Restarting moonlight"); + Logger.LogInformation("Restarting moonlight"); // Notify all users that this instance will restart await CoreEvents.OnMoonlightRestart.Invoke(); diff --git a/Moonlight/Core/Services/PluginService.cs b/Moonlight/Core/Services/PluginService.cs index 20c66fec..af3bca87 100644 --- a/Moonlight/Core/Services/PluginService.cs +++ b/Moonlight/Core/Services/PluginService.cs @@ -9,8 +9,12 @@ public class PluginService private readonly Dictionary> ImplementationCache = new(); private readonly List Plugins = new(); - public PluginService() + private readonly ILogger Logger; + + public PluginService(ILogger logger) { + Logger = logger; + Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins")); } @@ -136,7 +140,7 @@ public async Task Load() if (pluginTypes.Length == 0) { - Logger.Info($"Loaded assembly as library. {dllFile}"); + Logger.LogInformation("Loaded assembly as library: {dllFile}", dllFile); continue; } @@ -146,19 +150,17 @@ public async Task Load() { var plugin = await LoadFromType(pluginType); - Logger.Info($"Loaded plugin '{plugin.Name}'. Created by '{plugin.Author}'"); + Logger.LogInformation("Loaded plugin '{name}'. Created by '{author}'", plugin.Name, plugin.Author); } catch (Exception e) { - Logger.Fatal($"An error occured while loading plugin '{pluginType.FullName}'"); - Logger.Fatal(e); + Logger.LogError("An error occured while loading plugin '{name}': {e}", pluginType.FullName, e); } } } catch (Exception e) { - Logger.Fatal($"An error occured while loading assembly '{dllFile}'"); - Logger.Fatal(e); + Logger.LogError("An error occured while loading assembly '{dllFile}': {e}", dllFile, e); } } } diff --git a/Moonlight/Core/Services/StartupJobService.cs b/Moonlight/Core/Services/StartupJobService.cs index 08b2579b..88f39ff5 100644 --- a/Moonlight/Core/Services/StartupJobService.cs +++ b/Moonlight/Core/Services/StartupJobService.cs @@ -9,10 +9,12 @@ public class StartupJobService : BackgroundService { private readonly List Jobs = new(); private readonly IServiceProvider ServiceProvider; + private readonly ILogger Logger; - public StartupJobService(IServiceProvider serviceProvider) + public StartupJobService(IServiceProvider serviceProvider, ILogger logger) { ServiceProvider = serviceProvider; + Logger = logger; } public Task AddJob(string name, TimeSpan delay, Func action) @@ -29,7 +31,7 @@ public Task AddJob(string name, TimeSpan delay, Func act public override Task Run() { - Logger.Info("Running startup jobs"); + Logger.LogInformation("Running startup jobs"); foreach (var job in Jobs) { @@ -42,8 +44,7 @@ public override Task Run() } catch (Exception e) { - Logger.Warn($"The startup job '{job.Name}' failed:"); - Logger.Warn(e); + Logger.LogWarning("The startup job '{name}' failed: {e}", job.Name, e); } }); } diff --git a/Moonlight/Core/Services/UnloadService.cs b/Moonlight/Core/Services/UnloadService.cs index d62ced48..dc1ab095 100644 --- a/Moonlight/Core/Services/UnloadService.cs +++ b/Moonlight/Core/Services/UnloadService.cs @@ -11,13 +11,17 @@ namespace Moonlight.Core.Services; [Scoped] public class UnloadService { - public SmartEventHandler OnUnloaded { get; set; } = new(); + public SmartEventHandler OnUnloaded { get; set; } private readonly IJSRuntime JsRuntime; + private readonly ILogger Logger; - public UnloadService(IJSRuntime jsRuntime) + public UnloadService(IJSRuntime jsRuntime, ILogger eventHandlerLogger, ILogger logger) { JsRuntime = jsRuntime; + Logger = logger; + + OnUnloaded = new(eventHandlerLogger); } public async Task Initialize() @@ -28,8 +32,7 @@ public async Task Initialize() } catch (Exception e) { - Logger.Error("An error occured while registering unload event handler"); - Logger.Error(e); + Logger.LogError("An error occured while registering unload event handler: {e}", e); } } diff --git a/Moonlight/Core/UI/Components/Auth/Login.razor b/Moonlight/Core/UI/Components/Auth/Login.razor index 64819a1a..acf58eef 100644 --- a/Moonlight/Core/UI/Components/Auth/Login.razor +++ b/Moonlight/Core/UI/Components/Auth/Login.razor @@ -5,7 +5,6 @@ @using Moonlight.Core.Models.Forms @using Moonlight.Core.Services @using MoonCore.Exceptions -@using MoonCoreUI.Services @inject IAuthenticationProvider AuthenticationProvider @inject IdentityService IdentityService @@ -93,7 +92,7 @@ var token = await IdentityService.Authenticate(user); // Save token for later use - await CookieService.SetValue("token", token); + await CookieService.SetValue("token", token, 30); //TODO: Add days to config option // Forward the user if not on the specific page if(new Uri(Navigation.Uri).LocalPath.StartsWith("/login")) diff --git a/Moonlight/Core/UI/Components/Auth/Register.razor b/Moonlight/Core/UI/Components/Auth/Register.razor index 03c6f9db..d7cb984c 100644 --- a/Moonlight/Core/UI/Components/Auth/Register.razor +++ b/Moonlight/Core/UI/Components/Auth/Register.razor @@ -7,8 +7,8 @@ @using Moonlight.Core.Services @using MoonCore.Exceptions @using MoonCore.Services -@using MoonCoreUI.Services @using Moonlight.Core.Configuration +@using IdentityService = Moonlight.Core.Services.IdentityService @inject IAuthenticationProvider AuthenticationProvider @inject IdentityService IdentityService @@ -99,7 +99,7 @@ var token = await IdentityService.Authenticate(user); // Save token for later use - await CookieService.SetValue("token", token); + await CookieService.SetValue("token", token, 30); // TODO: config // Forward the user if not on the specific page if(new Uri(Navigation.Uri).LocalPath.StartsWith("/register")) diff --git a/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor b/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor index 701c54ab..bbaeca55 100644 --- a/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor +++ b/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor @@ -139,7 +139,7 @@ else private string Key = ""; private TwoFactorCodeForm CodeForm = new(); - private async Task Disable() + private async Task Disable(ConfirmButton _) { await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, ""); await IdentityService.Authenticate(true); diff --git a/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor b/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor index 0d62e63f..5da4737f 100644 --- a/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor +++ b/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor @@ -1,6 +1,7 @@ @using MoonCore.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Services +@using IdentityService = Moonlight.Core.Services.IdentityService @inject IdentityService IdentityService @inject ConfigService ConfigService diff --git a/Moonlight/Core/UI/Components/Partials/AppHeader.razor b/Moonlight/Core/UI/Components/Partials/AppHeader.razor index 930c4bc3..9fadd38f 100644 --- a/Moonlight/Core/UI/Components/Partials/AppHeader.razor +++ b/Moonlight/Core/UI/Components/Partials/AppHeader.razor @@ -1,5 +1,4 @@ @using Moonlight.Core.Services -@using MoonCoreUI.Services @inject IdentityService IdentityService @inject CookieService CookieService @@ -94,7 +93,7 @@ private async Task Logout() { // Reset token - await CookieService.SetValue("token", ""); + await CookieService.SetValue("token", "", 30); // Reset token in identity service await IdentityService.Authenticate(""); diff --git a/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor b/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor index f6b4b00e..0989c9c8 100644 --- a/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor +++ b/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor @@ -2,6 +2,7 @@ @using MoonCore.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Services +@using IdentityService = Moonlight.Core.Services.IdentityService @inject ConfigService ConfigService @inject IdentityService IdentityService diff --git a/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor b/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor index f6c4400c..27858698 100644 --- a/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor +++ b/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor @@ -4,6 +4,7 @@ @using Moonlight.Core.Services @inject IdentityService IdentityService +@inject ILogger Logger @inherits ErrorBoundaryBase @@ -75,8 +76,7 @@ else Crashed = true; var username = IdentityService.IsLoggedIn ? IdentityService.CurrentUser.Username : "Guest"; - Logger.Warn($"A crash occured in the view of '{username}'"); - Logger.Warn(exception); + Logger.LogWarning("A crash occured in the view of '{username}': {exception}", username, exception); } Recover(); diff --git a/Moonlight/Core/UI/Layouts/MainLayout.razor b/Moonlight/Core/UI/Layouts/MainLayout.razor index f0007150..c7c93001 100644 --- a/Moonlight/Core/UI/Layouts/MainLayout.razor +++ b/Moonlight/Core/UI/Layouts/MainLayout.razor @@ -3,6 +3,7 @@ @using MoonCore.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Events +@using IdentityService = Moonlight.Core.Services.IdentityService @inherits LayoutComponentBase @@ -13,6 +14,7 @@ @inject UnloadService UnloadService @inject ConfigService ConfigService @inject ScopedStorageService ScopedStorageService +@inject CoreEvents CoreEvents @implements IDisposable @@ -99,6 +101,9 @@ + + + @code { private bool IsInitialized = false; diff --git a/Moonlight/Core/UI/Views/Account/Index.razor b/Moonlight/Core/UI/Views/Account/Index.razor index 30963c15..23591e26 100644 --- a/Moonlight/Core/UI/Views/Account/Index.razor +++ b/Moonlight/Core/UI/Views/Account/Index.razor @@ -3,7 +3,6 @@ @using Moonlight.Core.Services @using Moonlight.Core.Models.Forms @using Mappy.Net -@using MoonCoreUI.Services @using Moonlight.Core.Models.Abstractions @using Moonlight.Core.UI.Components.Navigations @@ -23,7 +22,7 @@ - +
diff --git a/Moonlight/Core/UI/Views/Account/Security.razor b/Moonlight/Core/UI/Views/Account/Security.razor index 7c2cb050..d6dd5845 100644 --- a/Moonlight/Core/UI/Views/Account/Security.razor +++ b/Moonlight/Core/UI/Views/Account/Security.razor @@ -3,7 +3,6 @@ @using Moonlight.Core.Services @using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.Models.Forms -@using MoonCoreUI.Services @using Moonlight.Core.Models.Abstractions @using MoonCore.Exceptions @using Moonlight.Core.UI.Components.Auth diff --git a/Moonlight/Core/UI/Views/Admin/Api/Index.razor b/Moonlight/Core/UI/Views/Admin/Api/Index.razor index 06d66566..16b166e2 100644 --- a/Moonlight/Core/UI/Views/Admin/Api/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Api/Index.razor @@ -21,11 +21,11 @@
- - - - - + + + + + - - + +
diff --git a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor index 79223d13..03021e46 100644 --- a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor +++ b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor @@ -1,8 +1,8 @@ @page "/admin/api/keys" @using MoonCore.Abstractions +@using MoonCore.Blazor.Models.Forms @using MoonCore.Helpers -@using MoonCoreUI.Services @using Moonlight.Core.Database.Entities @using Moonlight.Core.Models.Forms.ApiKeys @using Moonlight.Core.UI.Components.Navigations @@ -19,9 +19,9 @@ TCreateForm="CreateApiKeyForm" TUpdateForm="UpdateApiKeyForm" Loader="ApiKeysLoader" - ValidateAdd="ValidateAdd"> + OnConfigure="OnConfigure"> - + - - - + + + - - + + - - + +
@@ -59,12 +59,15 @@ return repository.Get(); } - private async Task ValidateAdd(ApiKey apiKey) + private void OnConfigure(AutoCrudConfiguration configuration) { - var key = Formatter.GenerateString(32); - apiKey.Key = key; + configuration.ValidateAdd = async apiKey => + { + var key = Formatter.GenerateString(32); + apiKey.Key = key; - await ClipboardService.Copy(key); - await ToastService.Info("Copied api key into your clipboard"); + await ClipboardService.Copy(key); + await ToastService.Info("Copied api key into your clipboard"); + }; } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Diagnose.razor b/Moonlight/Core/UI/Views/Admin/Sys/Diagnose.razor index 5a0fcad7..c13d45f7 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Diagnose.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Diagnose.razor @@ -1,10 +1,9 @@ @page "/admin/sys/diagnose" @using Moonlight.Core.UI.Components.Navigations -@using MoonCoreUI.Services @using Moonlight.Core.Services -@inject FileDownloadService DownloadService +@inject DownloadService DownloadService @inject DiagnoseService DiagnoseService @attribute [RequirePermission(9999)] @@ -31,7 +30,7 @@
- + Download diagnose report
diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Index.razor b/Moonlight/Core/UI/Views/Admin/Sys/Index.razor index 626f7467..c821ccdd 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Index.razor @@ -6,6 +6,7 @@ @using MoonCore.Helpers @inject MoonlightService MoonlightService +@inject HostSystemHelper HostSystemHelper @attribute [RequirePermission(9999)] @@ -55,7 +56,7 @@
- + Restart
diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Permissions.razor b/Moonlight/Core/UI/Views/Admin/Sys/Permissions.razor index 31603fd8..ded39571 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Permissions.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Permissions.razor @@ -2,7 +2,6 @@ @using Moonlight.Core.UI.Components.Navigations @using MoonCore.Abstractions -@using MoonCoreUI.Services @using Moonlight.Core.Database.Entities @using Moonlight.Core.Services @@ -17,7 +16,7 @@
-
- @if (SelectedUser == null) - { - - } - else - { - - } + Apply
} diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor index cacd51dd..12c2cee2 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor @@ -3,8 +3,6 @@ @using Moonlight.Core.UI.Components.Navigations @using System.Reflection @using MoonCore.Services -@using MoonCoreUI.Services -@using MoonCoreUI.Helpers @using Moonlight.Core.Configuration @inject ConfigService ConfigService @@ -85,6 +83,8 @@ else
+ + @* @foreach (var prop in Properties) {
@@ -96,10 +96,10 @@ else parameters.Add("Property", prop); }); } - + @rf
- } + }*@
diff --git a/Moonlight/Core/UI/Views/Admin/Users/Index.razor b/Moonlight/Core/UI/Views/Admin/Users/Index.razor index 106923bd..2b9ad361 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Index.razor @@ -3,9 +3,7 @@ @using Moonlight.Core.UI.Components.Navigations @using MoonCore.Abstractions @using Moonlight.Core.Database.Entities -@using BlazorTable @using MoonCore.Exceptions -@using MoonCoreUI.Services @using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Forms.Users @@ -23,17 +21,17 @@ CustomAdd="Add" ValidateUpdate="ValidateUpdate"> - - - - - + + + + + @* Change password - + *@ @code @@ -45,13 +43,14 @@ private async Task ChangePassword(User user) { - var newPassword = await AlertService.Text($"Enter a new password for {user.Username}", ""); + await AlertService.Text("", "Enter a new password for {user.Username}", async newPassword => + { + // This handles empty and canceled input + if (string.IsNullOrEmpty(newPassword)) + return; - // This handles empty and canceled input - if (string.IsNullOrEmpty(newPassword)) - return; - - await AuthenticationProvider.ChangePassword(user, newPassword); + await AuthenticationProvider.ChangePassword(user, newPassword); + }); } private async Task Add(User user) diff --git a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor index 9d626380..c4fac3f5 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor @@ -3,7 +3,6 @@ @using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.Services @using MoonCore.Helpers -@using MoonCoreUI.Services @using Moonlight.Core.Models @inject SessionService SessionService @@ -24,11 +23,11 @@
- - + + - - + + - - + + - - + + - - + + - - + +
@@ -77,7 +76,7 @@ @code { - private CrudTable? Table; + private MCBTable? Table; private Timer? UpdateTimer; private Task Load(LazyLoader _) @@ -93,40 +92,42 @@ private async Task Redirect(Session session) { - var url = await AlertService.Text("Enter the target url to redirect to"); - - if (string.IsNullOrEmpty(url)) - return; - - try - { - session.NavigationManager.NavigateTo(url); - - await ToastService.Success("Successfully redirected user session"); - } - catch (Exception) + await AlertService.Text("Redirect to", "Enter the target url to redirect to", async url => { - await ToastService.Danger("Unable to redirect user. The user is probably no longer connect with moonlight"); - } + if (string.IsNullOrEmpty(url)) + return; + + try + { + session.NavigationManager.NavigateTo(url); + + await ToastService.Success("Successfully redirected user session"); + } + catch (Exception) + { + await ToastService.Danger("Unable to redirect user. The user is probably no longer connect with moonlight"); + } + }); } private async Task Message(Session session) { - var message = await AlertService.Text("Enter the message you want to send"); - - if (string.IsNullOrEmpty(message)) - return; - - try - { - await session.AlertService.Info(message); - - await ToastService.Success("Successfully sent message to user session"); - } - catch (Exception) + await AlertService.Text("Send message", "Enter the message you want to send", async message => { - await ToastService.Danger("Unable to send message. The user is probably no longer connect with moonlight"); - } + if (string.IsNullOrEmpty(message)) + return; + + try + { + await session.AlertService.Info(message); + + await ToastService.Success("Successfully sent message to user session"); + } + catch (Exception) + { + await ToastService.Danger("Unable to send message. The user is probably no longer connect with moonlight"); + } + }); } public void Dispose() diff --git a/Moonlight/Features/FileManager/FileManagerFeature.cs b/Moonlight/Features/FileManager/FileManagerFeature.cs index eb80a640..d0e56c7b 100644 --- a/Moonlight/Features/FileManager/FileManagerFeature.cs +++ b/Moonlight/Features/FileManager/FileManagerFeature.cs @@ -24,7 +24,13 @@ public override Task OnPreInitialized(PreInitContext context) // var config = new ConfigService(PathBuilder.File("storage", "configs", "core.json")); - context.Builder.Services.AddSingleton(new JwtService(config.Get().Security.Token)); + + context.Builder.Services.AddSingleton( + new JwtService( + config.Get().Security.Token, + context.LoggerFactory.CreateLogger>() + ) + ); context.AddAsset("FileManager", "js/filemanager.js"); context.AddAsset("FileManager", "editor/ace.css"); diff --git a/Moonlight/Features/FileManager/Http/Controllers/DownloadController.cs b/Moonlight/Features/FileManager/Http/Controllers/DownloadController.cs index 217f30fa..d73b54b6 100644 --- a/Moonlight/Features/FileManager/Http/Controllers/DownloadController.cs +++ b/Moonlight/Features/FileManager/Http/Controllers/DownloadController.cs @@ -13,11 +13,13 @@ public class DownloadController : Controller { private readonly JwtService JwtService; private readonly SharedFileAccessService SharedFileAccessService; + private readonly ILogger Logger; - public DownloadController(JwtService jwtService, SharedFileAccessService sharedFileAccessService) + public DownloadController(JwtService jwtService, SharedFileAccessService sharedFileAccessService, ILogger logger) { JwtService = jwtService; SharedFileAccessService = sharedFileAccessService; + Logger = logger; } [HttpGet] @@ -25,7 +27,7 @@ public async Task Download([FromQuery(Name = "token")] string down { if (name.Contains("..")) { - Logger.Warn($"A user tried to access a file via path transversal. Name: {name}"); + Logger.LogWarning("A user tried to access a file via path transversal. Name: {name}", name); return NotFound(); } diff --git a/Moonlight/Features/FileManager/Http/Controllers/UploadController.cs b/Moonlight/Features/FileManager/Http/Controllers/UploadController.cs index 93255e0b..37b9abe2 100644 --- a/Moonlight/Features/FileManager/Http/Controllers/UploadController.cs +++ b/Moonlight/Features/FileManager/Http/Controllers/UploadController.cs @@ -13,13 +13,15 @@ public class UploadController : Controller { private readonly JwtService JwtService; private readonly SharedFileAccessService SharedFileAccessService; + private readonly ILogger Logger; public UploadController( JwtService jwtService, - SharedFileAccessService sharedFileAccessService) + SharedFileAccessService sharedFileAccessService, ILogger logger) { JwtService = jwtService; SharedFileAccessService = sharedFileAccessService; + Logger = logger; } // The following method/api endpoint needs some explanation: @@ -57,7 +59,7 @@ public async Task Upload([FromQuery(Name = "token")] string upload if (path.Contains("..")) { - Logger.Warn("A path transversal attack has been detected while processing upload path", "security"); + Logger.LogWarning("A path transversal attack has been detected while processing upload path: {path}", path); return BadRequest("Invalid path. This attempt has been logged ;)"); } diff --git a/Moonlight/Features/FileManager/Implementations/ArchiveContextAction.cs b/Moonlight/Features/FileManager/Implementations/ArchiveContextAction.cs index f2045a7e..a9d67057 100644 --- a/Moonlight/Features/FileManager/Implementations/ArchiveContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/ArchiveContextAction.cs @@ -1,6 +1,6 @@ +using MoonCore.Blazor.Services; using MoonCore.Exceptions; using MoonCore.Helpers; -using MoonCoreUI.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; @@ -23,37 +23,39 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM var alertService = provider.GetRequiredService(); - var fileName = await alertService.Text("Enter the archive file name", "", + await alertService.Text("Create an archive", "Enter the archive file name", + async fileName => + { + if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled + return; + + var toastService = provider.GetRequiredService(); + + await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient"); + + try + { + await archiveAccess.Archive( + access.CurrentDirectory + fileName, + new[] { access.CurrentDirectory + entry.Name } + ); + + await toastService.Success("Successfully created archive"); + } + catch (Exception e) + { + var logger = provider.GetRequiredService>(); + logger.LogWarning("An error occured while archiving item ({name}): {e}", entry.Name, e); + + await toastService.Danger("An unknown error occured while creating archive"); + } + finally + { + await toastService.DeleteProgress("fileManagerArchive"); + } + + await fileManager.View.Refresh(); + }, Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz"); - - if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled - return; - - var toastService = provider.GetRequiredService(); - - await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient"); - - try - { - await archiveAccess.Archive( - access.CurrentDirectory + fileName, - new[] { access.CurrentDirectory + entry.Name } - ); - - await toastService.Success("Successfully created archive"); - } - catch (Exception e) - { - Logger.Warn($"An error occured while archiving item ({entry.Name}):"); - Logger.Warn(e); - - await toastService.Danger("An unknown error occured while creating archive"); - } - finally - { - await toastService.RemoveProgress("fileManagerArchive"); - } - - await fileManager.View.Refresh(); } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Implementations/ArchiveSelectionAction.cs b/Moonlight/Features/FileManager/Implementations/ArchiveSelectionAction.cs index 240372e1..ad632a39 100644 --- a/Moonlight/Features/FileManager/Implementations/ArchiveSelectionAction.cs +++ b/Moonlight/Features/FileManager/Implementations/ArchiveSelectionAction.cs @@ -1,6 +1,6 @@ +using MoonCore.Blazor.Services; using MoonCore.Exceptions; using MoonCore.Helpers; -using MoonCoreUI.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; @@ -21,35 +21,36 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM var alertService = provider.GetRequiredService(); - var fileName = await alertService.Text("Enter the archive file name", "", + await alertService.Text("Create an archive", "Enter the archive file name", async fileName => + { + if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled + return; + + var toastService = provider.GetRequiredService(); + + await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient"); + + try + { + await archiveAccess.Archive( + access.CurrentDirectory + fileName, + entries.Select(x => access.CurrentDirectory + x.Name).ToArray() + ); + + await toastService.Success("Successfully created archive"); + } + catch (Exception e) + { + var logger = provider.GetRequiredService>(); + logger.LogWarning("An error occured while archiving items ({lenght}): {e}", entries.Length, e); + + await toastService.Danger("An unknown error occured while creating archive"); + } + finally + { + await toastService.DeleteProgress("fileManagerArchive"); + } + }, Formatter.FormatDate(DateTime.UtcNow) + ".tar.gz"); - - if (string.IsNullOrEmpty(fileName) || fileName.Contains("..")) // => canceled - return; - - var toastService = provider.GetRequiredService(); - - await toastService.CreateProgress("fileManagerArchive", "Archiving... Please be patient"); - - try - { - await archiveAccess.Archive( - access.CurrentDirectory + fileName, - entries.Select(x => access.CurrentDirectory + x.Name).ToArray() - ); - - await toastService.Success("Successfully created archive"); - } - catch (Exception e) - { - Logger.Warn($"An error occured while archiving items ({entries.Length}):"); - Logger.Warn(e); - - await toastService.Danger("An unknown error occured while creating archive"); - } - finally - { - await toastService.RemoveProgress("fileManagerArchive"); - } } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Implementations/CreateFileAction.cs b/Moonlight/Features/FileManager/Implementations/CreateFileAction.cs index 68589af9..04e25fd0 100644 --- a/Moonlight/Features/FileManager/Implementations/CreateFileAction.cs +++ b/Moonlight/Features/FileManager/Implementations/CreateFileAction.cs @@ -1,4 +1,4 @@ -using MoonCoreUI.Services; +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; @@ -14,21 +14,22 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM { var alertService = provider.GetRequiredService(); - var name = await alertService.Text("Enter a name for the new file"); - - if (string.IsNullOrEmpty(name) || name.Contains("..")) - return; + await alertService.Text("Create a new file","Enter a name for the new file", async name => + { + if (string.IsNullOrEmpty(name) || name.Contains("..")) + return; - await access.CreateFile(name); + await access.CreateFile(name); - // We build a virtual entry here so we dont need to fetch one - await fileManager.OpenEditor(new() - { - Name = name, - Size = 0, - IsFile = true, - IsDirectory = false, - LastModifiedAt = DateTime.UtcNow + // We build a virtual entry here so we dont need to fetch one + await fileManager.OpenEditor(new() + { + Name = name, + Size = 0, + IsFile = true, + IsDirectory = false, + LastModifiedAt = DateTime.UtcNow + }); }); } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Implementations/CreateFolderAction.cs b/Moonlight/Features/FileManager/Implementations/CreateFolderAction.cs index 1b8fb36d..2a2c8671 100644 --- a/Moonlight/Features/FileManager/Implementations/CreateFolderAction.cs +++ b/Moonlight/Features/FileManager/Implementations/CreateFolderAction.cs @@ -1,4 +1,4 @@ -using MoonCoreUI.Services; +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.UI.Components; @@ -16,14 +16,15 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM var alertService = provider.GetRequiredService(); var toastService = provider.GetRequiredService(); - var name = await alertService.Text("Enter a name for the new directory"); + await alertService.Text("Create a new folder", "Enter a name for the new directory", async name => + { + if (string.IsNullOrEmpty(name) || name.Contains("..")) + return; - if (string.IsNullOrEmpty(name) || name.Contains("..")) - return; + await access.CreateDirectory(name); - await access.CreateDirectory(name); - - await toastService.Success("Successfully created directory"); - await fileManager.View.Refresh(); + await toastService.Success("Successfully created directory"); + await fileManager.View.Refresh(); + }); } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Implementations/DeleteContextAction.cs b/Moonlight/Features/FileManager/Implementations/DeleteContextAction.cs index 223509c6..c721691d 100644 --- a/Moonlight/Features/FileManager/Implementations/DeleteContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/DeleteContextAction.cs @@ -1,4 +1,5 @@ -using MoonCoreUI.Services; + +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.UI.Components; diff --git a/Moonlight/Features/FileManager/Implementations/DeleteSelectionAction.cs b/Moonlight/Features/FileManager/Implementations/DeleteSelectionAction.cs index c63c4e57..b2c1655f 100644 --- a/Moonlight/Features/FileManager/Implementations/DeleteSelectionAction.cs +++ b/Moonlight/Features/FileManager/Implementations/DeleteSelectionAction.cs @@ -1,4 +1,5 @@ -using MoonCoreUI.Services; + +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.UI.Components; @@ -42,23 +43,24 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM if (fileCount > showFileCount) fileList += "And " + (fileCount - showFileCount) + " more files..."; - - - if(!await alertService.YesNo($"Do you really want to delete {folderCount + fileCount} item(s)? \n\n" + fileList)) - return; - await toastService.CreateProgress("fileManagerSelectionDelete", "Deleting items"); - foreach (var entry in entries) - { - await toastService.ModifyProgress("fileManagerSelectionDelete", $"Deleting '{entry.Name}'"); + await alertService.Confirm("Confirm file deletion", + $"Do you really want to delete {folderCount + fileCount} item(s)? \n\n" + fileList, async () => + { + await toastService.CreateProgress("fileManagerSelectionDelete", "Deleting items"); - await access.Delete(entry); - } + foreach (var entry in entries) + { + await toastService.UpdateProgress("fileManagerSelectionDelete", $"Deleting '{entry.Name}'"); + + await access.Delete(entry); + } - await toastService.RemoveProgress("fileManagerSelectionDelete"); + await toastService.DeleteProgress("fileManagerSelectionDelete"); - await toastService.Success("Successfully deleted selection"); - await fileManager.View.Refresh(); + await toastService.Success("Successfully deleted selection"); + await fileManager.View.Refresh(); + }); } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Implementations/DownloadContextAction.cs b/Moonlight/Features/FileManager/Implementations/DownloadContextAction.cs index 6a56beec..e629869c 100644 --- a/Moonlight/Features/FileManager/Implementations/DownloadContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/DownloadContextAction.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components; +using MoonCore.Blazor.Services; using MoonCore.Helpers; -using MoonCoreUI.Services; + using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.Services; @@ -15,11 +16,11 @@ public class DownloadContextAction : IFileManagerContextAction public string Color => "primary"; public Func Filter => entry => entry.IsFile; - public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry, IServiceProvider serviceProvider) + public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileManager, FileEntry entry, IServiceProvider provider) { - var fileAccessService = serviceProvider.GetRequiredService(); - var navigation = serviceProvider.GetRequiredService(); - var toastService = serviceProvider.GetRequiredService(); + var fileAccessService = provider.GetRequiredService(); + var navigation = provider.GetRequiredService(); + var toastService = provider.GetRequiredService(); try { @@ -31,8 +32,8 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM } catch (Exception e) { - Logger.Warn("Unable to start download"); - Logger.Warn(e); + var logger = provider.GetRequiredService>(); + logger.LogWarning("Unable to start download: {e}", e); ; await toastService.Danger("Failed to start download"); } diff --git a/Moonlight/Features/FileManager/Implementations/ExtractContextAction.cs b/Moonlight/Features/FileManager/Implementations/ExtractContextAction.cs index 2b8735fe..46ca96a7 100644 --- a/Moonlight/Features/FileManager/Implementations/ExtractContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/ExtractContextAction.cs @@ -1,6 +1,7 @@ +using MoonCore.Blazor.Services; using MoonCore.Exceptions; using MoonCore.Helpers; -using MoonCoreUI.Services; + using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; @@ -36,14 +37,14 @@ await archiveAccess.Extract( } catch (Exception e) { - Logger.Warn($"An error occured while extracting archive ({entry.Name}):"); - Logger.Warn(e); + var logger = provider.GetRequiredService>(); + logger.LogWarning("An error occured while extracting archive ({name}): {e}", entry.Name, e); await toastService.Danger("An unknown error occured while extracting archive"); } finally { - await toastService.RemoveProgress("fileManagerExtract"); + await toastService.DeleteProgress("fileManagerExtract"); } await fileManager.View.Refresh(); diff --git a/Moonlight/Features/FileManager/Implementations/MoveContextAction.cs b/Moonlight/Features/FileManager/Implementations/MoveContextAction.cs index b5f3a7ce..b6f10e1c 100644 --- a/Moonlight/Features/FileManager/Implementations/MoveContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/MoveContextAction.cs @@ -1,4 +1,5 @@ -using MoonCoreUI.Services; + +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; diff --git a/Moonlight/Features/FileManager/Implementations/MoveSelectionAction.cs b/Moonlight/Features/FileManager/Implementations/MoveSelectionAction.cs index 981d9835..a0927959 100644 --- a/Moonlight/Features/FileManager/Implementations/MoveSelectionAction.cs +++ b/Moonlight/Features/FileManager/Implementations/MoveSelectionAction.cs @@ -1,4 +1,5 @@ -using MoonCoreUI.Services; + +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; @@ -19,12 +20,12 @@ await fileManager.OpenFolderSelect("Select the location to move the items to", a foreach (var entry in entries) { - await toastService.ModifyProgress("fileManagerSelectionMove", $"Moving '{entry.Name}'"); + await toastService.UpdateProgress("fileManagerSelectionMove", $"Moving '{entry.Name}'"); await access.Move(entry, path + entry.Name); } - await toastService.RemoveProgress("fileManagerSelectionMove"); + await toastService.DeleteProgress("fileManagerSelectionMove"); await toastService.Success("Successfully moved selection"); await fileManager.View.Refresh(); diff --git a/Moonlight/Features/FileManager/Implementations/RenameContextAction.cs b/Moonlight/Features/FileManager/Implementations/RenameContextAction.cs index 996dffd3..060fa906 100644 --- a/Moonlight/Features/FileManager/Implementations/RenameContextAction.cs +++ b/Moonlight/Features/FileManager/Implementations/RenameContextAction.cs @@ -1,4 +1,5 @@ -using MoonCoreUI.Services; + +using MoonCore.Blazor.Services; using Moonlight.Features.FileManager.Interfaces; using Moonlight.Features.FileManager.Models.Abstractions.FileAccess; using Moonlight.Features.FileManager.UI.Components; @@ -17,14 +18,15 @@ public async Task Execute(BaseFileAccess access, UI.Components.FileManager fileM var alertService = provider.GetRequiredService(); var toastService = provider.GetRequiredService(); - var newName = await alertService.Text($"Enter a new name for '{entry.Name}'", "", entry.Name); - - if (string.IsNullOrEmpty(newName)) - return; + await alertService.Text("Rename file" , $"Enter a new name for '{entry.Name}'", async newName => + { + if (string.IsNullOrEmpty(newName)) + return; - await access.Rename(entry.Name, newName); + await access.Rename(entry.Name, newName); - await fileManager.View.Refresh(); - await toastService.Success("Successfully renamed file"); + await fileManager.View.Refresh(); + await toastService.Success("Successfully renamed file"); + }, entry.Name); } } \ No newline at end of file diff --git a/Moonlight/Features/FileManager/Services/FileManagerInteropService.cs b/Moonlight/Features/FileManager/Services/FileManagerInteropService.cs index 208908c2..dbd7bf61 100644 --- a/Moonlight/Features/FileManager/Services/FileManagerInteropService.cs +++ b/Moonlight/Features/FileManager/Services/FileManagerInteropService.cs @@ -1,5 +1,6 @@ using Microsoft.JSInterop; using MoonCore.Attributes; +using MoonCore.Blazor.Extensions; using MoonCore.Helpers; namespace Moonlight.Features.FileManager.Services; @@ -9,32 +10,30 @@ public class FileManagerInteropService { private readonly IJSRuntime JsRuntime; - public SmartEventHandler OnUploadStateChanged { get; set; } = new(); + public SmartEventHandler OnUploadStateChanged { get; set; } - public FileManagerInteropService(IJSRuntime jsRuntime) + public FileManagerInteropService(IJSRuntime jsRuntime, ILogger eventHandlerLogger) { JsRuntime = jsRuntime; + + OnUploadStateChanged = new(eventHandlerLogger); } public async Task InitDropzone(string id, string urlId) { var reference = DotNetObjectReference.Create(this); - await JsRuntime.InvokeVoidAsync("filemanager.dropzone.init", id, urlId, reference); + await JsRuntime.InvokeVoidAsyncHandled("filemanager.dropzone.init", id, urlId, reference); } public async Task InitFileSelect(string id, string urlId) { var reference = DotNetObjectReference.Create(this); - await JsRuntime.InvokeVoidAsync("filemanager.fileselect.init", id, urlId, reference); + await JsRuntime.InvokeVoidAsyncHandled("filemanager.fileselect.init", id, urlId, reference); } public async Task UpdateUrl(string urlId, string url) { - try - { - await JsRuntime.InvokeVoidAsync("filemanager.updateUrl", urlId, url); - } - catch (TaskCanceledException) { /* ignored */ } + await JsRuntime.InvokeVoidAsyncHandled("filemanager.updateUrl", urlId, url); } [JSInvokable] diff --git a/Moonlight/Features/FileManager/UI/Components/FileEditor.razor b/Moonlight/Features/FileManager/UI/Components/FileEditor.razor index c63ed457..f8e6d6f0 100644 --- a/Moonlight/Features/FileManager/UI/Components/FileEditor.razor +++ b/Moonlight/Features/FileManager/UI/Components/FileEditor.razor @@ -1,4 +1,3 @@ -@using MoonCoreUI.Services @using Moonlight.Core.Services @using MoonCore.Helpers @using Moonlight.Core.Helpers @@ -7,6 +6,7 @@ @inject ToastService ToastService @inject HotKeyService HotKeyService +@inject ILogger Logger @implements IDisposable @@ -76,8 +76,7 @@ } catch (Exception e) { - Logger.Warn($"An unhandled error has occured while saving a file using access type {FileAccess.GetType().FullName}"); - Logger.Warn(e); + Logger.LogWarning("An unhandled error has occured while saving a file using access type {name}: {e}", FileAccess.GetType().FullName, e); await ToastService.Danger("An unknown error has occured while saving the file. Please try again later"); return; diff --git a/Moonlight/Features/FileManager/UI/Components/FileManager.razor b/Moonlight/Features/FileManager/UI/Components/FileManager.razor index e51c145d..81576627 100644 --- a/Moonlight/Features/FileManager/UI/Components/FileManager.razor +++ b/Moonlight/Features/FileManager/UI/Components/FileManager.razor @@ -1,6 +1,5 @@ @using MoonCore.Helpers @using MoonCore.Services -@using MoonCoreUI.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Services @using Moonlight.Features.FileManager.Interfaces @@ -9,6 +8,7 @@ @inject AlertService AlertService @inject ToastService ToastService +@inject ModalService ModalService @inject FileManagerInteropService FileManagerInteropService @inject SharedFileAccessService FileAccessService @inject ConfigService ConfigService @@ -111,28 +111,6 @@ else
- - - - - - } @code @@ -151,15 +129,6 @@ else private FileEntry FileToEdit; private bool ShowEditor = false; - // Folder select dialog - private bool FolderSelectIsOpen = false; - private SmartModal FolderSelectModal; - private BaseFileAccess FolderSelectFileAccess; - private string FolderSelectTitle; - private Func FolderSelectResult; - private FileView FolderSelectView; - private Func FolderSelectFilter => entry => entry.IsDirectory; - private Timer? UploadTokenTimer; protected override async Task OnInitializedAsync() @@ -174,8 +143,7 @@ else { if (!firstRender) return; - - + // Setup upload url update timer UploadTokenTimer = new(async _ => { @@ -307,53 +275,17 @@ else } #endregion - - #region Selects - + public async Task OpenFolderSelect(string title, Func onResult) { - if (FolderSelectIsOpen) - await HideFolderSelect(); - - FolderSelectResult = onResult; - FolderSelectTitle = title; - - FolderSelectFileAccess = FileAccess.Clone(); - await FolderSelectFileAccess.SetDirectory("/"); - - await FolderSelectModal.Show(); - } - - public async Task HideFolderSelect() - { - await FolderSelectModal.Hide(); - FolderSelectIsOpen = false; - FolderSelectFileAccess.Dispose(); - } - - private async Task SubmitFolderSelect() - { - var path = await FolderSelectFileAccess.GetCurrentDirectory(); - - await HideFolderSelect(); - - await FolderSelectResult.Invoke(path); - } - - private async Task NavigateUpFolderSelect() - { - await FolderSelectFileAccess.ChangeDirectory(".."); - await FolderSelectView.Refresh(); - } - - private async Task EntryClickFolderSelect(FileEntry entry) - { - await FolderSelectFileAccess.ChangeDirectory(entry.Name); - await FolderSelectView.Refresh(); + await ModalService.Launch(cssClasses: "modal-lg modal-dialog-centered", buildAttributes: parameters => + { + parameters.Add("Title", title); + parameters.Add("OnResult", onResult); + parameters.Add("FileAccess", FileAccess.Clone()); + }); } - - #endregion - + public async void Dispose() { if (UploadTokenTimer != null) diff --git a/Moonlight/Features/FileManager/UI/Components/FolderSelectModal.razor b/Moonlight/Features/FileManager/UI/Components/FolderSelectModal.razor new file mode 100644 index 00000000..b7136185 --- /dev/null +++ b/Moonlight/Features/FileManager/UI/Components/FolderSelectModal.razor @@ -0,0 +1,61 @@ +@using Moonlight.Features.FileManager.Models.Abstractions.FileAccess + +@implements IDisposable + + + + + +@code +{ + [Parameter] public BaseFileAccess FileAccess { get; set; } + [Parameter] public string Title { get; set; } + [Parameter] public Func OnResult { get; set; } + + private FileView View; + private Func Filter => entry => entry.IsDirectory; + + protected override async Task OnInitializedAsync() + { + await FileAccess.SetDirectory("/"); + } + + private async Task SubmitFolderSelect() + { + var path = await FileAccess.GetCurrentDirectory(); + await OnResult.Invoke(path); + } + + private async Task NavigateUpFolderSelect() + { + await FileAccess.ChangeDirectory(".."); + await View.Refresh(); + } + + private async Task EntryClickFolderSelect(FileEntry entry) + { + await FileAccess.ChangeDirectory(entry.Name); + await View.Refresh(); + } + + public void Dispose() + { + FileAccess.Dispose(); + } +} diff --git a/Moonlight/Features/Servers/Events/ServerEvents.cs b/Moonlight/Features/Servers/Events/ServerEvents.cs index 78111cdb..0be18882 100644 --- a/Moonlight/Features/Servers/Events/ServerEvents.cs +++ b/Moonlight/Features/Servers/Events/ServerEvents.cs @@ -1,9 +1,16 @@ -using MoonCore.Helpers; +using MoonCore.Attributes; +using MoonCore.Helpers; using Moonlight.Features.Servers.Entities; namespace Moonlight.Features.Servers.Events; +[Singleton] public class ServerEvents { - public static SmartEventHandler<(Server, ServerBackup)> OnBackupCompleted { get; set; } = new(); + public SmartEventHandler<(Server, ServerBackup)> OnBackupCompleted { get; set; } + + public ServerEvents(ILogger eventHandlerLogger) + { + OnBackupCompleted = new(eventHandlerLogger); + } } \ No newline at end of file diff --git a/Moonlight/Features/Servers/Extensions/NodeExtensions.cs b/Moonlight/Features/Servers/Extensions/NodeExtensions.cs index 436e75e2..320caea5 100644 --- a/Moonlight/Features/Servers/Extensions/NodeExtensions.cs +++ b/Moonlight/Features/Servers/Extensions/NodeExtensions.cs @@ -16,8 +16,13 @@ public static HttpApiClient CreateHttpClient(this ServerNode node return new HttpApiClient(remoteUrl, node.Token); } - public static JwtService CreateJwtService(this ServerNode node) + public static JwtService CreateJwtService(this ServerNode node, ILoggerFactory factory) { - return new JwtService(node.Token); + return node.CreateJwtService(factory.CreateLogger>()); + } + + public static JwtService CreateJwtService(this ServerNode node, ILogger> logger) + { + return new JwtService(node.Token, logger); } } \ No newline at end of file diff --git a/Moonlight/Features/Servers/Helpers/ServerConsole.cs b/Moonlight/Features/Servers/Helpers/ServerConsole.cs index 32d0e4ac..23d535db 100644 --- a/Moonlight/Features/Servers/Helpers/ServerConsole.cs +++ b/Moonlight/Features/Servers/Helpers/ServerConsole.cs @@ -8,10 +8,10 @@ namespace Moonlight.Features.Servers.Helpers; public class ServerConsole : IDisposable { - public SmartEventHandler OnStateChange { get; set; } = new(); - public SmartEventHandler OnStatsChange { get; set; } = new(); - public SmartEventHandler OnNewMessage { get; set; } = new(); - public SmartEventHandler OnDisconnected { get; set; } = new(); + public SmartEventHandler OnStateChange { get; set; } + public SmartEventHandler OnStatsChange { get; set; } + public SmartEventHandler OnNewMessage { get; set; } + public SmartEventHandler OnDisconnected { get; set; } public ServerState State { get; private set; } = ServerState.Offline; public ServerStats Stats { get; private set; } = new(); @@ -20,18 +20,29 @@ public class ServerConsole : IDisposable private readonly List MessageCache = new(); private readonly Server Server; + private readonly ILogger Logger; + private readonly ILogger AwsLogger; private ClientWebSocket WebSocket; private AdvancedWebsocketStream WebsocketStream; private CancellationTokenSource Cancellation = new(); - public ServerConsole(Server server) + public ServerConsole(Server server, ILoggerFactory loggerFactory) { if (server.Node == null) throw new ArgumentNullException(nameof(server.Node)); Server = server; + + Logger = loggerFactory.CreateLogger(); + AwsLogger = loggerFactory.CreateLogger(); + + var eventHandlerLogger = loggerFactory.CreateLogger(); + OnStateChange = new(eventHandlerLogger); + OnStatsChange = new(eventHandlerLogger); + OnDisconnected = new(eventHandlerLogger); + OnNewMessage = new(eventHandlerLogger); } public async Task Connect() @@ -49,7 +60,7 @@ public async Task Connect() wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws"; await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None); - WebsocketStream = new AdvancedWebsocketStream(WebSocket); + WebsocketStream = new AdvancedWebsocketStream(AwsLogger, WebSocket); WebsocketStream.RegisterPacket(1); WebsocketStream.RegisterPacket(2); @@ -103,11 +114,10 @@ private async Task Worker() break; if (e is WebSocketException) - Logger.Warn($"Lost connection to daemon server websocket: {e.Message}"); + Logger.LogWarning("Lost connection to daemon server websocket: {message}", e.Message); else { - Logger.Warn("Server console ws disconnected because of application error:"); - Logger.Warn(e); + Logger.LogWarning("Server console ws disconnected because of application error: {e}", e); } break; diff --git a/Moonlight/Features/Servers/Http/Controllers/ServersController.cs b/Moonlight/Features/Servers/Http/Controllers/ServersController.cs index ac7c7b9b..4ed3703d 100644 --- a/Moonlight/Features/Servers/Http/Controllers/ServersController.cs +++ b/Moonlight/Features/Servers/Http/Controllers/ServersController.cs @@ -18,11 +18,17 @@ public class ServersController : Controller { private readonly Repository ServerRepository; private readonly Repository BackupRepository; + private readonly ILogger Logger; + private readonly ILogger WebSocketLogger; + private readonly ServerEvents ServerEvents; - public ServersController(Repository serverRepository, Repository backupRepository) + public ServersController(Repository serverRepository, Repository backupRepository, ILogger logger, ILogger webSocketLogger, ServerEvents serverEvents) { ServerRepository = serverRepository; BackupRepository = backupRepository; + Logger = logger; + WebSocketLogger = webSocketLogger; + ServerEvents = serverEvents; } [HttpGet("ws")] @@ -36,7 +42,7 @@ public async Task GetAllServersWs() var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); // Build connection wrapper - var websocketStream = new AdvancedWebsocketStream(websocket); + var websocketStream = new AdvancedWebsocketStream(WebSocketLogger, websocket); websocketStream.RegisterPacket(1); websocketStream.RegisterPacket(2); @@ -66,8 +72,7 @@ public async Task GetAllServersWs() } catch (Exception e) { - Logger.Error($"An error occured while sending server {server.Id} (Image: {server.Image.Name}) to daemon. This may indicate a corrupt or broken image/server. Skipping this server"); - Logger.Error(e); + Logger.LogError("An error occured while sending server {serverId} (Image: {name}) to daemon. This may indicate a corrupt or broken image/server. Skipping this server. Error: {e}", server.Id, server.Image.Name, e); } } @@ -146,7 +151,7 @@ public async Task ReportBackupStatus(int id, int backupId, [FromBo return NotFound(); if(!status.Successful) - Logger.Warn($"A node reported an error for a backup for the server {server.Id}"); + Logger.LogWarning("A node reported an error for a backup for the server {serverId}", server.Id); backup.Successful = status.Successful; backup.Completed = true; diff --git a/Moonlight/Features/Servers/Implementations/UI/Admin/AdminColumns/ServerCount.cs b/Moonlight/Features/Servers/Implementations/AdminDashboard/Columns/ServerCount.cs similarity index 80% rename from Moonlight/Features/Servers/Implementations/UI/Admin/AdminColumns/ServerCount.cs rename to Moonlight/Features/Servers/Implementations/AdminDashboard/Columns/ServerCount.cs index 3e7928ae..0a2c6f96 100644 --- a/Moonlight/Features/Servers/Implementations/UI/Admin/AdminColumns/ServerCount.cs +++ b/Moonlight/Features/Servers/Implementations/AdminDashboard/Columns/ServerCount.cs @@ -1,9 +1,9 @@ -using MoonCoreUI.Helpers; +using MoonCore.Blazor.Helpers; using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Models.Abstractions; using Moonlight.Features.Servers.UI.Components.Cards; -namespace Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns; +namespace Moonlight.Features.Servers.Implementations.AdminDashboard.Columns; public class ServerCount : IAdminDashboardColumn { diff --git a/Moonlight/Features/Servers/Implementations/UI/Admin/AdminComponents/NodeOverview.cs b/Moonlight/Features/Servers/Implementations/AdminDashboard/Components/NodeOverview.cs similarity index 79% rename from Moonlight/Features/Servers/Implementations/UI/Admin/AdminComponents/NodeOverview.cs rename to Moonlight/Features/Servers/Implementations/AdminDashboard/Components/NodeOverview.cs index cd5116c8..aade955b 100644 --- a/Moonlight/Features/Servers/Implementations/UI/Admin/AdminComponents/NodeOverview.cs +++ b/Moonlight/Features/Servers/Implementations/AdminDashboard/Components/NodeOverview.cs @@ -1,9 +1,9 @@ -using MoonCoreUI.Helpers; +using MoonCore.Blazor.Helpers; using Moonlight.Core.Interfaces.Ui.Admin; using Moonlight.Core.Models.Abstractions; using Moonlight.Features.Servers.UI.Components.Cards; -namespace Moonlight.Features.Servers.Implementations.UI.Admin.AdminComponents; +namespace Moonlight.Features.Servers.Implementations.AdminDashboard.Components; public class NodeOverview : IAdminDashboardComponent { diff --git a/Moonlight/Features/Servers/Implementations/UI/UserDashboard/Components/UserDashboardServerCount.cs b/Moonlight/Features/Servers/Implementations/UserDashboard/Components/UserDashboardServerCount.cs similarity index 72% rename from Moonlight/Features/Servers/Implementations/UI/UserDashboard/Components/UserDashboardServerCount.cs rename to Moonlight/Features/Servers/Implementations/UserDashboard/Components/UserDashboardServerCount.cs index 5a10fee5..fdc2e0ec 100644 --- a/Moonlight/Features/Servers/Implementations/UI/UserDashboard/Components/UserDashboardServerCount.cs +++ b/Moonlight/Features/Servers/Implementations/UserDashboard/Components/UserDashboardServerCount.cs @@ -1,9 +1,8 @@ -using MoonCoreUI.Helpers; +using MoonCore.Blazor.Helpers; using Moonlight.Core.Interfaces.UI.User; using Moonlight.Core.Models.Abstractions; -using Moonlight.Features.Servers.UI.Components.Cards; -namespace Moonlight.Features.Servers.Implementations.UI.UserDashboard.Components; +namespace Moonlight.Features.Servers.Implementations.UserDashboard.Components; public class UserDashboardServerCount : IUserDashboardComponent { diff --git a/Moonlight/Features/Servers/Models/Forms/Admin/Servers/CreateServerForm.cs b/Moonlight/Features/Servers/Models/Forms/Admin/Servers/CreateServerForm.cs index c3f6be26..ae34d1bd 100644 --- a/Moonlight/Features/Servers/Models/Forms/Admin/Servers/CreateServerForm.cs +++ b/Moonlight/Features/Servers/Models/Forms/Admin/Servers/CreateServerForm.cs @@ -1,6 +1,6 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using MoonCoreUI.Attributes; +using MoonCore.Blazor.Attributes.Auto; using Moonlight.Core.Database.Entities; using Moonlight.Features.Servers.Entities; @@ -12,11 +12,11 @@ public class CreateServerForm public string Name { get; set; } [Required(ErrorMessage = "You need to specify a server owner")] - [Selector(SelectorProp = "Username", DisplayProp = "Username", UseDropdown = true)] + //[Selector(SelectorProp = "Username", DisplayProp = "Username", UseDropdown = true)] public User Owner { get; set; } [Required(ErrorMessage = "You need to specify a server image")] - [Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] + //[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] public ServerImage Image { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Enter a valid cpu value")] @@ -26,31 +26,31 @@ public class CreateServerForm [Range(1, int.MaxValue, ErrorMessage = "Enter a valid memory value")] [Description("The amount of memory this server will be able to use")] - [ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] + //[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] [Section("Resources", Icon = "bxs-chip")] public int Memory { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Enter a valid disk value")] [Description("The amount of disk space this server will be able to use")] - [ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] + //[ByteSize(MinimumUnit = 1, Converter = 1, DefaultUnit = 2)] [Section("Resources", Icon = "bxs-chip")] public int Disk { get; set; } [Description("Whether to use a virtual disk for storing server files. Dont use this if you want to overallocate as the virtual disks will fill out the space you allocate")] [Section("Deployment", Icon = "bx-cube")] - [RadioButtonBool("Virtual Disk", "Simple Volume", TrueIcon = "bxs-hdd", FalseIcon = "bxs-data")] + //[RadioButtonBool("Virtual Disk", "Simple Volume", TrueIcon = "bxs-hdd", FalseIcon = "bxs-data")] [DisplayName("Storage")] public bool UseVirtualDisk { get; set; } [Required(ErrorMessage = "You need to specify a server node")] - [Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] + //[Selector(SelectorProp = "Name", DisplayProp = "Name", UseDropdown = true)] [Section("Deployment", Icon = "bx-cube")] public ServerNode Node { get; set; } [Description("The allocations the server should have")] - [MultiSelection("Port", "Port", Icon = "bx-network-chart")] + //TODO: [MultiSelection("Port", "Port", Icon = "bx-network-chart")] [Section("Deployment", Icon = "bx-cube")] - [CustomItemLoader("FreeAllocations")] - [CustomDisplayFunction("AllocationWithIp")] + //[CustomItemLoader("FreeAllocations")] + //[CustomDisplayFunction("AllocationWithIp")] public List Allocations { get; set; } = new(); } \ No newline at end of file diff --git a/Moonlight/Features/Servers/Models/Forms/Users/Networks/CreateNetworkForm.cs b/Moonlight/Features/Servers/Models/Forms/Users/Networks/CreateNetworkForm.cs index 6c9cce05..299b4a4f 100644 --- a/Moonlight/Features/Servers/Models/Forms/Users/Networks/CreateNetworkForm.cs +++ b/Moonlight/Features/Servers/Models/Forms/Users/Networks/CreateNetworkForm.cs @@ -1,5 +1,4 @@ using System.ComponentModel.DataAnnotations; -using MoonCoreUI.Attributes; using Moonlight.Features.Servers.Entities; namespace Moonlight.Features.Servers.Models.Forms.Users.Networks; @@ -10,6 +9,6 @@ public class CreateNetworkForm public string Name { get; set; } [Required(ErrorMessage = "You need to specify a node to create the network on")] - [Selector(SelectorProp = "Name", DisplayProp = "Name")] + //[Selector(SelectorProp = "Name", DisplayProp = "Name")] public ServerNode Node { get; set; } } \ No newline at end of file diff --git a/Moonlight/Features/Servers/ServersFeature.cs b/Moonlight/Features/Servers/ServersFeature.cs index 8ebf44f7..1e1ee4d1 100644 --- a/Moonlight/Features/Servers/ServersFeature.cs +++ b/Moonlight/Features/Servers/ServersFeature.cs @@ -9,13 +9,13 @@ using Moonlight.Features.Servers.Actions; using Moonlight.Features.Servers.Configuration; using Moonlight.Features.Servers.Http.Middleware; +using Moonlight.Features.Servers.Implementations.AdminDashboard.Columns; +using Moonlight.Features.Servers.Implementations.AdminDashboard.Components; using Moonlight.Features.Servers.Implementations.Diagnose; -using Moonlight.Features.Servers.Implementations.UI.Admin.AdminColumns; -using Moonlight.Features.Servers.Implementations.UI.Admin.AdminComponents; using Moonlight.Features.Servers.Models.Enums; using Moonlight.Features.Servers.Services; using Moonlight.Features.Servers.UI.Components.Cards; -using UserDashboardServerCount = Moonlight.Features.Servers.Implementations.UI.UserDashboard.Components.UserDashboardServerCount; +using UserDashboardServerCount = Moonlight.Features.Servers.Implementations.UserDashboard.Components.UserDashboardServerCount; namespace Moonlight.Features.Servers; @@ -34,7 +34,7 @@ public override Task OnPreInitialized(PreInitContext context) // var config = new ConfigService(PathBuilder.File("storage", "configs", "core.json")); - context.Builder.Services.AddSingleton(new JwtService(config.Get().Security.Token)); + context.Builder.Services.AddSingleton(new JwtService(config.Get().Security.Token, context.LoggerFactory.CreateLogger>())); // var configService = new ConfigService(PathBuilder.File("storage", "configs", "servers.json")); diff --git a/Moonlight/Features/Servers/Services/NodeService.cs b/Moonlight/Features/Servers/Services/NodeService.cs index df39ed75..f5522462 100644 --- a/Moonlight/Features/Servers/Services/NodeService.cs +++ b/Moonlight/Features/Servers/Services/NodeService.cs @@ -11,10 +11,12 @@ namespace Moonlight.Features.Servers.Services; public class NodeService { private readonly IServiceProvider ServiceProvider; + private readonly ILogger Logger; - public NodeService(IServiceProvider serviceProvider) + public NodeService(IServiceProvider serviceProvider, ILogger logger) { ServiceProvider = serviceProvider; + Logger = logger; } public async Task Boot(ServerNode node) @@ -42,8 +44,7 @@ public Task BootAll() { //TODO: Add http exception check to reduce error logs - Logger.Warn($"An error occured while booting node '{node.Name}'"); - Logger.Warn(e); + Logger.LogWarning("An error occured while booting node '{name}': {e}", node.Name, e); } }); } diff --git a/Moonlight/Features/Servers/Services/ServerBackupService.cs b/Moonlight/Features/Servers/Services/ServerBackupService.cs index 84190a75..f5aaa995 100644 --- a/Moonlight/Features/Servers/Services/ServerBackupService.cs +++ b/Moonlight/Features/Servers/Services/ServerBackupService.cs @@ -116,7 +116,8 @@ public async Task GetDownloadUrl(Server server, ServerBackup backup) var remoteUrl = $"{protocol}://{node.Fqdn}:{node.HttpPort}/"; // Build jwt - var jwtService = node.CreateJwtService(); + var loggerFactory = ServiceProvider.GetRequiredService(); + var jwtService = node.CreateJwtService(loggerFactory); var jwt = await jwtService.Create(data => { diff --git a/Moonlight/Features/Servers/Services/ServerScheduleService.cs b/Moonlight/Features/Servers/Services/ServerScheduleService.cs index b82a4ac6..85e501de 100644 --- a/Moonlight/Features/Servers/Services/ServerScheduleService.cs +++ b/Moonlight/Features/Servers/Services/ServerScheduleService.cs @@ -14,10 +14,12 @@ public class ServerScheduleService { private readonly IServiceProvider ServiceProvider; public readonly Dictionary Actions = new(); + private readonly ILogger Logger; - public ServerScheduleService(IServiceProvider serviceProvider) + public ServerScheduleService(IServiceProvider serviceProvider, ILogger logger) { ServiceProvider = serviceProvider; + Logger = logger; } public Task RegisterAction(string id) where T : ScheduleAction @@ -50,7 +52,7 @@ public async Task Run(Server s, ServerSchedule sh) { if (!Actions.ContainsKey(scheduleItem.Action)) { - Logger.Warn($"The server {server.Id} has a invalid action type '{scheduleItem.Action}'"); + Logger.LogWarning("The server {serverId} has a invalid action type '{action}'", server.Id, scheduleItem.Action); continue; } @@ -69,8 +71,7 @@ public async Task Run(Server s, ServerSchedule sh) } catch (Exception e) { - Logger.Warn($"An unhandled error occured while running schedule {schedule.Name} for server {server.Id}"); - Logger.Warn(e); + Logger.LogWarning("An unhandled error occured while running schedule {name} for server {serverId}: {e}", schedule.Name, server.Id, e); sw.Stop(); diff --git a/Moonlight/Features/Servers/Services/ServerService.cs b/Moonlight/Features/Servers/Services/ServerService.cs index 219515ee..2fb857de 100644 --- a/Moonlight/Features/Servers/Services/ServerService.cs +++ b/Moonlight/Features/Servers/Services/ServerService.cs @@ -27,10 +27,12 @@ public class ServerService public NodeService NodeService => ServiceProvider.GetRequiredService(); private readonly IServiceProvider ServiceProvider; + private readonly ILogger Logger; - public ServerService(IServiceProvider serviceProvider) + public ServerService(IServiceProvider serviceProvider, ILogger logger) { ServiceProvider = serviceProvider; + Logger = logger; } public async Task Sync(Server server) @@ -84,8 +86,7 @@ public async Task Create(Server form) } catch (Exception e) { - Logger.Warn($"Could not establish to the node with the id {node.Id}"); - Logger.Warn(e); + Logger.LogWarning("Could not establish to the node with the id {nodeId}: {e}", node.Id, e); throw new DisplayException($"Could not establish connection to the node: {e.Message}"); } diff --git a/Moonlight/Features/Servers/UI/Components/Terminal.razor b/Moonlight/Features/Servers/UI/Components/Terminal.razor index 19a5ca32..3e5911e9 100644 --- a/Moonlight/Features/Servers/UI/Components/Terminal.razor +++ b/Moonlight/Features/Servers/UI/Components/Terminal.razor @@ -1,6 +1,6 @@ @using XtermBlazor -@using MoonCoreUI.Services + @inject ClipboardService ClipboardService @inject ToastService ToastService diff --git a/Moonlight/Features/Servers/UI/Components/VariableViews/NumberVariableView.razor b/Moonlight/Features/Servers/UI/Components/VariableViews/NumberVariableView.razor index 83854b5e..ab76c7ed 100644 --- a/Moonlight/Features/Servers/UI/Components/VariableViews/NumberVariableView.razor +++ b/Moonlight/Features/Servers/UI/Components/VariableViews/NumberVariableView.razor @@ -1,6 +1,5 @@ @using Moonlight.Features.Servers.Entities @using MoonCore.Abstractions -@using MoonCoreUI.Services @inject Repository ServerVariableRepository @inject ToastService ToastService diff --git a/Moonlight/Features/Servers/UI/Components/VariableViews/SelectVariableView.razor b/Moonlight/Features/Servers/UI/Components/VariableViews/SelectVariableView.razor index bf53dc01..8c0be787 100644 --- a/Moonlight/Features/Servers/UI/Components/VariableViews/SelectVariableView.razor +++ b/Moonlight/Features/Servers/UI/Components/VariableViews/SelectVariableView.razor @@ -1,6 +1,6 @@ @using Moonlight.Features.Servers.Entities @using MoonCore.Abstractions -@using MoonCoreUI.Services + @using System.Text.RegularExpressions @using MoonCore.Helpers diff --git a/Moonlight/Features/Servers/UI/Components/VariableViews/TextVariableView.razor b/Moonlight/Features/Servers/UI/Components/VariableViews/TextVariableView.razor index e0d11804..0cbae421 100644 --- a/Moonlight/Features/Servers/UI/Components/VariableViews/TextVariableView.razor +++ b/Moonlight/Features/Servers/UI/Components/VariableViews/TextVariableView.razor @@ -1,6 +1,6 @@ @using Moonlight.Features.Servers.Entities @using MoonCore.Abstractions -@using MoonCoreUI.Services + @using System.Text.RegularExpressions @inject Repository ServerVariableRepository diff --git a/Moonlight/Features/Servers/UI/Components/VariableViews/ToggleVariableView.razor b/Moonlight/Features/Servers/UI/Components/VariableViews/ToggleVariableView.razor index 1a613b0c..4adfac33 100644 --- a/Moonlight/Features/Servers/UI/Components/VariableViews/ToggleVariableView.razor +++ b/Moonlight/Features/Servers/UI/Components/VariableViews/ToggleVariableView.razor @@ -1,6 +1,6 @@ @using Moonlight.Features.Servers.Entities @using MoonCore.Abstractions -@using MoonCoreUI.Services + @inject Repository ServerVariableRepository @inject ToastService ToastService diff --git a/Moonlight/Features/Servers/UI/ImageComponents/ImageDetails.razor b/Moonlight/Features/Servers/UI/ImageComponents/ImageDetails.razor index d19a4678..2c531345 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/ImageDetails.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/ImageDetails.razor @@ -1,8 +1,9 @@ @using Moonlight.Features.Servers.Models.Forms.Admin.Images
-
+
@*
+ @@ -21,7 +22,7 @@ -
+
*@
diff --git a/Moonlight/Features/Servers/UI/ImageComponents/ImageDockerImages.razor b/Moonlight/Features/Servers/UI/ImageComponents/ImageDockerImages.razor index 6b131e24..daa043b9 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/ImageDockerImages.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/ImageDockerImages.razor @@ -8,7 +8,7 @@
-
- +@* - +*@ @code { [Parameter] public ServerImage Image { get; set; } diff --git a/Moonlight/Features/Servers/UI/ImageComponents/ImageInstall.razor b/Moonlight/Features/Servers/UI/ImageComponents/ImageInstall.razor index 6c2baefc..ea080729 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/ImageInstall.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/ImageInstall.razor @@ -2,7 +2,7 @@ @using Moonlight.Features.FileManager.UI.Components
-
+
@*
-
+
*@
-
+
@*
-
+
*@
diff --git a/Moonlight/Features/Servers/UI/ImageComponents/ImageVariables.razor b/Moonlight/Features/Servers/UI/ImageComponents/ImageVariables.razor index d4a1ae6f..aa4d1ee9 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/ImageVariables.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/ImageVariables.razor @@ -1,7 +1,7 @@ @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Models.Forms.Admin.Images.Variables @using BlazorTable - +@* - +*@ @code { [Parameter] public ServerImage Image { get; set; } diff --git a/Moonlight/Features/Servers/UI/Layouts/UserLayout.razor b/Moonlight/Features/Servers/UI/Layouts/UserLayout.razor index 49eef3de..7bbb1188 100644 --- a/Moonlight/Features/Servers/UI/Layouts/UserLayout.razor +++ b/Moonlight/Features/Servers/UI/Layouts/UserLayout.razor @@ -4,7 +4,6 @@ @using Moonlight.Features.Servers.UI.Components @using MoonCore.Abstractions @using MoonCore.Helpers -@using MoonCoreUI.Services @using Microsoft.EntityFrameworkCore @using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.UI.UserViews @@ -13,7 +12,8 @@ @using MoonCore.Exceptions @using Moonlight.Features.Servers.Configuration @using MoonCore.Services -@using Moonlight.Core.Services +@using IdentityService = Moonlight.Core.Services.IdentityService +@using MoonCore.Blazor.Forms.Router @inject Repository ServerRepository @inject ServerService ServerService @@ -21,10 +21,12 @@ @inject AlertService AlertService @inject IdentityService IdentityService @inject ConfigService ConfigService +@inject ILogger Logger +@inject ILoggerFactory LoggerFactory @implements IDisposable - +
@@ -43,21 +45,21 @@
@{ - var color = ServerUtilsHelper.GetColorFromState(Console.State); + var color = ServerUtilsHelper.GetColorFromState(Console.State); } - @(Console.State) + @(Console.State) (@(Formatter.FormatUptime(DateTime.UtcNow - Console.LastStateChangeTimestamp))) - +
- - - @(Server.Node.Fqdn):@(Server.MainAllocation.Port) - + + + @(Server.Node.Fqdn):@(Server.MainAllocation.Port) +
@*
@@ -73,47 +75,47 @@
@if (Console.State == ServerState.Offline) { - - - + + + } else { - + } @if (Console.State == ServerState.Offline || Console.State == ServerState.Installing) { - + } else { - - - + + + } @if (Console.State == ServerState.Offline || Console.State == ServerState.Installing) { - + } else { - - - + + + }
@@ -154,7 +156,7 @@ { - + @@ -186,7 +188,7 @@ - + } @@ -233,7 +235,7 @@ await lazyLoader.SetText("Establishing a connection to the server"); // Create console wrapper - Console = new ServerConsole(Server); + Console = new ServerConsole(Server, LoggerFactory); // Configure Console.OnStateChange += async state => await HandleStateChange(state); @@ -266,7 +268,7 @@ if (httpRequestException.InnerException is not SocketException socketException) throw; - Logger.Warn($"Unable to access the node's websocket endpoint: {socketException.Message}"); + Logger.LogWarning("Unable to access the node's websocket endpoint: {socketException}", socketException); // Change the ui and... IsNodeOffline = true; @@ -328,8 +330,13 @@ { if (!ConfigService.Get().DisableServerKillWarning) { - if (!await AlertService.YesNo("Do you really want to kill the server? This can result in data loss or corrupted server files")) - return; + await AlertService.Confirm( + "Server kill confirmation", + "Do you really want to kill the server? This can result in data loss or corrupted server files", + async () => await SendSignalHandled(PowerAction.Kill) + ); + + return; } await SendSignalHandled(PowerAction.Kill); @@ -347,8 +354,7 @@ } catch (Exception e) { - Logger.Warn($"An error occured while sending power action {action} to server {Server.Id}:"); - Logger.Warn(e); + Logger.LogWarning("An error occured while sending power action {action} to server {serverId}: {e}", action, Server.Id, e); await ToastService.Danger("An error occured while sending power action to server. Check the console for more information"); } diff --git a/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor b/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor index b93b9722..cc1317f3 100644 --- a/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor +++ b/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor @@ -3,7 +3,7 @@ @using BlazorTable @using MoonCore.Abstractions @using MoonCore.Exceptions -@using MoonCoreUI.Services + @inject Repository NodeRepository @inject Repository AllocationRepository @@ -32,7 +32,7 @@
-
+
@* - + *@
@@ -67,7 +67,7 @@ [Parameter] public ServerNode Node { get; set; } // A bit long, lol - private AutoListCrud Crud; + //private AutoListCrud Crud; // Quick add values private string IpAddress = "0.0.0.0"; @@ -101,31 +101,31 @@ NodeRepository.Update(Node!); await ToastService.Success($"Added {added} allocations and skipped {skipped} ports due to existing allocations"); - await Crud.Reload(); + //await Crud.Reload(); } private async Task DeleteAllAllocations() { - if (!await AlertService.YesNo("Do you really want to delete all allocations?", "Yes", "No")) - return; - - foreach (var allocation in Node!.Allocations.ToArray()) // To array in order to prevent collection modified exception + await AlertService.Confirm("Confirm mass deletion", "Do you really want to delete all allocations?", async () => { - // Check if a server is using this allocation before deleting - - if (ServerRepository - .Get() - .Any(x => x.Allocations.Any(y => y.Id == allocation.Id))) + foreach (var allocation in Node!.Allocations.ToArray()) // To array in order to prevent collection modified exception { - await ToastService.Danger($"Unable to delete allocation with port {allocation.Port} due to a server using this allocation"); - continue; - } + // Check if a server is using this allocation before deleting - AllocationRepository.Delete(allocation); - } + if (ServerRepository + .Get() + .Any(x => x.Allocations.Any(y => y.Id == allocation.Id))) + { + await ToastService.Danger($"Unable to delete allocation with port {allocation.Port} due to a server using this allocation"); + continue; + } + + AllocationRepository.Delete(allocation); + } - await ToastService.Success("Successfully deleted allocations"); - await Crud.Reload(); + await ToastService.Success("Successfully deleted allocations"); + //await Crud.Reload(); + }); } private Task ValidateDelete(ServerAllocation allocation) diff --git a/Moonlight/Features/Servers/UI/NodeComponents/NodeLogs.razor b/Moonlight/Features/Servers/UI/NodeComponents/NodeLogs.razor index 38da293e..542c69fb 100644 --- a/Moonlight/Features/Servers/UI/NodeComponents/NodeLogs.razor +++ b/Moonlight/Features/Servers/UI/NodeComponents/NodeLogs.razor @@ -1,13 +1,14 @@ @using MoonCore.Helpers @using Moonlight.Features.Servers.Entities @using MoonCore.Services -@using MoonCoreUI.Services + @using Moonlight.Core.Configuration @using Moonlight.Features.Servers.Services @inject ConfigService ConfigService @inject NodeService NodeService @inject ToastService ToastService +@inject ILogger Logger
@@ -69,8 +70,7 @@ } catch (Exception e) { - Logger.Warn($"An error occured while fetching logs from node '{Node.Name}'"); - Logger.Warn(e); + Logger.LogWarning("An error occured while fetching logs from node '{name}': {e}", Node.Name, e); await ToastService.Danger("An error occured while fetching logs. Please try again later"); } diff --git a/Moonlight/Features/Servers/UI/NodeComponents/NodeSetup.razor b/Moonlight/Features/Servers/UI/NodeComponents/NodeSetup.razor index 447a9d1a..13d3a782 100644 --- a/Moonlight/Features/Servers/UI/NodeComponents/NodeSetup.razor +++ b/Moonlight/Features/Servers/UI/NodeComponents/NodeSetup.razor @@ -2,6 +2,7 @@ @using MoonCore.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Services +@using IdentityService = Moonlight.Core.Services.IdentityService @inject ConfigService ConfigService @inject IdentityService IdentityService diff --git a/Moonlight/Features/Servers/UI/UserViews/Backups.razor b/Moonlight/Features/Servers/UI/UserViews/Backups.razor index bcba5200..9c9fdc69 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Backups.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Backups.razor @@ -2,7 +2,7 @@ @using MoonCore.Abstractions @using Microsoft.EntityFrameworkCore @using MoonCore.Helpers -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Events @using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.Models.Enums @@ -13,13 +13,14 @@ @inject ToastService ToastService @inject AlertService AlertService @inject NavigationManager Navigation +@inject ServerEvents ServerEvents @implements IDisposable
- + Create backup
@@ -171,23 +172,23 @@ public async Task Restore(ServerBackup backup) { - if(!await AlertService.YesNo("Do you really want to restore this backup? All files on the server will be deleted and replaced by the backup")) - return; - - await ServerService.Backup.Restore(Server, backup); + await AlertService.Confirm("Confirm backup restore", "Do you really want to restore this backup? All files on the server will be deleted and replaced by the backup", async () => + { + await ServerService.Backup.Restore(Server, backup); - await ToastService.Success("Successfully restored backup"); + await ToastService.Success("Successfully restored backup"); + }); } public async Task Delete(ServerBackup backup, bool safeDelete = true) { - if(!await AlertService.YesNo("Do you really want to delete this backup? Deleted backups cannot be restored")) - return; - - await ServerService.Backup.Delete(Server, backup, safeDelete); + await AlertService.Confirm("Confirm backup deletion", "Do you really want to delete this backup? Deleted backups cannot be restored", async () => + { + await ServerService.Backup.Delete(Server, backup, safeDelete); - await ToastService.Success("Successfully deleted backup"); - await LazyLoader.Reload(); + await ToastService.Success("Successfully deleted backup"); + await LazyLoader.Reload(); + }); } private async Task Download(ServerBackup backup) diff --git a/Moonlight/Features/Servers/UI/UserViews/Console.razor b/Moonlight/Features/Servers/UI/UserViews/Console.razor index fe50781a..9b0d1bf4 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Console.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Console.razor @@ -17,7 +17,7 @@
- + Execute
diff --git a/Moonlight/Features/Servers/UI/UserViews/Files.razor b/Moonlight/Features/Servers/UI/UserViews/Files.razor index 6f05f72f..79befcfb 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Files.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Files.razor @@ -11,7 +11,7 @@ @implements IDisposable - + diff --git a/Moonlight/Features/Servers/UI/UserViews/Network.razor b/Moonlight/Features/Servers/UI/UserViews/Network.razor index 7634372e..4de96d38 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Network.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Network.razor @@ -3,7 +3,7 @@ @using Microsoft.EntityFrameworkCore @using MoonCore.Abstractions @using MoonCore.Helpers -@using MoonCoreUI.Services + @inject Repository NetworkRepository @inject Repository ServerRepository @@ -33,8 +33,8 @@
@if (!Server.DisablePublicNetwork) { - - + + - - + + - - - + + + - - + + - - + + } else { diff --git a/Moonlight/Features/Servers/UI/UserViews/Reset.razor b/Moonlight/Features/Servers/UI/UserViews/Reset.razor index 740e7607..b68a7eba 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Reset.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Reset.razor @@ -3,7 +3,7 @@ @using Moonlight.Features.Servers.Models.Abstractions @using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Services -@using MoonCoreUI.Services + @implements IDisposable @@ -20,7 +20,7 @@

@if (Console.State == ServerState.Offline) { - + Reinstall } else { @@ -35,7 +35,7 @@

@if (Console.State == ServerState.Offline) { - + Reset } else { @@ -43,14 +43,15 @@ }
-
@* TODO: Make deleting configurable to show or not *@ +
+ @* TODO: Make deleting configurable to show or not *@

This deletes your server. The deleted data is not recoverable. Please make sure you have a backup of the data before deleting the server

@if (Console.State == ServerState.Offline) { - + Delete } else { @@ -78,50 +79,48 @@ private async Task Reinstall() { - if (!await AlertService.YesNo("Do you want to reinstall this server? This may replace/delete some files")) - return; - - await ServerService.Console.SendAction(Server, PowerAction.Install); + await AlertService.Confirm("Confirm reinstall", "Do you want to reinstall this server? This may replace/delete some files", async () => { await ServerService.Console.SendAction(Server, PowerAction.Install); }); } private async Task ResetServer() { - if (!await AlertService.YesNo("Do you want to reset this server? This will delete all files and run the install script")) - return; - - await ToastService.CreateProgress("serverReset", "Reset: Deleting files"); + await AlertService.Confirm("Confirm server reset", "Do you want to reset this server? This will delete all files and run the install script", async () => + { + await ToastService.CreateProgress("serverReset", "Reset: Deleting files"); - using var fileAccess = await ServerService.OpenFileAccess(Server); + using var fileAccess = await ServerService.OpenFileAccess(Server); - var files = await fileAccess.List(); - int i = 0; + var files = await fileAccess.List(); + int i = 0; - foreach (var fileEntry in files) - { - i++; + foreach (var fileEntry in files) + { + i++; - await ToastService.ModifyProgress("serverReset", $"Reset: Deleting files [{i} / {files.Length}]"); - await fileAccess.Delete(fileEntry); - } + await ToastService.UpdateProgress("serverReset", $"Reset: Deleting files [{i} / {files.Length}]"); + await fileAccess.Delete(fileEntry); + } - await ToastService.ModifyProgress("serverReset", "Reset: Starting install script"); + await ToastService.UpdateProgress("serverReset", "Reset: Starting install script"); - await ServerService.Console.SendAction(Server, PowerAction.Install); + await ServerService.Console.SendAction(Server, PowerAction.Install); - await ToastService.RemoveProgress("serverReset"); + await ToastService.DeleteProgress("serverReset"); + }); } private async Task Delete() { - var input = await AlertService.Text($"Please type '{Server.Name}' to confirm deleting this server"); - - if(input != Server.Name) - return; + await AlertService.Text("Server deletion", $"Please type '{Server.Name}' to confirm deleting this server", async input => + { + if (input != Server.Name) + return; - await ServerService.Delete(Server); + await ServerService.Delete(Server); - await ToastService.Success("Successfully deleted server"); - Navigation.NavigateTo("/servers"); + await ToastService.Success("Successfully deleted server"); + Navigation.NavigateTo("/servers"); + }); } private async Task OnStateChanged(ServerState _) => await InvokeAsync(StateHasChanged); diff --git a/Moonlight/Features/Servers/UI/UserViews/Schedules.razor b/Moonlight/Features/Servers/UI/UserViews/Schedules.razor index 23099aa7..9f7e37fd 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Schedules.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Schedules.razor @@ -2,7 +2,7 @@ @using MoonCore.Abstractions @using Microsoft.EntityFrameworkCore @using MoonCore.Helpers -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Models.Forms.Users.Schedules @using Moonlight.Features.Servers.Services @using Newtonsoft.Json @@ -19,11 +19,13 @@
- + Create new schedule @foreach (var schedule in ServerWithSchedules.Schedules) { - + + @schedule.Name + }
@@ -158,15 +160,12 @@
- - @code { [CascadingParameter] public Server Server { get; set; } private Server ServerWithSchedules; private LazyLoader LazyLoader; - private FormModalLauncher Launcher; private ServerSchedule? SelectedSchedule; private List SortedItems = new(); @@ -208,11 +207,11 @@ private async Task CreateSchedule() { - await Launcher.Show("Create a new schedule", async form => + await AlertService.Text("New schedule", "Create a new schedule", async name => { ServerWithSchedules.Schedules.Add(new() { - Name = form.Name + Name = name }); ServerRepository.Update(ServerWithSchedules); @@ -243,7 +242,8 @@ return; } - await Launcher.Show("Configure action", async formData => { await AddScheduleAction(NewItemActionType, SelectedSchedule.Items.Count, formData); }, action.FormType); + // TODO: Redo everything here + //await Launcher.Show("Configure action", async formData => { await AddScheduleAction(NewItemActionType, SelectedSchedule.Items.Count, formData); }, action.FormType); } private async Task AddScheduleAction(string type, int priority, object data) @@ -276,7 +276,7 @@ var action = ScheduleService.Actions.First(x => x.Key == item.Action).Value; var formModel = JsonConvert.DeserializeObject(item.DataJson, action.FormType)!; - +/* await Launcher.Show("Configure action", async formData => { item.DataJson = JsonConvert.SerializeObject(formData); @@ -284,7 +284,7 @@ ScheduleItemRepository.Update(item); await ToastService.Success("Successfully updated action"); - }, action.FormType, formModel: formModel); + }, action.FormType, formModel: formModel);*/ } private async Task MoveItem(ServerScheduleItem item, int move) @@ -312,18 +312,18 @@ if (SelectedSchedule == null) return; - if (!await AlertService.YesNo("Do you really want to delete this action? This cannot be undone")) - return; - - SortedItems.Remove(item); - SelectedSchedule.Items.Remove(item); + await AlertService.Confirm("Confirm schedule item deletion", "Do you really want to delete this action? This cannot be undone", async () => + { + SortedItems.Remove(item); + SelectedSchedule.Items.Remove(item); - ScheduleRepository.Update(SelectedSchedule); + ScheduleRepository.Update(SelectedSchedule); - await FixPriorities(); + await FixPriorities(); - await ToastService.Success("Successfully deleted action"); - await LazyLoader.Reload(); + await ToastService.Success("Successfully deleted action"); + await LazyLoader.Reload(); + }); } private Task FixPriorities() @@ -346,27 +346,27 @@ if (SelectedSchedule == null) return; - if (!await AlertService.YesNo($"Do you really want to delete the schedule '{SelectedSchedule.Name}'? This cannot be undone")) - return; - - foreach (var item in SelectedSchedule.Items.ToArray()) + await AlertService.Confirm("Confirm schedule deletion", $"Do you really want to delete the schedule '{SelectedSchedule.Name}'? This cannot be undone", async () => { - try - { - ScheduleItemRepository.Delete(item); - } - catch (Exception) + foreach (var item in SelectedSchedule.Items.ToArray()) { - /* this should not fail the operation */ + try + { + ScheduleItemRepository.Delete(item); + } + catch (Exception) + { + /* this should not fail the operation */ + } } - } - ScheduleRepository.Delete(SelectedSchedule); + ScheduleRepository.Delete(SelectedSchedule); - SelectedSchedule = null; + SelectedSchedule = null; - await ToastService.Success("Successfully deleted schedule"); - await LazyLoader.Reload(); + await ToastService.Success("Successfully deleted schedule"); + await LazyLoader.Reload(); + }); } private async Task RunSelectedSchedule() @@ -378,7 +378,7 @@ var result = await ScheduleService.Run(Server, SelectedSchedule); - await ToastService.RemoveProgress("scheduleRun"); + await ToastService.DeleteProgress("scheduleRun"); if (result.Failed) await ToastService.Danger($"Schedule run failed ({result.ExecutionSeconds}s)"); diff --git a/Moonlight/Features/Servers/UI/UserViews/Variables.razor b/Moonlight/Features/Servers/UI/UserViews/Variables.razor index d96d9ff7..f91560c8 100644 --- a/Moonlight/Features/Servers/UI/UserViews/Variables.razor +++ b/Moonlight/Features/Servers/UI/UserViews/Variables.razor @@ -2,7 +2,7 @@ @using Moonlight.Features.Servers.UI.Components.VariableViews @using MoonCore.Abstractions @using Microsoft.EntityFrameworkCore -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Entities.Enums @inject Repository ServerRepository @@ -10,14 +10,14 @@ @inject Repository ServerVariableRepository @inject ToastService ToastService - +
@if (Image.AllowDockerImageChange) { - diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor index 6275b273..8beba91d 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor @@ -4,7 +4,7 @@ @using Microsoft.EntityFrameworkCore @using MoonCore.Abstractions @using MoonCore.Exceptions -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Helpers @using Moonlight.Features.Servers.Models.Forms.Admin.Images @@ -17,9 +17,10 @@ @inject Repository ImageRepository @inject ImageConversionHelper ImageConversionHelper -@inject FileDownloadService FileDownloadService +@inject DownloadService DownloadService @inject ToastService ToastService @inject AlertService AlertService +@inject ILogger Logger @attribute [RequirePermission(5002)] @@ -34,14 +35,14 @@ CustomDelete="CustomDelete" @ref="Crud"> - - + + - - - + + + - - + + @* Download and import a image from our repository or create a new one. Need help? Check out our documentation @@ -84,14 +85,14 @@ Import - + *@ @code { private AutoCrud Crud; - private SmartCustomFileSelect ImageUpload; - private SmartCustomFileSelect EggUpload; + private MCBCustomFileSelect ImageUpload; + private MCBCustomFileSelect EggUpload; private IEnumerable Load(Repository repository) { @@ -178,7 +179,7 @@ { var json = await ImageConversionHelper.ExportAsJson(image); var imageName = image.Name.Replace(" ", ""); - await FileDownloadService.DownloadString($"{imageName}.json", json); + await DownloadService.DownloadString($"{imageName}.json", json); await ToastService.Success($"Successfully exported '{image.Name}'"); } @@ -198,7 +199,7 @@ await ToastService.Success($"Successfully imported '{image.Name}'"); await ImageUpload.RemoveSelection(); - await Crud.Reload(); + //await Crud.Reload(); } catch (DisplayException) { @@ -206,8 +207,7 @@ } catch (Exception e) { - Logger.Warn("An error occured while importing a image"); - Logger.Warn(e); + Logger.LogWarning("An error occured while importing a image: {e}", e); await ToastService.Danger("Unable to import egg: " + e.Message); } @@ -219,45 +219,39 @@ private async Task ImportEgg(IBrowserFile file) { - var confirm = await AlertService.YesNo("Importing pterodactyl eggs is a experimental feature and may result in unusable images. Are you sure you want to proceed?", - "Yes, i take the risk", - "Cancel"); - - if (!confirm) - { - await EggUpload.RemoveSelection(); - return; - } - - try - { - var stream = file.OpenReadStream(); - - using var sr = new StreamReader(stream); - var content = await sr.ReadToEndAsync(); - - var image = await ImageConversionHelper.ImportFromEggJson(content); - - ImageRepository.Add(image); - await ToastService.Success($"Successfully imported '{image.Name}'"); - - await EggUpload.RemoveSelection(); - await Crud.Reload(); - } - catch (DisplayException) - { - throw; - } - catch (Exception e) - { - Logger.Warn("An error occured while importing a pterodactyl egg"); - Logger.Warn(e); - - await ToastService.Danger("Unable to import egg: " + e.Message); - } - finally - { - await EggUpload.RemoveSelection(); - } + await AlertService.Confirm("Import a pterodactyl egg", "Importing pterodactyl eggs is a experimental feature and may result in unusable images. Are you sure you want to proceed?", + async () => + { + try + { + var stream = file.OpenReadStream(); + + using var sr = new StreamReader(stream); + var content = await sr.ReadToEndAsync(); + + var image = await ImageConversionHelper.ImportFromEggJson(content); + + ImageRepository.Add(image); + await ToastService.Success($"Successfully imported '{image.Name}'"); + + await EggUpload.RemoveSelection(); + //await Crud.Reload(); + } + catch (DisplayException) + { + throw; + } + catch (Exception e) + { + Logger.LogWarning("An error occured while importing a pterodactyl egg: {e}", e); + + await ToastService.Danger("Unable to import egg: " + e.Message); + } + finally + { + await EggUpload.RemoveSelection(); + } + }, + "Yes, i take the risk"); } } \ No newline at end of file diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Images/View.razor b/Moonlight/Features/Servers/UI/Views/Admin/Images/View.razor index 83599c99..6a3b9357 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Images/View.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Images/View.razor @@ -1,7 +1,7 @@ @page "/admin/servers/images/view/{Id:int}/{Route?}" @using Mappy.Net -@using MoonCoreUI.Services + @using MoonCore.Abstractions @using Moonlight.Features.Servers.Entities @using Microsoft.EntityFrameworkCore diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor index 177ebe9b..75aab3af 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor @@ -4,9 +4,7 @@ @using Microsoft.EntityFrameworkCore @using MoonCore.Abstractions @using MoonCore.Exceptions -@using MoonCore.Helpers -@using MoonCoreUI.Models -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Models.Forms.Admin.Servers @@ -15,11 +13,12 @@ @inject ServerService ServerService @inject Repository ServerRepository @inject ToastService ToastService +@inject ILogger Logger @attribute [RequirePermission(5000)] - +@* - - - + + + - - + + - - + + - + Create a new server in order to manage it using this page. Need help? Check out our documentation - +*@ @code { + /* private void OnConfigure(AutoCrudOptions options) { options.AddCustomItemLoader("FreeAllocations", LoadFreeAllocations); options.AddCustomDisplayFunction("AllocationWithIp", allocation => allocation.IpAddress + ":" + allocation.Port); - } + }*/ private IEnumerable Load(Repository repository) { @@ -124,8 +124,7 @@ } catch (Exception e) { - Logger.Error("Unable to sync server changes due to an error occuring"); - Logger.Error(e); + Logger.LogError("Unable to sync server changes due to an error occuring: {e}", e); await ToastService.Danger("An error occured while sending the changes to the daemon"); } diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Manager.razor b/Moonlight/Features/Servers/UI/Views/Admin/Manager.razor index ebe8b8c9..b3a981bc 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Manager.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Manager.razor @@ -2,7 +2,7 @@ @using MoonCore.Abstractions @using MoonCore.Helpers -@using MoonCoreUI.Services + @using Moonlight.Features.Servers.Api.Resources @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Services @@ -18,6 +18,7 @@ @inject ServerService ServerService @inject ToastService ToastService @inject AlertService AlertService +@inject ILogger Logger @attribute [RequirePermission(5000)] @@ -210,8 +211,7 @@ } catch (Exception e) { - Logger.Warn($"An error occured while fetching server list from node {node.Id}"); - Logger.Warn(e); + Logger.LogWarning("An error occured while fetching server list from node {nodeId}: {e}", node.Id, e); OfflineNodes.Add(node); @@ -248,33 +248,32 @@ await Table.SetPageSizeAsync(PageSize); // Confirm - if(!await AlertService.YesNo($"Do you really want to perform the action '{action}' for {items.Length} servers?")) - return; - - await ToastService.CreateProgress("multiPowerAction", "Preparing"); - - // Perform - int i = 0; - foreach (var item in items) + await AlertService.Confirm("Confirm power action", $"Do you really want to perform the action '{action}' for {items.Length} servers?", async () => { - try + await ToastService.CreateProgress("multiPowerAction", "Preparing"); + + // Perform + int i = 0; + foreach (var item in items) { - await ToastService.ModifyProgress("multiPowerAction", $"Sending power action [{i + 1} / {items.Length}]"); - await ServerService.Console.SendAction(item.Server, action); + try + { + await ToastService.UpdateProgress("multiPowerAction", $"Sending power action [{i + 1} / {items.Length}]"); + await ServerService.Console.SendAction(item.Server, action); - i++; - } - catch (Exception e) - { - Logger.Warn($"An error occured while performing power action on server {item.Server.Id}"); - Logger.Warn(e); + i++; + } + catch (Exception e) + { + Logger.LogWarning("An error occured while performing power action on server {serverId}: {e}", item.Server.Id, e); - await ToastService.Danger($"Unable to perform power action for server '{item.Server.Name}'"); + await ToastService.Danger($"Unable to perform power action for server '{item.Server.Name}'"); + } } - } - await ToastService.RemoveProgress("multiPowerAction"); - await ToastService.Success($"Successfully performed the action for {i} servers"); + await ToastService.DeleteProgress("multiPowerAction"); + await ToastService.Success($"Successfully performed the action for {i} servers"); + }); } public void Dispose() diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor index 1effa876..7a0148a5 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor @@ -15,6 +15,7 @@ @inject Repository NodeRepository @inject NodeService NodeService @inject IServiceProvider ServiceProvider +@inject ILogger Logger @implements IDisposable @@ -31,14 +32,14 @@ ValidateUpdate="ValidateUpdate" ValidateDelete="ValidateDelete"> - - + + - - - + + + - - + + - - + + - - + + - + + @* Add a new node in order to get started. Need help? Check out our documentation - + *@ @@ -140,8 +142,7 @@ } catch (Exception e) { - Logger.Warn($"Unable to fetch system status for node '{node.Name}'"); - Logger.Warn(e); + Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e); NodeStats[node.Id] = null; } diff --git a/Moonlight/Features/Servers/UI/Views/Servers/Index.razor b/Moonlight/Features/Servers/UI/Views/Servers/Index.razor index 80007814..bb634e89 100644 --- a/Moonlight/Features/Servers/UI/Views/Servers/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Servers/Index.razor @@ -14,6 +14,7 @@ @inject Repository ServerRepository @inject IdentityService IdentityService @inject ServerService ServerService +@inject ILogger Logger @@ -163,8 +164,7 @@ } catch (Exception e) { - Logger.Warn($"Unable to get server state for server {server.Id}"); - Logger.Warn(e); + Logger.LogWarning("Unable to get server state for server {serverId}: {e}", server.Id, e); } } }); @@ -187,8 +187,7 @@ } catch (Exception e) { - Logger.Warn($"Unable to get server stats for server {server.Id}"); - Logger.Warn(e); + Logger.LogWarning("Unable to get server stats for server {serverId}: {e}", server.Id, e); } } }); diff --git a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor index 2c24bdba..9af97c28 100644 --- a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor +++ b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor @@ -19,13 +19,13 @@ Loader="Load" ValidateAdd="ValidateAdd"> - - + + - - + + - - + + @* Create a new private network in order to connect multiple servers on the same node - + *@ @code diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 63a95038..50851cb9 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -93,8 +93,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/Moonlight/Pages/_Host.cshtml b/Moonlight/Pages/_Host.cshtml index b90a9547..58bc63a0 100644 --- a/Moonlight/Pages/_Host.cshtml +++ b/Moonlight/Pages/_Host.cshtml @@ -37,6 +37,8 @@ + + + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 6a553486..ce531bc7 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -1,6 +1,7 @@ using System.Security.Cryptography.X509Certificates; using MoonCore.Extensions; using MoonCore.Helpers; +using MoonCore.Logging; using MoonCore.Services; using Moonlight.Core.Configuration; using Moonlight.Core.Database; @@ -19,25 +20,41 @@ PathBuilder.File("storage", "configs", "core.json") ); -var builder = WebApplication.CreateBuilder(args); +// Build pre run logger +var loggerProviders = LoggerBuildHelper.BuildFromConfiguration(new() +{ + Console = new() + { + Enable = true, + EnableAnsiMode = true + }, + FileLogging = new() + { + Enable = true, + Path = PathBuilder.File("storage", "logs", "moonlight.log"), + EnableLogRotation = true, + RotateLogNameTemplate = PathBuilder.File("storage", "logs", "moonlight.{0}.log") + } +}); -// Setup logging -Logger.Setup( - logInConsole: true, - logInFile: true, - logPath: PathBuilder.File("storage", "logs", "moonlight.log"), - isDebug: builder.Environment.IsDevelopment(), - enableFileLogRotate: true, - rotateLogNameTemplate: PathBuilder.File("storage", "logs", "moonlight.{0}.log") -); +var preRunLoggerFactory = new LoggerFactory(); +preRunLoggerFactory.AddProviders(loggerProviders); +var preRunLogger = preRunLoggerFactory.CreateLogger(); + +preRunLogger.LogInformation("Initializing moonlight"); -builder.Logging.MigrateToMoonCore(); +// Initialisation +var builder = WebApplication.CreateBuilder(args); + +builder.Logging.ClearProviders(); +builder.Logging.AddProviders(loggerProviders); builder.Logging.AddConfiguration("{\"LogLevel\":{\"Default\":\"Information\",\"Microsoft.AspNetCore\":\"Warning\"}}"); // Configure http if (builder.Environment.IsDevelopment()) - Logger.Info( - "Disabling http pipeline configuration as the environment is set to development. All http endpoint config options in the core.json will be ignored"); +{ + preRunLogger.LogInformation("Disabling http pipeline configuration as the environment is set to development. All http endpoint config options in the core.json will be ignored"); +} else { var httpConfig = configService.Get().Http; @@ -49,16 +66,15 @@ try { certificate = X509Certificate2.CreateFromPemFile(httpConfig.CertPath, httpConfig.KeyPath); - - Logger.Info($"Successfully loaded certificate '{certificate.FriendlyName}'"); + + preRunLogger.LogInformation("Successfully loaded certificate {name}", certificate.Subject); } catch (Exception e) { - Logger.Fatal("An error occured while loading certificate"); - Logger.Fatal(e); + preRunLogger.LogCritical("An error occured while loading certificate: {e}", e); } } - + builder.WebHost.ConfigureMoonCoreHttp( httpConfig.HttpPort, httpConfig.EnableSsl, @@ -68,19 +84,26 @@ } // Build feature service and perform load -var featureService = new FeatureService(configService); +var featureService = new FeatureService( + configService, + preRunLoggerFactory.CreateLogger() +); + await featureService.Load(); // Build plugin service and perform load -var pluginService = new PluginService(); +var pluginService = new PluginService( + preRunLoggerFactory.CreateLogger() +); + await pluginService.Load(); try { // Check database migrations await DatabaseCheckHelper.Check( - new DataContext(configService), - false + preRunLoggerFactory.CreateLogger(), + new DataContext(configService) ); } catch (MySqlException e) @@ -89,13 +112,13 @@ await DatabaseCheckHelper.Check( { if (eosException.Message.Contains("read 4 header bytes")) { - Logger.Warn("The mysql server appears to be still booting up. Exiting..."); - + preRunLogger.LogWarning("The mysql server appears to be still booting up. Exiting..."); + Environment.Exit(1); return; } } - + throw; } @@ -105,7 +128,7 @@ await DatabaseCheckHelper.Check( builder.Services.AddSingleton(pluginService); // Feature hook -await featureService.PreInit(builder, pluginService); +await featureService.PreInit(builder, pluginService, preRunLoggerFactory); // Plugin hook await pluginService.PreInitialize(builder); diff --git a/Moonlight/_Imports.razor b/Moonlight/_Imports.razor index afa33440..f83e39b4 100644 --- a/Moonlight/_Imports.razor +++ b/Moonlight/_Imports.razor @@ -3,8 +3,12 @@ @using Microsoft.JSInterop @using Moonlight -@using MoonCoreUI.Components.Forms -@using MoonCoreUI.Components +@using MoonCore.Blazor.Components +@using MoonCore.Blazor.Forms +@using MoonCore.Blazor.Services +@using MoonCore.Blazor.Helpers +@using MoonCore.Blazor.Forms.Table +@using MoonCore.Blazor.Forms.Auto @using Moonlight.Core.UI @using Moonlight.Core.Attributes From 7fcd674b7fdb99d6d565f1da0961bccf15a273d2 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 24 Jun 2024 19:30:20 +0200 Subject: [PATCH 02/16] Improved mysql container boot state error handling --- Moonlight/Program.cs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index ce531bc7..44fea258 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -108,18 +108,31 @@ await DatabaseCheckHelper.Check( } catch (MySqlException e) { - if (e.InnerException is EndOfStreamException eosException) + bool IsBootException(MySqlException e) { - if (eosException.Message.Contains("read 4 header bytes")) + if (e.InnerException is EndOfStreamException eosException) { - preRunLogger.LogWarning("The mysql server appears to be still booting up. Exiting..."); - - Environment.Exit(1); - return; + if (!eosException.Message.Contains("read 4 header bytes")) + return false; + } + else if (e.InnerException is MySqlEndOfStreamException endOfStreamException) + { + if (!endOfStreamException.Message.Contains("An incomplete response was received from the server")) + return false; } + else + return false; + + return true; } - throw; + if (IsBootException(e)) + { + preRunLogger.LogWarning("The mysql server appears to be still booting up. Exiting..."); + + Environment.Exit(1); + return; + } } // Add pre constructed services From 2838a91e3ce4332e1be7236e0272ce4714af0b99 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 25 Jun 2024 17:52:58 +0200 Subject: [PATCH 03/16] Upgraded users, sessions and settings page --- Moonlight/Core/UI/Layouts/MainLayout.razor | 6 +- .../Core/UI/Views/Admin/Sys/Settings.razor | 222 ++++++++++++------ .../Core/UI/Views/Admin/Users/Index.razor | 92 ++++++-- .../Core/UI/Views/Admin/Users/Sessions.razor | 4 +- Moonlight/Moonlight.csproj | 4 +- Moonlight/_Imports.razor | 1 + 6 files changed, 222 insertions(+), 107 deletions(-) diff --git a/Moonlight/Core/UI/Layouts/MainLayout.razor b/Moonlight/Core/UI/Layouts/MainLayout.razor index c7c93001..ab8dceb0 100644 --- a/Moonlight/Core/UI/Layouts/MainLayout.razor +++ b/Moonlight/Core/UI/Layouts/MainLayout.razor @@ -78,9 +78,9 @@
- - - +
diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor index 12c2cee2..57000001 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor @@ -1,7 +1,11 @@ @page "/admin/sys/settings" +@using System.ComponentModel +@using System.Linq.Expressions @using Moonlight.Core.UI.Components.Navigations @using System.Reflection +@using MoonCore.Blazor.Models.Fast +@using MoonCore.Helpers @using MoonCore.Services @using Moonlight.Core.Configuration @@ -10,9 +14,9 @@ @attribute [RequirePermission(9999)] - + -@if (ModelToShow == null) +@if (CurrentModel == null) { No model found to show. Please refresh the page to go back @@ -20,20 +24,36 @@ } else { +
+ + Changes to these settings are live applied. The save button only make the changes persistently saved to disk + +
+
- @{ - string title; - - if (Path.Length == 0) - title = "Configuration"; +

+ @if (Path.Length == 0) + { + Configuration + } else { - title = "Configuration - " + string.Join(" - ", Path); + + Configuration + + @foreach (var subPart in Path.SkipLast(1)) + { + + @subPart + } + + + + @Path.Last() + } - } - -

@(title)

+
@@ -44,34 +64,24 @@ else
- - - Changes to these settings are live applied. The save button only make the changes persistently saved to disk - - +
- @{ - var props = ModelToShow - .GetType() - .GetProperties() - .Where(x => x.PropertyType.Assembly.FullName!.Contains("Moonlight") && x.PropertyType.IsClass) - .ToArray(); - } - - @foreach (var prop in props) + @foreach (var item in SidebarItems) { } @if (Path.Length != 0) { -
+
@@ -80,42 +90,21 @@ else
-
-
- - - @* - @foreach (var prop in Properties) - { -
- @{ - var typeToCreate = typeof(AutoProperty<>).MakeGenericType(prop.PropertyType); - var rf = ComponentHelper.FromType(typeToCreate, parameters => - { - parameters.Add("Data", ModelToShow); - parameters.Add("Property", prop); - }); - } - - @rf -
- }*@ -
-
-
+ + +
} @code { - [Parameter] - [SupplyParameterFromQuery] - public string? Section { get; set; } = ""; - - private object? ModelToShow; - private PropertyInfo[] Properties = Array.Empty(); - private string[] Path = Array.Empty(); + [Parameter] [SupplyParameterFromQuery] public string? Section { get; set; } = ""; + + private object? CurrentModel; + private string[] SidebarItems = []; + private string[] Path = []; + private PropertyInfo[] Properties = []; private LazyLoader? LazyLoader; @@ -124,21 +113,36 @@ else if (Section != null && Section.StartsWith("/")) Section = Section.TrimStart('/'); - Path = Section != null ? Section.Split("/") : Array.Empty(); + Path = Section != null ? Section.Split("/") : []; - ModelToShow = Resolve(ConfigService.Get(), Path, 0); + CurrentModel = Resolve(ConfigService.Get(), Path, 0); - if (ModelToShow != null) + if (CurrentModel == null) { - Properties = ModelToShow - .GetType() - .GetProperties() - .Where(x => !x.PropertyType.Assembly.FullName!.Contains("Moonlight")) - .ToArray(); + SidebarItems = []; + Properties = []; } else { - Properties = Array.Empty(); + var props = CurrentModel + .GetType() + .GetProperties() + .ToArray(); + + SidebarItems = props + .Where(x => + x.PropertyType.IsClass && + x.PropertyType.Namespace!.StartsWith("Moonlight") + ) + .Select(x => x.Name) + .ToArray(); + + Properties = props + .Where(x => + !x.PropertyType.Namespace.StartsWith("Moonlight") && + DefaultComponentSelector.GetDefault(x.PropertyType) != null // Check if a component has been registered for that type + ) + .ToArray(); } await InvokeAsync(StateHasChanged); @@ -146,16 +150,40 @@ else if (LazyLoader != null) await LazyLoader.Reload(); } + + private void OnFormConfigure(FastConfiguration configuration) + { + if(CurrentModel == null) // This will technically never be true because of the ui logic + return; + + foreach (var property in Properties) + { + var propConfig = configuration + .AddProperty(CreatePropertyAccessExpression(property)) + .WithDefaultComponent(); + + var customAttributes = property.GetCustomAttributes(false); + + if(customAttributes.Length == 0) + continue; + + if (TryGetAttribute(customAttributes, out DisplayNameAttribute nameAttribute)) + propConfig.WithName(nameAttribute.DisplayName); + else + propConfig.WithName(Formatter.ConvertCamelCaseToSpaces(property.Name)); + + if (TryGetAttribute(customAttributes, out DescriptionAttribute descriptionAttribute)) + propConfig.WithDescription(descriptionAttribute.Description); + } + } private string GetBackPath() { if (Path.Length == 1) return "settings"; - else - { - var path = string.Join('/', Path.Take(Path.Length - 1)).TrimEnd('/'); - return $"settings?section={path}"; - } + + var path = string.Join('/', Path.Take(Path.Length - 1)).TrimEnd('/'); + return $"settings?section={path}"; } private object? Resolve(object model, string[] path, int index) @@ -177,20 +205,60 @@ else return Resolve(prop.GetValue(model)!, path, index + 1); } - private Task Load(LazyLoader arg) - { - return Task.CompletedTask; - } + private Task Load(LazyLoader _) => Task.CompletedTask; // Seems useless, it more or less is, but it shows a nice loading ui while the form changes - private async Task Save() + private async Task Save() // Saves all changes to disk, all changes are live updated as the config service reference will be edited directly { ConfigService.Save(); await ToastService.Success("Successfully saved config to disk"); } - private async Task Reload() + private async Task Reload() // This will also discard all unsaved changes { ConfigService.Reload(); await ToastService.Info("Reloaded configuration from disk"); } + + // Building lambda expressions at runtime using reflection is nice ;3 + public static Expression> CreatePropertyAccessExpression(PropertyInfo property) + { + // Get the type that declares the property + Type declaringType = property.DeclaringType!; + + // Create a parameter expression for the input object + ParameterExpression param = Expression.Parameter(typeof(object), "obj"); + + // Create an expression to cast the input object to the declaring type + UnaryExpression cast = Expression.Convert(param, declaringType); + + // Create an expression to access the property + MemberExpression propertyAccess = Expression.Property(cast, property); + + // Create an expression to cast the property value to object + UnaryExpression castResult = Expression.Convert(propertyAccess, typeof(object)); + + // Create the final lambda expression + Expression> lambda = Expression.Lambda>( + castResult, param); + + return lambda; + } + + // From MoonCore. TODO: Maybe provide this and the above function as mooncore helper + private bool TryGetAttribute(object[] attributes, out T result) where T : Attribute + { + var searchType = typeof(T); + + var attr = attributes + .FirstOrDefault(x => x.GetType() == searchType); + + if (attr == null) + { + result = default!; + return false; + } + + result = (attr as T)!; + return true; + } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Users/Index.razor b/Moonlight/Core/UI/Views/Admin/Users/Index.razor index 2b9ad361..28aa6d6c 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Index.razor @@ -1,7 +1,11 @@ @page "/admin/users" +@using System.ComponentModel.DataAnnotations @using Moonlight.Core.UI.Components.Navigations @using MoonCore.Abstractions +@using MoonCore.Blazor.Forms.Fast.Components +@using MoonCore.Blazor.Models.Fast +@using MoonCore.Blazor.Models.Fast.Validators @using Moonlight.Core.Database.Entities @using MoonCore.Exceptions @using Moonlight.Core.Models.Abstractions @@ -14,36 +18,30 @@ - + - @* - + + Change password - *@ - + + @code { - private IEnumerable Load(Repository repository) - { - return repository.Get(); - } - private async Task ChangePassword(User user) { - await AlertService.Text("", "Enter a new password for {user.Username}", async newPassword => + await AlertService.Text($"Change password for '{user.Username}'", "Enter a new password for {user.Username}", async newPassword => { // This handles empty and canceled input if (string.IsNullOrEmpty(newPassword)) @@ -52,18 +50,66 @@ await AuthenticationProvider.ChangePassword(user, newPassword); }); } + + private void OnConfigure(FastCrudConfiguration configuration) + { + configuration.CustomCreate += async user => + { + var result = await AuthenticationProvider.Register(user.Username, user.Email, user.Password); + + if (result == null) + throw new DisplayException("An unknown error occured while creating user"); + }; - private async Task Add(User user) + configuration.ValidateEdit += async user => + { + await AuthenticationProvider.ChangeDetails(user, user.Email, user.Username); + }; + } + + private void OnConfigureCreate(FastConfiguration configuration) { - var result = await AuthenticationProvider.Register(user.Username, user.Email, user.Password); + configuration.AddProperty(x => x.Username) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^[a-z][a-z0-9]*$", "Usernames can only contain lowercase characters and numbers and should not start with a number")) + .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) + .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); + + configuration.AddProperty(x => x.Email) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^.+@.+$", "You need to enter a valid email address")); + + configuration.AddProperty(x => x.Password) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(x => x.Length >= 8 ? ValidationResult.Success : new ValidationResult("The password must be at least 8 characters long")) + .WithValidation(x => x.Length <= 256 ? ValidationResult.Success : new ValidationResult("The password must not be longer than 256 characters")); + } + + private void OnConfigureEdit(FastConfiguration configuration, User currentUser) + { + configuration.AddProperty(x => x.Username) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^[a-z][a-z0-9]*$", "Usernames can only contain lowercase characters and numbers and should not start with a number")) + .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) + .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); + + configuration.AddProperty(x => x.Email) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^.+@.+$", "You need to enter a valid email address")); - if (result == null) - throw new DisplayException("An unknown error occured while creating user"); + configuration.AddProperty(x => x.Totp) + .WithComponent() + .WithName("Two factor authentication") + .WithDescription("This toggles the use of the two factor authentication"); } - // To notify the authentication provider before we update the data in the database, we call it here - private async Task ValidateUpdate(User user) + private IEnumerable Search(IEnumerable source, string term) { - await AuthenticationProvider.ChangeDetails(user, user.Email, user.Username); + return source.Where(x => x.Username.Contains(term) || x.Email.Contains(term)); } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor index c4fac3f5..3f96fffe 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor @@ -62,8 +62,8 @@ diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 50851cb9..58bb91c6 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -93,8 +93,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/Moonlight/_Imports.razor b/Moonlight/_Imports.razor index f83e39b4..8704dafb 100644 --- a/Moonlight/_Imports.razor +++ b/Moonlight/_Imports.razor @@ -9,6 +9,7 @@ @using MoonCore.Blazor.Helpers @using MoonCore.Blazor.Forms.Table @using MoonCore.Blazor.Forms.Auto +@using MoonCore.Blazor.Forms.Fast @using Moonlight.Core.UI @using Moonlight.Core.Attributes From 95a5eafec2891415c40d4e025adbc8852253c775 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 25 Jun 2024 23:19:27 +0200 Subject: [PATCH 04/16] Upgraded server networks and api keys --- Moonlight/Core/UI/Views/Admin/Api/Keys.razor | 47 +++++++----- .../Servers/UI/Views/Servers/Networks.razor | 72 ++++++++++++------- Moonlight/Moonlight.csproj | 4 +- 3 files changed, 79 insertions(+), 44 deletions(-) diff --git a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor index 03021e46..3bd01e14 100644 --- a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor +++ b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor @@ -1,10 +1,8 @@ @page "/admin/api/keys" -@using MoonCore.Abstractions -@using MoonCore.Blazor.Models.Forms +@using MoonCore.Blazor.Models.Fast @using MoonCore.Helpers @using Moonlight.Core.Database.Entities -@using Moonlight.Core.Models.Forms.ApiKeys @using Moonlight.Core.UI.Components.Navigations @inject ClipboardService ClipboardService @@ -15,17 +13,15 @@
- + - @* - - - Add a new node in order to get started. Need help? Check out our documentation - - *@ - + @code @@ -126,78 +124,162 @@ { UpdateTimer = new(async _ => { - NodeStats.Clear(); + try + { + NodeStats.Clear(); - using var scope = ServiceProvider.CreateScope(); - var nodeRepo = scope.ServiceProvider.GetRequiredService>(); - var nodes = nodeRepo.Get().ToArray(); + using var scope = ServiceProvider.CreateScope(); + var nodeRepo = scope.ServiceProvider.GetRequiredService>(); + var nodes = nodeRepo.Get().ToArray(); - foreach (var node in nodes) - { - try + foreach (var node in nodes) { - var status = await NodeService.GetStatus(node); + try + { + var status = await NodeService.GetStatus(node); - NodeStats[node.Id] = status; - } - catch (Exception e) - { - Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e); + NodeStats[node.Id] = status; + } + catch (Exception e) + { + Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e); - NodeStats[node.Id] = null; - } + NodeStats[node.Id] = null; + } - await InvokeAsync(StateHasChanged); + await InvokeAsync(StateHasChanged); + } + } + catch (Exception e) + { + Logger.LogError("Unable to update node stats due to an unhandled error: {e}", e); } }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); return Task.CompletedTask; } - - private IEnumerable LoadData(Repository repository) + + private IEnumerable Loader(Repository repository) { return repository - .Get(); + .Get() + .Include(x => x.Allocations); } - private Task ValidateDelete(ServerNode node) + private void OnConfigure(FastCrudConfiguration configuration) { - if (ServerRepository - .Get() - .Any(x => x.Node.Id == node.Id)) + configuration.ValidateDelete = node => { - throw new DisplayException("There are still servers on this node. Delete the servers in order to delete the node"); - } + if (ServerRepository + .Get() + .Any(x => x.Node.Id == node.Id)) + { + throw new DisplayException("There are still servers on this node. Delete the servers in order to delete the node"); + } - if (NodeRepository - .Get() - .Include(x => x.Allocations) - .First(x => x.Id == node.Id) - .Allocations - .Any()) + if (NodeRepository + .Get() + .Include(x => x.Allocations) + .First(x => x.Id == node.Id) + .Allocations + .Any()) + { + throw new DisplayException("There are still allocations on this node. Delete the allocations in order to delete the node"); + } + + return Task.CompletedTask; + }; + + configuration.ValidateCreate = node => { - throw new DisplayException("There are still allocations on this node. Delete the allocations in order to delete the node"); - } + ValidateFqdn(node); + + node.Token = Formatter.GenerateString(32); + + return Task.CompletedTask; + }; - return Task.CompletedTask; + configuration.ValidateEdit = node => + { + ValidateFqdn(node); + + return Task.CompletedTask; + }; } - private Task ValidateAdd(ServerNode node) + private void OnConfigureCreate(FastConfiguration configuration) { - ValidateFqdn(node); + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithValidation(FastValidators.Required); - node.Token = Formatter.GenerateString(32); + configuration.AddProperty(x => x.Fqdn) + .WithDefaultComponent() + .WithDescription("This needs to be the ip or domain of the node depending on the ssl settings") + .WithValidation(FastValidators.Required); - return Task.CompletedTask; + configuration.AddProperty(x => x.Ssl) + .WithComponent() + .WithDescription("This enables ssl for the http connections to the node. Only enable this if you have the cert installed on the node"); + + configuration.AddProperty(x => x.HttpPort) + .WithDefaultComponent() + .WithDescription("This is the http(s) port used by the node to allow communication to the node from the panel"); + + configuration.AddProperty(x => x.FtpPort) + .WithDefaultComponent() + .WithDescription("This is the ftp port users can use to access their servers filesystem via their ftp client"); } - private Task ValidateUpdate(ServerNode node) + private void OnConfigureEdit(FastConfiguration configuration, ServerNode node) { - ValidateFqdn(node); + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithPage("Settings") + .WithValidation(FastValidators.Required); - return Task.CompletedTask; - } + configuration.AddProperty(x => x.Fqdn) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This needs to be the ip or domain of the node depending on the ssl settings") + .WithValidation(FastValidators.Required); + configuration.AddProperty(x => x.Ssl) + .WithComponent() + .WithPage("Settings") + .WithDescription("This enables ssl for the http connections to the node. Only enable this if you have the cert installed on the node"); + + configuration.AddProperty(x => x.HttpPort) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This is the http(s) port used by the node to allow communication to the node from the panel"); + + configuration.AddProperty(x => x.FtpPort) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This is the ftp port users can use to access their servers filesystem via their ftp client"); + + configuration.AddCustomPage("Overview", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Allocations", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Setup", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Logs", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + } + private void ValidateFqdn(ServerNode node) { if (node.Ssl) @@ -213,7 +295,7 @@ // Is it a valid domain? if (Regex.IsMatch(node.Fqdn, "^(?!-)(?:[a-zA-Z\\d-]{0,62}[a-zA-Z\\d]\\.)+(?:[a-zA-Z]{2,})$")) return; - + // Is it a valid ip? if (Regex.IsMatch(node.Fqdn, "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")) return; @@ -221,7 +303,7 @@ throw new DisplayException("The fqdn needs to be either a domain or an ip"); } } - + public void Dispose() { UpdateTimer?.Dispose(); diff --git a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor index 38fb1df1..7b01e632 100644 --- a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor +++ b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor @@ -74,7 +74,7 @@ private void OnConfigure(FastCrudConfiguration configuration) { - configuration.ValidateCreate += network => + configuration.ValidateCreate = network => { if (!ServerRepository .Get() diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 29ffcc6d..ea125abc 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -94,7 +94,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + From 326bd096626ab712e03b78e6e7a30a52f93ad76b Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 27 Jun 2024 11:50:44 +0200 Subject: [PATCH 06/16] Upgraded server images to new mooncore forms --- Moonlight/Core/Database/DataContext.cs | 5 + Moonlight/Core/UI/Views/Admin/Api/Keys.razor | 8 +- .../Core/UI/Views/Admin/Users/Index.razor | 2 +- .../Servers/Entities/ServerDockerImage.cs | 2 +- .../Features/Servers/Entities/ServerImage.cs | 12 +- .../ImageComponents/DefaultDockerImage.razor | 53 ++++++ .../UI/ImageComponents/EditorComponent.razor | 41 +++++ .../ImageComponents/ImageDockerImages.razor | 134 +++++++++----- .../ImageParseConfigEditor.razor | 159 +++++++++++++++++ .../UI/ImageComponents/ImageVariables.razor | 145 ++++++++++++--- .../UI/NodeComponents/NodeAllocations.razor | 13 +- .../Servers/UI/Views/Admin/Images/Index.razor | 168 +++++++++++++----- .../Servers/UI/Views/Admin/Nodes/Index.razor | 2 +- .../Servers/UI/Views/Servers/Networks.razor | 2 +- Moonlight/Moonlight.csproj | 2 +- 15 files changed, 604 insertions(+), 144 deletions(-) create mode 100644 Moonlight/Features/Servers/UI/ImageComponents/DefaultDockerImage.razor create mode 100644 Moonlight/Features/Servers/UI/ImageComponents/EditorComponent.razor create mode 100644 Moonlight/Features/Servers/UI/ImageComponents/ImageParseConfigEditor.razor diff --git a/Moonlight/Core/Database/DataContext.cs b/Moonlight/Core/Database/DataContext.cs index d987ada6..8ebd658d 100644 --- a/Moonlight/Core/Database/DataContext.cs +++ b/Moonlight/Core/Database/DataContext.cs @@ -50,4 +50,9 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) ); } } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + + } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor index 3bd01e14..ce9546a9 100644 --- a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor +++ b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor @@ -15,8 +15,8 @@
+ OnConfigureCreate="OnConfigureFrom" + OnConfigureEdit="OnConfigureFrom"> - @* - - - Download and import a image from our repository or create a new one. Need help? Check out our documentation - - - - - + + + + Import egg - - + + Import - - *@ - + + + @code { - private AutoCrud Crud; + private FastCrud Crud; + private MCBCustomFileSelect ImageUpload; private MCBCustomFileSelect EggUpload; - private IEnumerable Load(Repository repository) + private IEnumerable Loader(Repository repository) { - return repository.Get(); + return repository + .Get() + .Include(x => x.DockerImages) + .Include(x => x.Variables); } - private Task ValidateDelete(ServerImage serverImage) + private void OnConfigure(FastCrudConfiguration configuration) { - if (ServerRepository.Get().Any(x => x.Image.Id == serverImage.Id)) - throw new DisplayException("A server using this image exists. Please delete the servers using this image to continue"); + configuration.ValidateDelete = image => + { + if (ServerRepository.Get().Any(x => x.Image.Id == image.Id)) + throw new DisplayException("A server using this image exists. Please delete the servers using this image to continue"); - return Task.CompletedTask; + return Task.CompletedTask; + }; + + configuration.CustomDelete = CustomDelete; } - private Task ValidateAdd(ServerImage image) + private void OnConfigureForm(FastConfiguration configuration, ServerImage image) { - // Set defaults - - image.StopCommand = "^C"; - image.StartupCommand = "echo Startup command here"; - image.AllocationsNeeded = 1; - image.InstallScript = "#! /bin/bash\necho Done"; - image.InstallShell = "/bin/bash"; - image.InstallDockerImage = "debian:latest"; - image.OnlineDetection = "Running"; - image.AllowDockerImageChange = false; - image.DefaultDockerImage = 0; - image.ParseConfiguration = "[]"; + // General + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithPage("General") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.Author) + .WithDefaultComponent() + .WithPage("General") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.DonateUrl) + .WithDefaultComponent() + .WithPage("General") + .WithDescription("Provide a url here in order to give people the ability to donate for your work"); + + configuration.AddProperty(x => x.UpdateUrl) + .WithDefaultComponent() + .WithPage("General") + .WithDescription("A http(s) url directly to a json file which will serve as an update for the image. When a update is fetched, it will just get this url and try to load it"); + + // Power + configuration.AddProperty(x => x.StartupCommand) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithPage("Start, Stop & Status") + .WithDescription("This command will be executed at the start of a server. You can use environment variables in a {} here"); + + configuration.AddProperty(x => x.OnlineDetection) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithPage("Start, Stop & Status") + .WithDescription("A regex string specifying that a server is online when the daemon finds a match in the console output matching this expression"); + + configuration.AddProperty(x => x.StopCommand) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithPage("Start, Stop & Status") + .WithDescription("A command which will be sent to the servers stdin when it should get stopped. Power signals can be achived by using ^. E.g. ^C"); + + // Parsing + configuration.AddProperty(x => x.ParseConfiguration) + .WithComponent() + .WithPage("Parsing"); + + configuration.AddCustomPage("Variables", ComponentHelper.FromType(parameters => + { + parameters.Add("Image", image); + })); - return Task.CompletedTask; + configuration.AddCustomPage("Docker Images", ComponentHelper.FromType(parameters => + { + parameters.Add("Image", image); + })); + + configuration.AddProperty(x => x.AllowDockerImageChange) + .WithComponent() + .WithPage("Miscellaneous") + .WithDescription("This toggle specifies if a user is allowed to change the docker image from the list of docker images associated to the image"); + + configuration.AddProperty(x => x.DefaultDockerImage) + .WithComponent() + .WithAdditionalOption("Image", image) + .WithPage("Miscellaneous"); + + configuration.AddProperty(x => x.AllocationsNeeded) + .WithDefaultComponent() + .WithPage("Miscellaneous") + .WithValidation(x => x > 1 ? ValidationResult.Success : new ValidationResult("This specifies the amount of allocations needed for this image in order to create a server")); + + configuration.AddProperty(x => x.InstallDockerImage) + .WithDefaultComponent() + .WithPage("Installation") + .WithName("Docker Image") + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^(?:[a-zA-Z0-9\\-\\.]+\\/)?[a-zA-Z0-9\\-]+(?:\\/[a-zA-Z0-9\\-]+)*(?::[a-zA-Z0-9_\\.-]+)?$", "You need to provide a valid docker image name")); + + configuration.AddProperty(x => x.InstallShell) + .WithDefaultComponent() + .WithPage("Installation") + .WithName("Shell") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.InstallScript) + .WithComponent() + .WithPage("Installation") + .WithName("Script") + .WithValidation(FastValidators.Required); } private Task CustomDelete(ServerImage serverImage) diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor index 59ec6be5..dd8f4ffd 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor @@ -207,7 +207,7 @@ }; } - private void OnConfigureCreate(FastConfiguration configuration) + private void OnConfigureCreate(FastConfiguration configuration, ServerNode _) { configuration.AddProperty(x => x.Name) .WithDefaultComponent() diff --git a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor index 7b01e632..3815f758 100644 --- a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor +++ b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor @@ -92,7 +92,7 @@ }; } - private void OnConfigureCreate(FastConfiguration configuration) + private void OnConfigureCreate(FastConfiguration configuration, ServerNetwork _) { configuration.AddProperty(x => x.Name) .WithDefaultComponent() diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index ea125abc..b180b0ea 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -94,7 +94,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + From 9eb9e047e1d97b9fe9169cda1c756a59e6169b39 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 27 Jun 2024 12:01:41 +0200 Subject: [PATCH 07/16] Added auto refresh to image import --- Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor index 7851407a..7b0dd8d1 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor @@ -277,7 +277,7 @@ await ToastService.Success($"Successfully imported '{image.Name}'"); await ImageUpload.RemoveSelection(); - //await Crud.Reload(); + await Crud.Refresh(); } catch (DisplayException) { @@ -313,7 +313,7 @@ await ToastService.Success($"Successfully imported '{image.Name}'"); await EggUpload.RemoveSelection(); - //await Crud.Reload(); + await Crud.Refresh(); } catch (DisplayException) { From ba907d5499b4cc4683f811c9b1fca3ebd9889cb0 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 30 Jun 2024 19:15:32 +0200 Subject: [PATCH 08/16] Upgraded servers page to new mooncore version --- Moonlight/Features/Servers/Entities/Server.cs | 6 +- .../Servers/UI/Views/Admin/Index.razor | 214 +++++++++++++----- Moonlight/Moonlight.csproj | 2 +- 3 files changed, 160 insertions(+), 62 deletions(-) diff --git a/Moonlight/Features/Servers/Entities/Server.cs b/Moonlight/Features/Servers/Entities/Server.cs index 7377987a..b25d0d94 100644 --- a/Moonlight/Features/Servers/Entities/Server.cs +++ b/Moonlight/Features/Servers/Entities/Server.cs @@ -14,14 +14,14 @@ public class Server public string? OverrideStartupCommand { get; set; } - public int Cpu { get; set; } + public int Cpu { get; set; } = 100; public int Memory { get; set; } public int Disk { get; set; } - public bool UseVirtualDisk { get; set; } + public bool UseVirtualDisk { get; set; } = false; public ServerNode Node { get; set; } public ServerNetwork? Network { get; set; } - public bool DisablePublicNetwork { get; set; } + public bool DisablePublicNetwork { get; set; } = false; public ServerAllocation MainAllocation { get; set; } public List Allocations { get; set; } = new(); diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor index 75aab3af..965fd648 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor @@ -1,33 +1,36 @@ @page "/admin/servers" +@using System.ComponentModel.DataAnnotations @using Moonlight.Features.Servers.UI.Components @using Microsoft.EntityFrameworkCore @using MoonCore.Abstractions +@using MoonCore.Blazor.Extensions +@using MoonCore.Blazor.Forms.Fast.Components +@using MoonCore.Blazor.Models.Fast @using MoonCore.Exceptions +@using Moonlight.Core.Database.Entities @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Models.Enums -@using Moonlight.Features.Servers.Models.Forms.Admin.Servers @using Moonlight.Features.Servers.Services @inject ServerService ServerService @inject Repository ServerRepository +@inject Repository AllocRepository @inject ToastService ToastService +@inject AlertService AlertService @inject ILogger Logger @attribute [RequirePermission(5000)] -@* - + + @@ -47,56 +50,17 @@ - - - Create a new server in order to manage it using this page. Need help? Check out our documentation - - -*@ + + + + Force delete + + + @code { - /* - private void OnConfigure(AutoCrudOptions options) - { - options.AddCustomItemLoader("FreeAllocations", LoadFreeAllocations); - - options.AddCustomDisplayFunction("AllocationWithIp", - allocation => allocation.IpAddress + ":" + allocation.Port); - }*/ - - private IEnumerable Load(Repository repository) - { - return repository - .Get() - .Include(x => x.Owner) - .Include(x => x.Image) - .Include(x => x.Allocations) - .Include(x => x.Node); - } - - private IEnumerable LoadFreeAllocations(Repository repository, Server? currentServer) - { - if (currentServer == null) - { - return repository - .Get() - .FromSqlRaw("SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL"); - } - else - { - return currentServer.Allocations.Concat( - repository - .Get() - .FromSqlRaw($"SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL AND ServerNodeId = {currentServer.Node.Id}") - .AsEnumerable() // => executes the sql - ); - } - } - - private async Task CustomAdd(Server form) => await ServerService.Create(form); - - private async Task CustomDelete(Server s) => await ServerService.Delete(s); + private FastCrud Crud; private async Task CustomUpdate(Server server) { @@ -154,4 +118,138 @@ return Task.CompletedTask; } + + private async Task StartForceDelete(Server server) + { + await AlertService.Confirm( + "Confirm forcefully server deletion", + "Do you really want to delete this server forcefully?", + async () => + { + await ServerService.Delete(server, safeDelete: false); + await ToastService.Success("Successfully deleted server"); + + await Crud.SetState(FastCrudState.View); + } + ); + } + + private IEnumerable Loader(Repository repository) + { + return repository + .Get() + .Include(x => x.Image) + .Include(x => x.Node) + .Include(x => x.Owner) + .Include(x => x.Allocations); + } + + private void OnConfigure(FastCrudConfiguration configuration) + { + configuration.CustomCreate = ServerService.Create; + configuration.CustomDelete = server => ServerService.Delete(server); + configuration.CustomEdit = CustomUpdate; + + configuration.ValidateEdit = ValidateUpdate; + } + + // Shared form + private void OnConfigureBase(FastConfiguration configuration, Server server) + { + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithValidation(FastValidators.Required); + + Func usernameFunc = x => x.Username; + + configuration.AddProperty(x => x.Owner) + .WithComponent>() + .WithAdditionalOption("SearchFunc", usernameFunc) + .WithAdditionalOption("DisplayFunc", usernameFunc) + .WithValidation(FastValidators.Required); + + Func imageNameFunc = x => x.Name; + + configuration.AddProperty(x => x.Image) + .WithComponent>() + .WithAdditionalOption("SearchFunc", imageNameFunc) + .WithAdditionalOption("DisplayFunc", imageNameFunc) + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.Cpu) + .WithDefaultComponent() + .WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value")) + .WithSection("Resources", "bxs-chip") + .WithDescription("The cores the server will be able to use. 100 = 1 Core"); + + configuration.AddProperty(x => x.Memory) + .WithComponent() + .WithAdditionalOption("MinimumUnit", "MB") + .WithAdditionalOption("DefaultUnit", "GB") + .WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value")) + .WithSection("Resources") + .WithDescription("The amount of memory this server will be able to use"); + + configuration.AddProperty(x => x.Disk) + .WithComponent() + .WithAdditionalOption("MinimumUnit", "MB") + .WithAdditionalOption("DefaultUnit", "GB") + .WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value")) + .WithSection("Resources") + .WithDescription("The amount of disk space this server will be able to use"); + + Func nodeNameFunc = x => x.Name; + + configuration.AddProperty(x => x.Node) + .WithComponent>() + .WithAdditionalOption("SearchFunc", nodeNameFunc) + .WithAdditionalOption("DisplayFunc", nodeNameFunc) + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.UseVirtualDisk) + .WithComponent() + .WithPage("Advanced options") + .WithDescription("Whether to use a virtual disk for storing server files. Dont use this if you want to overallocate as the virtual disks will fill out the space you allocate"); + + configuration.AddProperty(x => x.DisablePublicNetwork) + .WithComponent() + .WithPage("Network") + .WithDescription("Whether to block all incoming connections to this server from the internet"); + } + + // Specific form + private void OnConfigureCreate(FastConfiguration configuration, Server server) + { + OnConfigureBase(configuration, server); + + // Allocations + var items = AllocRepository + .Get() + .FromSqlRaw("SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL") + .AsEnumerable(); + + Func buildDisplay = x => $"{x.IpAddress}:{x.Port}"; + + configuration.AddProperty(x => x.Allocations) + .WithMultiSelect(buildDisplay, buildDisplay, items) + .WithPage("Network"); + } + + private void OnConfigureEdit(FastConfiguration configuration, Server server) + { + OnConfigureBase(configuration, server); + + // Allocations + var items = server.Allocations.Concat( + AllocRepository + .Get() + .FromSqlRaw($"SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL AND ServerNodeId = {server.Node.Id}") + .AsEnumerable()); + + Func buildDisplay = x => $"{x.IpAddress}:{x.Port}"; + + configuration.AddProperty(x => x.Allocations) + .WithMultiSelect(buildDisplay, buildDisplay, items) + .WithPage("Network"); + } } \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index b180b0ea..41cc40cc 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -94,7 +94,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + From 9602a201ce63f3e0bedef5f52e6d4598e0c72888 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 3 Jul 2024 22:38:40 +0200 Subject: [PATCH 09/16] Switched to final fast forms implementation. Changed some stuff --- Moonlight/Core/UI/Components/Auth/Login.razor | 4 +- Moonlight/Core/UI/Views/Admin/Api/Keys.razor | 6 +- .../Core/UI/Views/Admin/Sys/Settings.razor | 6 +- .../Core/UI/Views/Admin/Users/Index.razor | 33 +++-- .../ImageComponents/DefaultDockerImage.razor | 12 +- .../UI/ImageComponents/EditorComponent.razor | 19 +-- .../ImageComponents/ImageDockerImages.razor | 33 +---- .../ImageParseConfigEditor.razor | 3 +- .../UI/ImageComponents/ImageVariables.razor | 16 ++- .../UI/NodeComponents/NodeAllocations.razor | 8 +- .../Servers/UI/Views/Admin/Images/Index.razor | 35 +++--- .../Servers/UI/Views/Admin/Index.razor | 117 +++++++++--------- .../Servers/UI/Views/Admin/Nodes/Index.razor | 21 ++-- .../Servers/UI/Views/Servers/Networks.razor | 21 ++-- Moonlight/Moonlight.csproj | 7 +- Moonlight/_Imports.razor | 5 +- 16 files changed, 146 insertions(+), 200 deletions(-) diff --git a/Moonlight/Core/UI/Components/Auth/Login.razor b/Moonlight/Core/UI/Components/Auth/Login.razor index acf58eef..3402f97a 100644 --- a/Moonlight/Core/UI/Components/Auth/Login.razor +++ b/Moonlight/Core/UI/Components/Auth/Login.razor @@ -23,7 +23,7 @@
- + @if (RequiresTwoFactor) {
@@ -57,7 +57,7 @@ @* OAuth2 Providers here *@
-
+ diff --git a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor index ce9546a9..d88630ca 100644 --- a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor +++ b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor @@ -1,6 +1,6 @@ @page "/admin/api/keys" -@using MoonCore.Blazor.Models.Fast +@using MoonCore.Blazor.Models.FastForms @using MoonCore.Helpers @using Moonlight.Core.Database.Entities @using Moonlight.Core.UI.Components.Navigations @@ -66,12 +66,12 @@ }; } - private void OnConfigureFrom(FastConfiguration configuration, ApiKey _) + private void OnConfigureFrom(FastFormConfiguration configuration, ApiKey _) { configuration.AddProperty(x => x.Description) .WithDefaultComponent() .WithDescription("Write a note here for which application the api key is used for") - .WithValidation(FastValidators.Required); + .WithValidation(FastFormValidators.Required); configuration.AddProperty(x => x.ExpiresAt) .WithDefaultComponent() diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor index f4079d13..11a925dc 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor @@ -4,8 +4,6 @@ @using System.Linq.Expressions @using Moonlight.Core.UI.Components.Navigations @using System.Reflection -@using MoonCore.Blazor.Models.Fast -@using MoonCore.Helpers @using MoonCore.Services @using Moonlight.Core.Configuration @@ -140,7 +138,7 @@ else Properties = props .Where(x => !x.PropertyType.Namespace.StartsWith("Moonlight") && - DefaultComponentSelector.GetDefault(x.PropertyType) != null // Check if a component has been registered for that type + DefaultComponentRegistry.Get(x.PropertyType) != null // Check if a component has been registered for that type ) .ToArray(); } @@ -151,7 +149,7 @@ else await LazyLoader.Reload(); } - private void OnFormConfigure(FastConfiguration configuration) + private void OnFormConfigure(FastFormConfiguration configuration) { if(CurrentModel == null) // This will technically never be true because of the ui logic return; diff --git a/Moonlight/Core/UI/Views/Admin/Users/Index.razor b/Moonlight/Core/UI/Views/Admin/Users/Index.razor index 82e29603..0bb30809 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Index.razor @@ -2,14 +2,9 @@ @using System.ComponentModel.DataAnnotations @using Moonlight.Core.UI.Components.Navigations -@using MoonCore.Abstractions -@using MoonCore.Blazor.Forms.Fast.Components -@using MoonCore.Blazor.Models.Fast -@using MoonCore.Blazor.Models.Fast.Validators @using Moonlight.Core.Database.Entities @using MoonCore.Exceptions @using Moonlight.Core.Models.Abstractions -@using Moonlight.Core.Models.Forms.Users @inject AlertService AlertService @inject IAuthenticationProvider AuthenticationProvider @@ -67,43 +62,43 @@ }; } - private void OnConfigureCreate(FastConfiguration configuration, User _) + private void OnConfigureCreate(FastFormConfiguration configuration, User _) { configuration.AddProperty(x => x.Username) .WithDefaultComponent() - .WithValidation(FastValidators.Required) + .WithValidation(FastFormValidators.Required) .WithValidation(RegexValidator.Create("^[a-z][a-z0-9]*$", "Usernames can only contain lowercase characters and numbers and should not start with a number")) - .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) - .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); + .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) + .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); configuration.AddProperty(x => x.Email) .WithDefaultComponent() - .WithValidation(FastValidators.Required) + .WithValidation(FastFormValidators.Required) .WithValidation(RegexValidator.Create("^.+@.+$", "You need to enter a valid email address")); configuration.AddProperty(x => x.Password) .WithDefaultComponent() - .WithValidation(FastValidators.Required) - .WithValidation(x => x.Length >= 8 ? ValidationResult.Success : new ValidationResult("The password must be at least 8 characters long")) - .WithValidation(x => x.Length <= 256 ? ValidationResult.Success : new ValidationResult("The password must not be longer than 256 characters")); + .WithValidation(FastFormValidators.Required) + .WithValidation(x => x.Length >= 8 ? ValidationResult.Success : new ValidationResult("The password must be at least 8 characters long")) + .WithValidation(x => x.Length <= 256 ? ValidationResult.Success : new ValidationResult("The password must not be longer than 256 characters")); } - private void OnConfigureEdit(FastConfiguration configuration, User currentUser) + private void OnConfigureEdit(FastFormConfiguration configuration, User currentUser) { configuration.AddProperty(x => x.Username) .WithDefaultComponent() - .WithValidation(FastValidators.Required) + .WithValidation(FastFormValidators.Required) .WithValidation(RegexValidator.Create("^[a-z][a-z0-9]*$", "Usernames can only contain lowercase characters and numbers and should not start with a number")) - .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) - .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); + .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new ValidationResult("The username is too short")) + .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new ValidationResult("The username cannot be longer than 20 characters")); configuration.AddProperty(x => x.Email) .WithDefaultComponent() - .WithValidation(FastValidators.Required) + .WithValidation(FastFormValidators.Required) .WithValidation(RegexValidator.Create("^.+@.+$", "You need to enter a valid email address")); configuration.AddProperty(x => x.Totp) - .WithComponent() + .WithComponent() .WithName("Two factor authentication") .WithDescription("This toggles the use of the two factor authentication"); } diff --git a/Moonlight/Features/Servers/UI/ImageComponents/DefaultDockerImage.razor b/Moonlight/Features/Servers/UI/ImageComponents/DefaultDockerImage.razor index 27d9c69f..0269c82b 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/DefaultDockerImage.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/DefaultDockerImage.razor @@ -1,7 +1,7 @@ +@using Microsoft.CSharp.RuntimeBinder @using Moonlight.Features.Servers.Entities -@using MoonCore.Blazor.Forms.Fast.Components -@inherits BaseFastFormComponent +@inherits FastFormBaseComponent
@@ -14,6 +14,8 @@ @code { + [Parameter] public ServerImage Image { get; set; } + private ServerDockerImage? SelectedDockerImage { get @@ -42,11 +44,7 @@ protected override void OnInitialized() { - ArgumentNullException.ThrowIfNull(AdditionalOptions); - - var image = AdditionalOptions.Get("Image"); - - SortedImages = image.DockerImages + SortedImages = Image.DockerImages .OrderBy(x => x.Id) .ToList(); } diff --git a/Moonlight/Features/Servers/UI/ImageComponents/EditorComponent.razor b/Moonlight/Features/Servers/UI/ImageComponents/EditorComponent.razor index 419caa38..c7cfe9c0 100644 --- a/Moonlight/Features/Servers/UI/ImageComponents/EditorComponent.razor +++ b/Moonlight/Features/Servers/UI/ImageComponents/EditorComponent.razor @@ -1,7 +1,6 @@ -@using MoonCore.Blazor.Forms.Fast.Components @using Moonlight.Features.FileManager.UI.Components -@inherits BaseFastFormComponent +@inherits FastFormBaseComponent
- +
- +
- +
} else @@ -170,7 +171,10 @@ else var correctCode = totp.ComputeTotp(); if (CodeForm.Code != correctCode) - throw new DisplayException("Invalid code entered. Please try again"); + { + await AlertService.Danger("Invalid code entered. Please try again"); + return; + } // Enable two factor auth for user await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, Key); diff --git a/Moonlight/Core/UI/Views/Account/Index.razor b/Moonlight/Core/UI/Views/Account/Index.razor index 23591e26..ed218f6c 100644 --- a/Moonlight/Core/UI/Views/Account/Index.razor +++ b/Moonlight/Core/UI/Views/Account/Index.razor @@ -1,8 +1,10 @@ @page "/account" +@using System.ComponentModel.DataAnnotations @using Moonlight.Core.Services @using Moonlight.Core.Models.Forms @using Mappy.Net +@using Moonlight.Core.Database.Entities @using Moonlight.Core.Models.Abstractions @using Moonlight.Core.UI.Components.Navigations @@ -22,53 +24,73 @@ - - -
-
-
-
-
-
- image -
-
-
- To change your profile picture go to Gravatar and - register with the same email address you are using here +
+
+
+
+
+
+ image
+
+ To change your profile picture go to Gravatar and + register with the same email address you are using here +
-
-
- -
- -
- -
+
+
+
+
+ +
+
- +
@code { - private UpdateAccountForm Form = new(); + private User User; + private FastForm Form; + + protected override void OnInitialized() + { + // Create a copy of the user + User = Mapper.Map(IdentityService.CurrentUser); + } - private Task Load(LazyLoader _) + private void OnConfigure(FastFormConfiguration configuration) { - Form = Mapper.Map(IdentityService.CurrentUser); + configuration.AddProperty(x => x.Username) + .WithComponent(component => + { + component.ColumnsMd = 12; + }) + .WithValidation(FastFormValidators.Required) + .WithValidation(RegexValidator.Create("^[a-z][a-z0-9]*$", "Usernames can only contain lowercase characters and numbers and should not start with a number")) + .WithValidation(x => x.Length >= 6 ? ValidationResult.Success : new("The username is too short")) + .WithValidation(x => x.Length <= 20 ? ValidationResult.Success : new("The username cannot be longer than 20 characters")); - return Task.CompletedTask; + configuration.AddProperty(x => x.Email) + .WithComponent(component => + { + component.ColumnsMd = 12; + component.Type = "email"; + }) + .WithValidation(FastFormValidators.Required) + .WithValidation(RegexValidator.Create("^.+@.+$", "You need to provide a valid email address")); } - private async Task Update() + private async Task SaveChanges() { - await AuthenticationProvider.ChangeDetails(IdentityService.CurrentUser, Form.Email, Form.Username); + if(!await Form.Submit()) + return; + + await AuthenticationProvider.ChangeDetails(IdentityService.CurrentUser, User.Email, User.Username); await ToastService.Success("Successfully updated details"); // This will trigger a re-render as well as an update of the model diff --git a/Moonlight/Core/UI/Views/Account/Security.razor b/Moonlight/Core/UI/Views/Account/Security.razor index d6dd5845..529fd190 100644 --- a/Moonlight/Core/UI/Views/Account/Security.razor +++ b/Moonlight/Core/UI/Views/Account/Security.razor @@ -1,5 +1,6 @@ @page "/account/security" +@using System.ComponentModel.DataAnnotations @using Moonlight.Core.Services @using Moonlight.Core.UI.Components.Navigations @using Moonlight.Core.Models.Forms @@ -23,74 +24,97 @@ - -
-
-
- -
-
- -
-
- -
+
+
+
+
+ +
+
-
-
-
- -
+
+
+
+
+
-
-
- -
-

Cookies

-
- - -
-
- +
+
+
+

Cookies

+

+ This specifies if you would like to personalize your experience with optional cookies. +

+
+ @if (CookieConsent) + { + + } + else + { + + }
-
- +
@code { - private readonly ChangePasswordForm PasswordForm = new(); - - private ChangeCookiesForm CookiesForm = new(); + private ChangePasswordForm PasswordModel = new(); + private FastForm PasswordForm; - private async Task Load(LazyLoader lazyLoader) + private bool CookieConsent; + + protected override async Task OnInitializedAsync() { - CookiesForm.UseOptionalCookies = await IdentityService.HasFlag("CookieConsent"); + CookieConsent = await IdentityService.HasFlag("CookieConsent"); } - private async Task OnValidSubmitPassword() + private async Task SetCookieConsent(bool flag) { - if (PasswordForm.Password != PasswordForm.RepeatedPassword) - throw new DisplayException("The passwords do not match"); - - await AuthenticationProvider.ChangePassword(IdentityService.CurrentUser, PasswordForm.Password); + await IdentityService.SetFlag("CookieConsent", flag); - await ToastService.Success("Successfully changed password"); - await IdentityService.Authenticate(true); + await ToastService.Success("Successfully changed cookie preferences"); + await InvokeAsync(StateHasChanged); } - private async Task OnValidSubmitCookie() + private void OnConfigurePasswordForm(FastFormConfiguration configuration) { - await IdentityService.SetFlag("CookieConsent", CookiesForm.UseOptionalCookies); + configuration.AddProperty(x => x.Password) + .WithComponent(component => + { + component.ColumnsMd = 6; + component.Type = "password"; + }) + .WithValidation(FastFormValidators.Required) + .WithValidation(x => x.Length >= 8 ? ValidationResult.Success : new("The password must be at least 8 characters long")) + .WithValidation(x => x.Length <= 256 ? ValidationResult.Success : new("The password must not be longer than 256 characters")); - await InvokeAsync(StateHasChanged); + configuration.AddProperty(x => x.RepeatedPassword) + .WithComponent(component => + { + component.ColumnsMd = 6; + component.Type = "password"; + }); + } + + private async Task ChangePassword() + { + if(!await PasswordForm.Submit()) + return; + + if (PasswordModel.Password != PasswordModel.RepeatedPassword) + throw new DisplayException("The passwords do not match"); + + await AuthenticationProvider.ChangePassword(IdentityService.CurrentUser, PasswordModel.Password); + + await ToastService.Success("Successfully changed password"); + await IdentityService.Authenticate(true); } } \ No newline at end of file diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor index f6f1f1f0..cdca0ee5 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Index.razor @@ -217,6 +217,7 @@ component.SearchFunc = x => $"{x.IpAddress}:{x.Port}"; component.DisplayFunc = x => $"{x.IpAddress}:{x.Port}"; component.ItemsCallback = () =>GetAllocation(server); + component.ColumnsMd = 6; }) .WithPage("Network"); } From dcef6e45005c177923fc2bcbbd313e47a1533321 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 6 Jul 2024 11:29:45 +0200 Subject: [PATCH 15/16] Switched to new mooncore authentication service --- .../Core/Configuration/CoreConfiguration.cs | 10 ++ Moonlight/Core/CoreFeature.cs | 5 +- .../Extensions/IdentityServiceExtensions.cs | 54 +++++++ .../Helpers/AuthenticationStateProvider.cs | 50 ++++++ .../Core/Http/Controllers/AvatarController.cs | 14 +- .../Core/Models/Forms/UpdateAccountForm.cs | 6 - Moonlight/Core/Models/Session.cs | 2 +- Moonlight/Core/Services/IdentityService.cs | 147 ------------------ Moonlight/Core/UI/Components/Auth/Login.razor | 8 +- .../Core/UI/Components/Auth/Register.razor | 8 +- .../UI/Components/Auth/TwoFactorWizard.razor | 12 +- .../Cards/TimeBasedGreetingMessages.razor | 5 +- .../UI/Components/Partials/AppHeader.razor | 16 +- .../UI/Components/Partials/AppSidebar.razor | 4 +- .../Partials/CookieConsentBanner.razor | 62 ++++---- .../Partials/PermissionChecker.razor | 2 +- .../Partials/SoftErrorHandler.razor | 4 +- Moonlight/Core/UI/Layouts/MainLayout.razor | 8 +- Moonlight/Core/UI/Views/Account/Api.razor | 2 +- Moonlight/Core/UI/Views/Account/Index.razor | 16 +- .../Core/UI/Views/Account/Security.razor | 6 +- Moonlight/Core/UI/Views/Admin/Index.razor | 4 +- .../Core/UI/Views/Admin/Users/Sessions.razor | 8 +- Moonlight/Core/UI/Views/Index.razor | 2 +- .../Cards/UserDashboardServerCount.razor | 4 +- .../Servers/UI/Layouts/UserLayout.razor | 5 +- .../Servers/UI/NodeComponents/NodeSetup.razor | 5 +- .../Servers/UI/Views/Servers/Index.razor | 2 +- .../Servers/UI/Views/Servers/Networks.razor | 8 +- Moonlight/_Imports.razor | 4 + 30 files changed, 223 insertions(+), 260 deletions(-) create mode 100644 Moonlight/Core/Extensions/IdentityServiceExtensions.cs create mode 100644 Moonlight/Core/Helpers/AuthenticationStateProvider.cs delete mode 100644 Moonlight/Core/Services/IdentityService.cs diff --git a/Moonlight/Core/Configuration/CoreConfiguration.cs b/Moonlight/Core/Configuration/CoreConfiguration.cs index db0e59fa..f89e839a 100644 --- a/Moonlight/Core/Configuration/CoreConfiguration.cs +++ b/Moonlight/Core/Configuration/CoreConfiguration.cs @@ -90,6 +90,16 @@ public class AuthenticationData [JsonProperty("DenyRegister")] [Description("This disables the register function. No user will be able to sign up anymore. Its recommended to enable this for private instances")] public bool DenyRegister { get; set; } = false; + + [JsonProperty("EnablePeriodicReAuth")] + [Description( + "If this option is enabled, every session will reauthenticate perdiodicly to track state changes in real time without the user refreshing the page")] + public bool EnablePeriodicReAuth { get; set; } = true; + + [JsonProperty("PeriodicReAuthDelay")] + [Description( + "This option specifies how long the intervals are between reauthentications. The value is specified in minutes")] + public int PeriodicReAuthDelay { get; set; } = 5; } public class SecurityData diff --git a/Moonlight/Core/CoreFeature.cs b/Moonlight/Core/CoreFeature.cs index 6743a11f..3a614ef6 100644 --- a/Moonlight/Core/CoreFeature.cs +++ b/Moonlight/Core/CoreFeature.cs @@ -26,7 +26,7 @@ using Moonlight.Core.Implementations.ApiDefinition; using Moonlight.Core.Implementations.UserDashboard; using Swashbuckle.AspNetCore.SwaggerGen; -using IdentityService = Moonlight.Core.Services.IdentityService; +using AuthenticationStateProvider = Moonlight.Core.Helpers.AuthenticationStateProvider; namespace Moonlight.Core; @@ -68,6 +68,9 @@ public override Task OnPreInitialized(PreInitContext context) builder.Services.AddMoonCore(configuration => { configuration.Identity.Token = config.Security.Token; + configuration.Identity.PeriodicReAuthDelay = TimeSpan.FromMinutes(config.Authentication.PeriodicReAuthDelay); + configuration.Identity.EnablePeriodicReAuth = config.Authentication.EnablePeriodicReAuth; + configuration.Identity.Provider = new AuthenticationStateProvider(); }); builder.Services.AddMoonCoreBlazor(); diff --git a/Moonlight/Core/Extensions/IdentityServiceExtensions.cs b/Moonlight/Core/Extensions/IdentityServiceExtensions.cs new file mode 100644 index 00000000..83142cae --- /dev/null +++ b/Moonlight/Core/Extensions/IdentityServiceExtensions.cs @@ -0,0 +1,54 @@ +using MoonCore.Abstractions; +using MoonCore.Services; +using Moonlight.Core.Database.Entities; + +namespace Moonlight.Core.Extensions; + +public static class IdentityServiceExtensions +{ + public static User GetUser(this IdentityService identityService) + { + return identityService.Storage.Get(); + } + + public static Task HasFlag(this IdentityService identityService, string flag) + { + if (!identityService.IsAuthenticated) + return Task.FromResult(false); + + var result = identityService.GetUser().Flags.Split(";").Contains(flag); + return Task.FromResult(result); + } + + public static Task SetFlag(this IdentityService identityService, string flag, bool toggle) + { + if (!identityService.IsAuthenticated) + return Task.CompletedTask; + + var user = identityService.GetUser(); + + // Rebuild flags + var flags = user.Flags.Split(";").ToList(); + + if (toggle) + { + if(!flags.Contains(flag)) + flags.Add(flag); + } + else + { + if (flags.Contains(flag)) + flags.Remove(flag); + } + + user.Flags = string.Join(';', flags); + + // Save changes + var serviceProvider = identityService.Storage.Get(); + var userRepo = serviceProvider.GetRequiredService>(); + + userRepo.Update(user); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight/Core/Helpers/AuthenticationStateProvider.cs b/Moonlight/Core/Helpers/AuthenticationStateProvider.cs new file mode 100644 index 00000000..4b830999 --- /dev/null +++ b/Moonlight/Core/Helpers/AuthenticationStateProvider.cs @@ -0,0 +1,50 @@ +using MoonCore.Abstractions; +using MoonCore.Helpers; +using Moonlight.Core.Database.Entities; + +namespace Moonlight.Core.Helpers; + +public class AuthenticationStateProvider : MoonCore.Abstractions.AuthenticationStateProvider +{ + public override Task IsValidIdentifier(IServiceProvider provider, string identifier) + { + if(!int.TryParse(identifier, out int searchId)) + return Task.FromResult(false); + + var userRepo = provider.GetRequiredService>(); + var result = userRepo.Get().Any(x => x.Id == searchId); + + return Task.FromResult(result); + } + + public override Task LoadFromIdentifier(IServiceProvider provider, string identifier, DynamicStorage storage) + { + if(!int.TryParse(identifier, out int searchId)) + return Task.CompletedTask; + + var userRepo = provider.GetRequiredService>(); + var user = userRepo.Get().FirstOrDefault(x => x.Id == searchId); + + if(user == null) + return Task.CompletedTask; + + storage.Set("User", user); + storage.Set("ServiceProvider", provider); + + return Task.CompletedTask; + } + + public override Task DetermineTokenValidTimestamp(IServiceProvider provider, string identifier) + { + if(!int.TryParse(identifier, out int searchId)) + return Task.FromResult(DateTime.MaxValue); + + var userRepo = provider.GetRequiredService>(); + var user = userRepo.Get().FirstOrDefault(x => x.Id == searchId); + + if(user == null) + return Task.FromResult(DateTime.MaxValue); + + return Task.FromResult(user.TokenValidTimestamp); + } +} \ No newline at end of file diff --git a/Moonlight/Core/Http/Controllers/AvatarController.cs b/Moonlight/Core/Http/Controllers/AvatarController.cs index 530199f2..696463cf 100644 --- a/Moonlight/Core/Http/Controllers/AvatarController.cs +++ b/Moonlight/Core/Http/Controllers/AvatarController.cs @@ -2,13 +2,11 @@ using System.Text; using Microsoft.AspNetCore.Mvc; using MoonCore.Abstractions; -using MoonCore.Helpers; using MoonCore.Services; using Moonlight.Core.Attributes; using Moonlight.Core.Configuration; using Moonlight.Core.Database.Entities; -using Moonlight.Core.Services; -using IdentityService = Moonlight.Core.Services.IdentityService; +using Moonlight.Core.Extensions; namespace Moonlight.Core.Http.Controllers; @@ -42,10 +40,10 @@ public async Task Get() var token = Request.Cookies["token"]; await IdentityService.Authenticate(token!); - if (!IdentityService.IsLoggedIn) + if (!IdentityService.IsAuthenticated) return StatusCode(403); - return File(await GetAvatar(IdentityService.CurrentUser), "image/jpeg"); + return File(await GetAvatar(IdentityService.GetUser()), "image/jpeg"); } [HttpGet("{id:int}")] @@ -57,12 +55,12 @@ public async Task Get(int id) var token = Request.Cookies["token"]; await IdentityService.Authenticate(token!); - if (!IdentityService.IsLoggedIn) + if (!IdentityService.IsAuthenticated) return StatusCode(403); if (ConfigService.Get().Security.EnforceAvatarPrivacy && // Do we need to enforce privacy? - id != IdentityService.CurrentUser.Id && // is the user not viewing his own image? - IdentityService.CurrentUser.Permissions < 1000) // and not an admin? + id != IdentityService.GetUser().Id && // is the user not viewing his own image? + IdentityService.GetUser().Permissions < 1000) // and not an admin? { return StatusCode(403); } diff --git a/Moonlight/Core/Models/Forms/UpdateAccountForm.cs b/Moonlight/Core/Models/Forms/UpdateAccountForm.cs index f5fdafc3..f3b6b54c 100644 --- a/Moonlight/Core/Models/Forms/UpdateAccountForm.cs +++ b/Moonlight/Core/Models/Forms/UpdateAccountForm.cs @@ -4,12 +4,6 @@ namespace Moonlight.Core.Models.Forms; public class UpdateAccountForm { - [Required(ErrorMessage = "You need to provide an username")] - [MinLength(6, ErrorMessage = "The username is too short")] - [MaxLength(20, ErrorMessage = "The username cannot be longer than 20 characters")] public string Username { get; set; } - - [Required(ErrorMessage = "You need to provide an email address")] - [EmailAddress(ErrorMessage = "You need to enter a valid email address")] public string Email { get; set; } = ""; } \ No newline at end of file diff --git a/Moonlight/Core/Models/Session.cs b/Moonlight/Core/Models/Session.cs index ba2670b0..0474e7f7 100644 --- a/Moonlight/Core/Models/Session.cs +++ b/Moonlight/Core/Models/Session.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Components; using MoonCore.Blazor.Services; -using Moonlight.Core.Services; +using MoonCore.Services; namespace Moonlight.Core.Models; diff --git a/Moonlight/Core/Services/IdentityService.cs b/Moonlight/Core/Services/IdentityService.cs deleted file mode 100644 index e6ea8cc2..00000000 --- a/Moonlight/Core/Services/IdentityService.cs +++ /dev/null @@ -1,147 +0,0 @@ -using MoonCore.Abstractions; -using MoonCore.Attributes; -using MoonCore.Exceptions; -using MoonCore.Helpers; -using MoonCore.Services; -using Moonlight.Core.Configuration; -using Moonlight.Core.Database.Entities; -using Moonlight.Core.Models.Enums; - -namespace Moonlight.Core.Services; - -[Scoped] -public class IdentityService : IDisposable -{ - public User? CurrentUserNullable { get; private set; } - public User CurrentUser => CurrentUserNullable!; - public bool IsLoggedIn => CurrentUserNullable != null; - public SmartEventHandler OnAuthenticationStateChanged { get; set; } - - private string Token = ""; - - private readonly JwtService JwtService; - private readonly ConfigService ConfigService; - private readonly Repository UserRepository; - - public IdentityService( - JwtService jwtService, - ConfigService configService, - Repository userRepository, - ILogger eventHandlerLogger) - { - JwtService = jwtService; - ConfigService = configService; - UserRepository = userRepository; - - OnAuthenticationStateChanged = new(eventHandlerLogger); - } - - public async Task Authenticate(User user) - { - var duration = TimeSpan.FromDays(ConfigService.Get().Authentication.TokenDuration); - - var token = await JwtService.Create(data => - { - data.Add("UserId", user.Id.ToString()); - data.Add("IssuedAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()); - }, CoreJwtType.Login, duration); - - await Authenticate(token); - - return token; - } - - public async Task Authenticate(string token) - { - Token = token; - - await Authenticate(); - } - - public async Task Authenticate(bool forceStateChange = false) // Can be used for authentication of the token as well - { - var lastUserId = CurrentUserNullable?.Id ?? -1; - - await ProcessToken(); - - var currentUserId = CurrentUserNullable?.Id ?? -1; - - if (lastUserId != currentUserId || forceStateChange) - await OnAuthenticationStateChanged.Invoke(); - } - - private async Task ProcessToken() - { - CurrentUserNullable = null; - - // Check jwt signature - if (!await JwtService.Validate(Token, CoreJwtType.Login)) - return; - - var data = await JwtService.Decode(Token); - - // Check for missing content - if(!data.ContainsKey("UserId") || !data.ContainsKey("IssuedAt")) - return; - - // Load user - var userId = int.Parse(data["UserId"]); - - var user = UserRepository - .Get() - .FirstOrDefault(x => x.Id == userId); - - // Check if user was found - if(user == null) - return; - - // Check token valid time - var issuedAt = long.Parse(data["IssuedAt"]); - var issuedAtDateTime = DateTimeOffset.FromUnixTimeSeconds(issuedAt).DateTime; - - // If the valid time is newer then when the token was issued, the token is not longer valid - if (user.TokenValidTimestamp > issuedAtDateTime) - return; - - CurrentUserNullable = user; - } - - public Task HasFlag(string flag) - { - if (!IsLoggedIn) - return Task.FromResult(false); - - var flags = CurrentUser.Flags.Split(";"); - - return Task.FromResult(flags.Contains(flag)); - } - - public Task SetFlag(string flag, bool toggle) - { - if (!IsLoggedIn) - throw new DisplayException("Unable to set flag while not logged in"); - - var flags = CurrentUser.Flags.Split(";").ToList(); - - if (toggle) - { - if(!flags.Contains(flag)) - flags.Add(flag); - } - else - { - if (flags.Contains(flag)) - flags.Remove(flag); - } - - CurrentUser.Flags = string.Join(';', flags); - UserRepository.Update(CurrentUser); - - return Task.CompletedTask; - } - - public async void Dispose() - { - await OnAuthenticationStateChanged.ClearSubscribers(); - } -} \ No newline at end of file diff --git a/Moonlight/Core/UI/Components/Auth/Login.razor b/Moonlight/Core/UI/Components/Auth/Login.razor index 3402f97a..87587c40 100644 --- a/Moonlight/Core/UI/Components/Auth/Login.razor +++ b/Moonlight/Core/UI/Components/Auth/Login.razor @@ -3,13 +3,14 @@ @using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Forms -@using Moonlight.Core.Services @using MoonCore.Exceptions +@using Moonlight.Core.Configuration @inject IAuthenticationProvider AuthenticationProvider @inject IdentityService IdentityService @inject CookieService CookieService @inject NavigationManager Navigation +@inject ConfigService ConfigService
@@ -89,7 +90,10 @@ throw new DisplayException("A user with these credential combination was not found"); // Generate token and authenticate - var token = await IdentityService.Authenticate(user); + var token = await IdentityService.Login( + user.Id.ToString(), + TimeSpan.FromHours(ConfigService.Get().Authentication.TokenDuration) + ); // Save token for later use await CookieService.SetValue("token", token, 30); //TODO: Add days to config option diff --git a/Moonlight/Core/UI/Components/Auth/Register.razor b/Moonlight/Core/UI/Components/Auth/Register.razor index d7cb984c..849b170f 100644 --- a/Moonlight/Core/UI/Components/Auth/Register.razor +++ b/Moonlight/Core/UI/Components/Auth/Register.razor @@ -4,11 +4,8 @@ @using Moonlight.Core.Models.Abstractions @using Moonlight.Core.Models.Forms -@using Moonlight.Core.Services @using MoonCore.Exceptions -@using MoonCore.Services @using Moonlight.Core.Configuration -@using IdentityService = Moonlight.Core.Services.IdentityService @inject IAuthenticationProvider AuthenticationProvider @inject IdentityService IdentityService @@ -96,7 +93,10 @@ throw new DisplayException("Unable to create account"); // Generate token and authenticate - var token = await IdentityService.Authenticate(user); + var token = await IdentityService.Login( + user.Id.ToString(), + TimeSpan.FromHours(ConfigService.Get().Authentication.TokenDuration) + ); // Save token for later use await CookieService.SetValue("token", token, 30); // TODO: config diff --git a/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor b/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor index 4d4dc37a..1a9cda51 100644 --- a/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor +++ b/Moonlight/Core/UI/Components/Auth/TwoFactorWizard.razor @@ -9,7 +9,7 @@ @inject AlertService AlertService @inject IAuthenticationProvider AuthenticationProvider -@if (IdentityService.CurrentUser.Totp) +@if (IdentityService.GetUser().Totp) {
@@ -43,7 +43,7 @@ else var qrCodeData = qrGenerator.CreateQrCode ( - $"otpauth://totp/{Uri.EscapeDataString(IdentityService.CurrentUser.Email)}?secret={Key}&issuer={Uri.EscapeDataString("Moonlight")}", + $"otpauth://totp/{Uri.EscapeDataString(IdentityService.GetUser().Email)}?secret={Key}&issuer={Uri.EscapeDataString("Moonlight")}", QRCodeGenerator.ECCLevel.Q ); @@ -142,8 +142,8 @@ else private async Task Disable(ConfirmButton _) { - await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, ""); - await IdentityService.Authenticate(true); + await AuthenticationProvider.SetTwoFactorSecret(IdentityService.GetUser(), ""); + await IdentityService.Authenticate(); HasStarted = false; HasCompletedAppLinks = false; @@ -177,8 +177,8 @@ else } // Enable two factor auth for user - await AuthenticationProvider.SetTwoFactorSecret(IdentityService.CurrentUser, Key); - await IdentityService.Authenticate(true); + await AuthenticationProvider.SetTwoFactorSecret(IdentityService.GetUser(), Key); + await IdentityService.Authenticate(); HasStarted = false; HasCompletedAppLinks = false; diff --git a/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor b/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor index 5da4737f..c645228f 100644 --- a/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor +++ b/Moonlight/Core/UI/Components/Cards/TimeBasedGreetingMessages.razor @@ -1,7 +1,6 @@ @using MoonCore.Services @using Moonlight.Core.Configuration -@using Moonlight.Core.Services -@using IdentityService = Moonlight.Core.Services.IdentityService +@using Moonlight.Core.Database.Entities @inject IdentityService IdentityService @inject ConfigService ConfigService @@ -13,7 +12,7 @@ var greeting = GetGreetingMessage(); } @greeting.Item1 - @IdentityService.CurrentUser.Username + @IdentityService.GetUser().Username @greeting.Item2
diff --git a/Moonlight/Core/UI/Components/Partials/AppHeader.razor b/Moonlight/Core/UI/Components/Partials/AppHeader.razor index 9fadd38f..cf2d4e2f 100644 --- a/Moonlight/Core/UI/Components/Partials/AppHeader.razor +++ b/Moonlight/Core/UI/Components/Partials/AppHeader.razor @@ -1,6 +1,4 @@ -@using Moonlight.Core.Services - -@inject IdentityService IdentityService +@inject IdentityService IdentityService @inject CookieService CookieService
@@ -24,13 +22,13 @@
- @if (IdentityService.IsLoggedIn) + @if (IdentityService.IsAuthenticated) {
@@ -80,7 +78,7 @@ protected override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) - IdentityService.OnAuthenticationStateChanged += OnAuthenticationStateChanged; + IdentityService.OnStateChanged += OnAuthenticationStateChanged; return Task.CompletedTask; } @@ -96,6 +94,6 @@ await CookieService.SetValue("token", "", 30); // Reset token in identity service - await IdentityService.Authenticate(""); + await IdentityService.Logout(); } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Components/Partials/AppSidebar.razor b/Moonlight/Core/UI/Components/Partials/AppSidebar.razor index ed251ff0..9341ed66 100644 --- a/Moonlight/Core/UI/Components/Partials/AppSidebar.razor +++ b/Moonlight/Core/UI/Components/Partials/AppSidebar.razor @@ -56,13 +56,13 @@ } } - @if (IdentityService.IsLoggedIn && IdentityService.CurrentUser.Permissions > 0) + @if (IdentityService.IsAuthenticated && IdentityService.GetUser().Permissions > 0) { - @foreach (var sidebarItem in FeatureService.UiContext.SidebarItems.Where(x => x.IsAdmin).OrderBy(x => x.Index).ToArray()) + foreach (var sidebarItem in FeatureService.UiContext.SidebarItems.Where(x => x.IsAdmin).OrderBy(x => x.Index).ToArray()) { if (IsMatch(sidebarItem)) { diff --git a/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor b/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor index 0989c9c8..c4885f78 100644 --- a/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor +++ b/Moonlight/Core/UI/Components/Partials/CookieConsentBanner.razor @@ -1,8 +1,4 @@ -@using ApexCharts -@using MoonCore.Services -@using Moonlight.Core.Configuration -@using Moonlight.Core.Services -@using IdentityService = Moonlight.Core.Services.IdentityService +@using Moonlight.Core.Configuration @inject ConfigService ConfigService @inject IdentityService IdentityService @@ -13,17 +9,17 @@
-

@ConfigService.Get().Customisation.CookieConsentBanner.BannerTitle

+

@BannerTitle

- @ConfigService.Get().Customisation.CookieConsentBanner.BannerText + @BannerText

- - @ConfigService.Get().Customisation.CookieConsentBanner.ConsentText - - - @ConfigService.Get().Customisation.CookieConsentBanner.DeclineText - + + @ConsentText + + + @DeclineText +
@@ -31,16 +27,31 @@
} -@code { - +@code +{ private bool ShowBanner; + private string BannerTitle; + private string BannerText; + private string ConsentText; + private string DeclineText; + + protected override void OnInitialized() + { + var config = ConfigService.Get().Customisation.CookieConsentBanner; + + BannerTitle = config.BannerTitle; + BannerText = config.BannerText; + ConsentText = config.ConsentText; + DeclineText = config.DeclineText; + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var userWasAsked = await IdentityService.HasFlag("CookieAsked"); - + if (ConfigService.Get().Customisation.CookieConsentBanner.Enabled && !userWasAsked) ShowBanner = true; @@ -48,25 +59,16 @@ } } - private async Task Consent() + private async Task SetAnswer(bool answer) { - if (!IdentityService.IsLoggedIn) + if (!IdentityService.IsAuthenticated) return; - - await IdentityService.SetFlag("CookieAsked", true); - await IdentityService.SetFlag("CookieConsent", true); - await InvokeAsync(StateHasChanged); - } + await IdentityService.SetFlag("CookieAsked", true); - private async Task Decline() - { - if (!IdentityService.IsLoggedIn) - return; + if (answer) + await IdentityService.SetFlag("CookieConsent", true); - await IdentityService.SetFlag("CookieAsked", true); - await InvokeAsync(StateHasChanged); } - } \ No newline at end of file diff --git a/Moonlight/Core/UI/Components/Partials/PermissionChecker.razor b/Moonlight/Core/UI/Components/Partials/PermissionChecker.razor index ad21c235..ef404acf 100644 --- a/Moonlight/Core/UI/Components/Partials/PermissionChecker.razor +++ b/Moonlight/Core/UI/Components/Partials/PermissionChecker.razor @@ -52,7 +52,7 @@ else if(permissionRequired == null) continue; - if (IdentityService.CurrentUser.Permissions >= permissionRequired.Level) + if (IdentityService.GetUser().Permissions >= permissionRequired.Level) Allowed = true; } diff --git a/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor b/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor index 27858698..ff596554 100644 --- a/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor +++ b/Moonlight/Core/UI/Components/Partials/SoftErrorHandler.razor @@ -10,7 +10,7 @@ @if (Crashed || Exception != null) { - if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" || (IdentityService.IsLoggedIn && IdentityService.CurrentUser.Permissions >= 9000)) + if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" || (IdentityService.IsAuthenticated && IdentityService.GetUser().Permissions >= 9000)) { if (Exception != null) { @@ -75,7 +75,7 @@ else Exception = exception; Crashed = true; - var username = IdentityService.IsLoggedIn ? IdentityService.CurrentUser.Username : "Guest"; + var username = IdentityService.IsAuthenticated ? IdentityService.GetUser().Username : "Guest"; Logger.LogWarning("A crash occured in the view of '{username}': {exception}", username, exception); } diff --git a/Moonlight/Core/UI/Layouts/MainLayout.razor b/Moonlight/Core/UI/Layouts/MainLayout.razor index ab8dceb0..5ad40a44 100644 --- a/Moonlight/Core/UI/Layouts/MainLayout.razor +++ b/Moonlight/Core/UI/Layouts/MainLayout.razor @@ -1,9 +1,7 @@ @using Moonlight.Core.Services @using Moonlight.Core.UI.Components.Auth -@using MoonCore.Services @using Moonlight.Core.Configuration @using Moonlight.Core.Events -@using IdentityService = Moonlight.Core.Services.IdentityService @inherits LayoutComponentBase @@ -27,7 +25,7 @@
- @if (IdentityService.IsLoggedIn) + @if (IdentityService.IsAuthenticated) { } @@ -54,7 +52,7 @@ { if (IsInitialized) { - if (IdentityService.IsLoggedIn) + if (IdentityService.IsAuthenticated) { @Body @@ -115,7 +113,7 @@ // Base init await lazyLoader.SetText("Initializing"); - IdentityService.OnAuthenticationStateChanged += OnAuthenticationStateChanged; + IdentityService.OnStateChanged += OnAuthenticationStateChanged; CoreEvents.OnMoonlightRestart += async () => { diff --git a/Moonlight/Core/UI/Views/Account/Api.razor b/Moonlight/Core/UI/Views/Account/Api.razor index fb21b19d..cc5bbc81 100644 --- a/Moonlight/Core/UI/Views/Account/Api.razor +++ b/Moonlight/Core/UI/Views/Account/Api.razor @@ -10,7 +10,7 @@ @* the @@@ looks weird, ik that, it will result in @username *@ - @@@IdentityService.CurrentUser.Username + @@@IdentityService.GetUser().Username
diff --git a/Moonlight/Core/UI/Views/Account/Index.razor b/Moonlight/Core/UI/Views/Account/Index.razor index ed218f6c..73b39638 100644 --- a/Moonlight/Core/UI/Views/Account/Index.razor +++ b/Moonlight/Core/UI/Views/Account/Index.razor @@ -17,7 +17,7 @@ @* the @@@ looks weird, ik that, it will result in @username *@ - @@@IdentityService.CurrentUser.Username + @@@IdentityService.GetUser().Username
@@ -43,7 +43,7 @@
- +
@@ -112,9 +112,9 @@ if (PasswordModel.Password != PasswordModel.RepeatedPassword) throw new DisplayException("The passwords do not match"); - await AuthenticationProvider.ChangePassword(IdentityService.CurrentUser, PasswordModel.Password); + await AuthenticationProvider.ChangePassword(IdentityService.GetUser(), PasswordModel.Password); await ToastService.Success("Successfully changed password"); - await IdentityService.Authenticate(true); + await IdentityService.Authenticate(); } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Index.razor b/Moonlight/Core/UI/Views/Admin/Index.razor index 9d7d9a22..872cf906 100644 --- a/Moonlight/Core/UI/Views/Admin/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Index.razor @@ -12,7 +12,7 @@
@foreach (var column in Columns.OrderBy(x => x.Index)) { - if (column.RequiredPermissionLevel <= IdentityService.CurrentUser.Permissions) + if (column.RequiredPermissionLevel <= IdentityService.GetUser().Permissions) {
@column.Component @@ -22,7 +22,7 @@
@foreach (var component in Components.OrderBy(x => x.Index)) { - if (component.RequiredPermissionLevel <= IdentityService.CurrentUser.Permissions) + if (component.RequiredPermissionLevel <= IdentityService.GetUser().Permissions) {
@component.Component diff --git a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor index 3f96fffe..faf93bad 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Sessions.razor @@ -29,10 +29,12 @@ PageSize="50">