From 760281d377b0b1f689cf2dd899c4d49711ea5054 Mon Sep 17 00:00:00 2001 From: ebolo Date: Fri, 24 Jan 2025 12:47:20 +0700 Subject: [PATCH] feat: save caddy file on edit and add new --- CaddyManager/CaddyManager.csproj | 1 - CaddyManager/Components/App.razor | 2 +- .../CaddyfileEditor/CaddyfileEditor.razor | 5 +- .../CaddyfileEditor/CaddyfileEditor.razor.cs | 44 +++++++++---- .../{Caddyfile.razor => CaddyfilePage.razor} | 6 +- ...dyfile.razor.cs => CaddyfilePage.razor.cs} | 28 ++++++-- .../ReverseProxies/ReverseProxiesPage.razor | 3 - .../ReverseProxiesPage.razor.cs | 35 ++++++++-- .../ReverseProxies/ReverseProxyItem.razor | 3 - .../ReverseProxies/ReverseProxyItem.razor.cs | 7 ++ CaddyManager/Contracts/Caddy/ICaddyService.cs | 16 +++++ .../Models/Caddy/CaddyOperationResponse.cs | 17 +++++ .../Caddy/CaddySaveConfigurationRequest.cs | 22 +++++++ CaddyManager/Program.cs | 9 +++ CaddyManager/Services/CaddyService.cs | 66 +++++++++++++++++-- 15 files changed, 221 insertions(+), 43 deletions(-) rename CaddyManager/Components/Pages/{Caddyfile.razor => CaddyfilePage.razor} (65%) rename CaddyManager/Components/Pages/{Caddyfile.razor.cs => CaddyfilePage.razor.cs} (55%) create mode 100644 CaddyManager/Models/Caddy/CaddyOperationResponse.cs create mode 100644 CaddyManager/Models/Caddy/CaddySaveConfigurationRequest.cs diff --git a/CaddyManager/CaddyManager.csproj b/CaddyManager/CaddyManager.csproj index fab0efe..52ae363 100644 --- a/CaddyManager/CaddyManager.csproj +++ b/CaddyManager/CaddyManager.csproj @@ -21,7 +21,6 @@ - diff --git a/CaddyManager/Components/App.razor b/CaddyManager/Components/App.razor index 8f70b9c..79fa896 100644 --- a/CaddyManager/Components/App.razor +++ b/CaddyManager/Components/App.razor @@ -19,8 +19,8 @@ - + \ No newline at end of file diff --git a/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor b/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor index a036b47..c2b90b8 100644 --- a/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor +++ b/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor @@ -1,6 +1,4 @@ @attribute [StreamRendering] -@using CaddyManager.Contracts.Caddy -@inject ICaddyService CaddyService @@ -9,7 +7,8 @@ ShrinkLabel="true" Disabled="@(!IsNew)"> File content - diff --git a/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor.cs b/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor.cs index 2772597..30fb3a5 100644 --- a/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor.cs +++ b/CaddyManager/Components/Pages/CaddyfileEditor/CaddyfileEditor.razor.cs @@ -1,5 +1,6 @@ using BlazorMonaco.Editor; using CaddyManager.Contracts.Caddy; +using CaddyManager.Models.Caddy; using Microsoft.AspNetCore.Components; using MudBlazor; @@ -8,31 +9,34 @@ namespace CaddyManager.Components.Pages.CaddyfileEditor; public partial class CaddyfileEditor : ComponentBase { private string _caddyConfigurationContent = string.Empty; - - [CascadingParameter] - private IMudDialogInstance MudDialog { get; set; } = null!; - + private StandaloneCodeEditor _codeEditor = null!; + + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; + /// /// Determines if the Caddy configuration file is new /// private bool IsNew { get; set; } - - [Parameter] - public string FileName { get; set; } = string.Empty; + + [Parameter] public string FileName { get; set; } = string.Empty; + + [Inject] private ICaddyService CaddyService { get; set; } = null!; + + [Inject] private ISnackbar Snackbar { get; set; } = null!; protected override Task OnInitializedAsync() { IsNew = string.IsNullOrWhiteSpace(FileName); - + if (!IsNew) { // Load the content of the Caddy configuration file _caddyConfigurationContent = CaddyService.GetCaddyConfigurationContent(FileName); } - + return base.OnInitializedAsync(); } - + private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor) { return new StandaloneEditorConstructionOptions @@ -44,7 +48,25 @@ public partial class CaddyfileEditor : ComponentBase }; } - private void Submit() => MudDialog.Close(DialogResult.Ok(true)); + private async Task Submit() + { + var response = CaddyService.SaveCaddyConfiguration(new CaddySaveConfigurationRequest + { + IsNew = IsNew, + FileName = FileName, + Content = await _codeEditor.GetValue(), + }); + + if (response.Success) + { + Snackbar.Add($"{FileName} Caddy configuration saved successfully", Severity.Success); + MudDialog.Close(DialogResult.Ok(true)); + } + else + { + Snackbar.Add("Failed to save Caddy configuration", Severity.Error); + } + } private void Cancel() => MudDialog.Cancel(); } \ No newline at end of file diff --git a/CaddyManager/Components/Pages/Caddyfile.razor b/CaddyManager/Components/Pages/CaddyfilePage.razor similarity index 65% rename from CaddyManager/Components/Pages/Caddyfile.razor rename to CaddyManager/Components/Pages/CaddyfilePage.razor index ed8e896..f9b4be1 100644 --- a/CaddyManager/Components/Pages/Caddyfile.razor +++ b/CaddyManager/Components/Pages/CaddyfilePage.razor @@ -1,7 +1,5 @@ @page "/caddyfile" @attribute [StreamRendering] -@using CaddyManager.Contracts.Caddy -@inject ICaddyService CaddyService Global Caddyfile @@ -12,7 +10,9 @@ - + File content + diff --git a/CaddyManager/Components/Pages/Caddyfile.razor.cs b/CaddyManager/Components/Pages/CaddyfilePage.razor.cs similarity index 55% rename from CaddyManager/Components/Pages/Caddyfile.razor.cs rename to CaddyManager/Components/Pages/CaddyfilePage.razor.cs index 64c6d1c..56bb885 100644 --- a/CaddyManager/Components/Pages/Caddyfile.razor.cs +++ b/CaddyManager/Components/Pages/CaddyfilePage.razor.cs @@ -1,11 +1,18 @@ using BlazorMonaco.Editor; +using CaddyManager.Contracts.Caddy; using Microsoft.AspNetCore.Components; +using MudBlazor; namespace CaddyManager.Components.Pages; -public partial class Caddyfile: ComponentBase +public partial class CaddyfilePage : ComponentBase { private string _caddyConfigurationContent = string.Empty; + private StandaloneCodeEditor _codeEditor = null!; + + [Inject] private ICaddyService CaddyService { get; set; } = null!; + + [Inject] private ISnackbar Snackbar { get; set; } = null!; protected override Task OnInitializedAsync() { @@ -13,7 +20,7 @@ public partial class Caddyfile: ComponentBase _caddyConfigurationContent = CaddyService.GetCaddyGlobalConfigurationContent(); return base.OnInitializedAsync(); } - + private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor) { return new StandaloneEditorConstructionOptions @@ -23,12 +30,21 @@ public partial class Caddyfile: ComponentBase Value = _caddyConfigurationContent, }; } - - private void Submit() + + private async Task Submit() { - // CaddyService.SaveCaddyGlobalConfigurationContent(_caddyConfigurationContent); + var response = CaddyService.SaveCaddyGlobalConfiguration(await _codeEditor.GetValue()); + + if (response.Success) + { + Snackbar.Add("Caddy configuration saved successfully", Severity.Success); + } + else + { + Snackbar.Add("Failed to save Caddy configuration", Severity.Error); + } } - + private void Cancel() { // CaddyService.GetCaddyGlobalConfigurationContent(); diff --git a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor index 1160356..876aa9a 100644 --- a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor +++ b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor @@ -1,8 +1,5 @@ @page "/" @attribute [StreamRendering] -@using CaddyManager.Contracts.Caddy -@inject ICaddyService CaddyService -@inject IDialogService DialogService Reverse proxy configurations diff --git a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor.cs b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor.cs index 7209d69..764fc47 100644 --- a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor.cs +++ b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxiesPage.razor.cs @@ -1,3 +1,4 @@ +using CaddyManager.Contracts.Caddy; using Microsoft.AspNetCore.Components; using MudBlazor; @@ -7,20 +8,28 @@ public partial class ReverseProxiesPage : ComponentBase { private List _availableCaddyConfigurations = []; private IReadOnlyCollection _selectedCaddyConfigurations = []; + + [Inject] + private ICaddyService CaddyService { get; set; } = null!; + + [Inject] + private IDialogService DialogService { get; set; } = null!; - protected override Task OnInitializedAsync() + protected override void OnAfterRender(bool firstRender) { - _availableCaddyConfigurations = CaddyService.GetExistingCaddyConfigurations(); - return base.OnInitializedAsync(); + if (firstRender) + { + Refresh(); + } } /// /// Method to help open the dialog to create a new reverse proxy configuration /// /// - private Task NewReverseProxy() + private async Task NewReverseProxy() { - return DialogService.ShowAsync("New configuration", + var dialog = await DialogService.ShowAsync("New configuration", options: new DialogOptions { FullWidth = true, @@ -29,5 +38,21 @@ public partial class ReverseProxiesPage : ComponentBase { { "FileName", string.Empty } }); + + var result = await dialog.Result; + + if (result is { Data: bool, Canceled: false } && (bool)result.Data) + { + Refresh(); + } + } + + /// + /// Get the latest information from the server + /// + private void Refresh() + { + _availableCaddyConfigurations = CaddyService.GetExistingCaddyConfigurations(); + StateHasChanged(); } } \ No newline at end of file diff --git a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor index d67540b..06006df 100644 --- a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor +++ b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor @@ -1,7 +1,4 @@ -@using CaddyManager.Contracts.Caddy @attribute [StreamRendering] -@inject ICaddyService CaddyService -@inject IDialogService DialogService diff --git a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor.cs b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor.cs index 23d5d5f..3bfa86f 100644 --- a/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor.cs +++ b/CaddyManager/Components/Pages/ReverseProxies/ReverseProxyItem.razor.cs @@ -1,3 +1,4 @@ +using CaddyManager.Contracts.Caddy; using Microsoft.AspNetCore.Components; using MudBlazor; @@ -10,6 +11,12 @@ public partial class ReverseProxyItem : ComponentBase /// [Parameter] public string FileName { get; set; } = string.Empty; + + [Inject] + private IDialogService DialogService { get; set; } = null!; + + [Inject] + private ICaddyService CaddyService { get; set; } = null!; private Task Edit() { diff --git a/CaddyManager/Contracts/Caddy/ICaddyService.cs b/CaddyManager/Contracts/Caddy/ICaddyService.cs index fdfbd12..55887aa 100644 --- a/CaddyManager/Contracts/Caddy/ICaddyService.cs +++ b/CaddyManager/Contracts/Caddy/ICaddyService.cs @@ -1,3 +1,5 @@ +using CaddyManager.Models.Caddy; + namespace CaddyManager.Contracts.Caddy; /// @@ -24,4 +26,18 @@ public interface ICaddyService /// /// string GetCaddyGlobalConfigurationContent(); + + /// + /// Method to help save a Caddy configuration file + /// + /// + /// + CaddyOperationResponse SaveCaddyConfiguration(CaddySaveConfigurationRequest request); + + /// + /// Method to help save the global Caddyfile configuration + /// + /// + /// + CaddyOperationResponse SaveCaddyGlobalConfiguration(string content); } \ No newline at end of file diff --git a/CaddyManager/Models/Caddy/CaddyOperationResponse.cs b/CaddyManager/Models/Caddy/CaddyOperationResponse.cs new file mode 100644 index 0000000..fa42200 --- /dev/null +++ b/CaddyManager/Models/Caddy/CaddyOperationResponse.cs @@ -0,0 +1,17 @@ +namespace CaddyManager.Models.Caddy; + +/// +/// Class to wrap the response of a generic Caddy operation +/// +public class CaddyOperationResponse +{ + /// + /// Indicates if the operation was successful + /// + public bool Success { get; set; } + + /// + /// Message to describe the operation result to provide more context + /// + public string Message { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/CaddyManager/Models/Caddy/CaddySaveConfigurationRequest.cs b/CaddyManager/Models/Caddy/CaddySaveConfigurationRequest.cs new file mode 100644 index 0000000..8493cfd --- /dev/null +++ b/CaddyManager/Models/Caddy/CaddySaveConfigurationRequest.cs @@ -0,0 +1,22 @@ +namespace CaddyManager.Models.Caddy; + +/// +/// Wraps the information needed to save a Caddy configuration +/// +public class CaddySaveConfigurationRequest +{ + /// + /// Indicates if the configuration is new + /// + public bool IsNew { get; set; } = false; + + /// + /// Name of the Caddy configuration file + /// + public required string FileName { get; init; } = string.Empty; + + /// + /// Content of the Caddy configuration file + /// + public string Content { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/CaddyManager/Program.cs b/CaddyManager/Program.cs index 3acbd61..ae2f8ee 100644 --- a/CaddyManager/Program.cs +++ b/CaddyManager/Program.cs @@ -15,6 +15,15 @@ builder.Services.RegisterAssemblyPublicNonGenericClasses() .Where(t => t.Name.EndsWith("Service") || t.Name.EndsWith("Repository")) .AsPublicImplementedInterfaces(); +builder.Services.AddSignalR(e => { e.MaximumReceiveMessageSize = 102400000; }); + +builder.Services.AddMudServices(config => +{ + config.SnackbarConfiguration.VisibleStateDuration = 4000; + config.SnackbarConfiguration.HideTransitionDuration = 100; + config.SnackbarConfiguration.ShowTransitionDuration = 100; +}); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/CaddyManager/Services/CaddyService.cs b/CaddyManager/Services/CaddyService.cs index afb5207..4ac9b99 100644 --- a/CaddyManager/Services/CaddyService.cs +++ b/CaddyManager/Services/CaddyService.cs @@ -1,6 +1,7 @@ using CaddyManager.Configurations.Caddy; using CaddyManager.Contracts.Caddy; using CaddyManager.Contracts.Configurations; +using CaddyManager.Models.Caddy; namespace CaddyManager.Services; @@ -10,10 +11,10 @@ public class CaddyService(IConfigurationsService configurationsService) : ICaddy /// /// File name of the global configuration Caddyfile /// - public const string CaddyGlobalConfigName = "Caddyfile"; - + private const string CaddyGlobalConfigName = "Caddyfile"; + private CaddyServiceConfigurations Configurations => configurationsService.CaddyServiceConfigurations; - + /// public List GetExistingCaddyConfigurations() { @@ -26,18 +27,69 @@ public class CaddyService(IConfigurationsService configurationsService) : ICaddy /// public string GetCaddyConfigurationContent(string configurationName) { - var path = configurationName == CaddyGlobalConfigName - ? Path.Combine(Configurations.ConfigDir, CaddyGlobalConfigName) + var path = configurationName == CaddyGlobalConfigName + ? Path.Combine(Configurations.ConfigDir, CaddyGlobalConfigName) : Path.Combine(Configurations.ConfigDir, $"{configurationName}.caddy"); - + if (File.Exists(path)) { return File.ReadAllText(path); } - + return string.Empty; } /// public string GetCaddyGlobalConfigurationContent() => GetCaddyConfigurationContent(CaddyGlobalConfigName); + + /// + public CaddyOperationResponse SaveCaddyConfiguration(CaddySaveConfigurationRequest request) + { + if (string.IsNullOrWhiteSpace(request.FileName)) + { + return new CaddyOperationResponse + { + Success = false, + Message = "The configuration file name is required" + }; + } + + var filePath = Path.Combine(Configurations.ConfigDir, + request.FileName == CaddyGlobalConfigName ? CaddyGlobalConfigName : $"{request.FileName}.caddy"); + // if in the new mode, we would have to check if the file already exists + if (request.IsNew && File.Exists(filePath)) + { + return new CaddyOperationResponse + { + Success = false, + Message = "The configuration file already exists" + }; + } + + try + { + File.WriteAllText(filePath, request.Content); + return new CaddyOperationResponse + { + Success = true, + Message = "Configuration file saved successfully" + }; + } + catch (Exception e) + { + return new CaddyOperationResponse + { + Success = false, + Message = e.Message + }; + } + } + + /// + public CaddyOperationResponse SaveCaddyGlobalConfiguration(string content) => SaveCaddyConfiguration( + new CaddySaveConfigurationRequest + { + FileName = CaddyGlobalConfigName, + Content = content + }); } \ No newline at end of file