feat: init the pages for reverse proxy configs and global configs

This commit is contained in:
2025-01-23 17:22:32 +07:00
parent 0ab177abc7
commit 8d322cbecd
19 changed files with 258 additions and 109 deletions

View File

@@ -5,6 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<LangVersion>13</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -14,6 +15,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BlazorMonaco" Version="3.3.0" />
<PackageReference Include="MudBlazor" Version="8.0.0" />
<PackageReference Include="NetCore.AutoRegisterDi" Version="2.2.1" />
</ItemGroup>

View File

@@ -16,6 +16,9 @@
<body>
<Routes @rendermode="InteractiveServer"/>
<script src="_content/BlazorMonaco/jsInterop.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="_framework/blazor.web.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>

View File

@@ -12,20 +12,11 @@
<MudLayout>
<MudAppBar Elevation="1" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@ToggleDrawer" />
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@_drawer.ToggleDrawer" />
<MudSpacer />
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Href="https://github.com/MudBlazor/MudBlazor" Target="_blank" />
</MudAppBar>
<MudDrawer @bind-Open="@_open" ClipMode="DrawerClipMode.Docked" Elevation="1" Variant="@DrawerVariant.Responsive">
<MudDrawerHeader>
<MudText Typo="Typo.h6">Caddy Manager</MudText>
</MudDrawerHeader>
<MudNavMenu>
<MudNavLink Match="NavLinkMatch.All" Href="/">Reverse Proxies</MudNavLink>
<MudNavLink Match="NavLinkMatch.All" Href="/caddyfile">Global Caddyfile</MudNavLink>
<MudNavLink Match="NavLinkMatch.All" Href="/settings">Settings</MudNavLink>
</MudNavMenu>
</MudDrawer>
<NavigationDrawer @ref="_drawer"/>
<MudMainContent Class="pt-16 px-16">
<MudContainer Class="mt-6">
@Body
@@ -33,11 +24,7 @@
</MudMainContent>
</MudLayout>
@code{
private bool _open = false;
private void ToggleDrawer()
{
_open = !_open;
}
}
@code
{
private NavigationDrawer _drawer = null!;
}

View File

@@ -0,0 +1,10 @@
<MudDrawer @bind-Open="@_drawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="1" Variant="@DrawerVariant.Responsive">
<MudDrawerHeader>
<MudText Typo="Typo.h6">Caddy Manager</MudText>
</MudDrawerHeader>
<MudNavMenu>
<MudNavLink Match="NavLinkMatch.All" Href="/" Icon="@Icons.Custom.FileFormats.FileCode">Reverse Proxies</MudNavLink>
<MudNavLink Match="NavLinkMatch.All" Href="/caddyfile" Icon="@Icons.Material.Filled.Language">Global Caddyfile</MudNavLink>
<MudNavLink Match="NavLinkMatch.All" Href="/settings" Icon="@Icons.Material.Filled.Settings">Settings</MudNavLink>
</MudNavMenu>
</MudDrawer>

View File

@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Components;
namespace CaddyManager.Components.Layout;
public partial class NavigationDrawer : ComponentBase
{
private bool _drawerOpen = false;
internal void ToggleDrawer()
{
_drawerOpen = !_drawerOpen;
}
}

View File

@@ -1,66 +1,24 @@
@page "/caddyfile"
@attribute [StreamRendering]
@using CaddyManager.Contracts.Caddy
@inject ICaddyService CaddyService
<PageTitle>Weather</PageTitle>
<PageTitle>Global Caddyfile</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p>
<em>Loading...</em>
</p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th aria-label="Temperature in Celsius">Temp. (C)</th>
<th aria-label="Temperature in Farenheit">Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate a loading indicator
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
<MudCard>
<MudCardHeader>
<MudText Typo="Typo.h6">
Global Caddyfile
</MudText>
</MudCardHeader>
<MudCardContent>
<StandaloneCodeEditor ConstructionOptions="@EditorConstructionOptions" CssClass="caddy-file-editor global-caddy"></StandaloneCodeEditor>
<MudDivider/>
</MudCardContent>
<MudCardActions>
<MudContainer Class="d-flex flex-row-reverse flex-grow-1 gap-4">
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
<MudButton OnClick="Cancel">Cancel</MudButton>
</MudContainer>
</MudCardActions>
</MudCard>

View File

@@ -0,0 +1,36 @@
using BlazorMonaco.Editor;
using Microsoft.AspNetCore.Components;
namespace CaddyManager.Components.Pages;
public partial class Caddyfile: ComponentBase
{
private string _caddyConfigurationContent = string.Empty;
protected override Task OnInitializedAsync()
{
// Load the content of the Caddy configuration file
_caddyConfigurationContent = CaddyService.GetCaddyGlobalConfigurationContent();
return base.OnInitializedAsync();
}
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
{
AutomaticLayout = true,
Language = "plaintext",
Value = _caddyConfigurationContent,
};
}
private void Submit()
{
// CaddyService.SaveCaddyGlobalConfigurationContent(_caddyConfigurationContent);
}
private void Cancel()
{
// CaddyService.GetCaddyGlobalConfigurationContent();
}
}

View File

@@ -0,0 +1,14 @@
@attribute [StreamRendering]
@using CaddyManager.Contracts.Caddy
@inject ICaddyService CaddyService
<MudDialog>
<DialogContent>
<StandaloneCodeEditor ConstructionOptions="@EditorConstructionOptions" CssClass="caddy-file-editor"></StandaloneCodeEditor>
<MudDivider/>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
</DialogActions>
</MudDialog>

View File

@@ -0,0 +1,37 @@
using BlazorMonaco.Editor;
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace CaddyManager.Components.Pages.CaddyfileEditor;
public partial class CaddyfileEditor : ComponentBase
{
private string _caddyConfigurationContent = string.Empty;
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public string FileName { get; set; } = string.Empty;
protected override Task OnInitializedAsync()
{
// Load the content of the Caddy configuration file
_caddyConfigurationContent = CaddyService.GetCaddyConfigurationContent(FileName);
return base.OnInitializedAsync();
}
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
{
AutomaticLayout = true,
Language = "plaintext",
Value = _caddyConfigurationContent,
};
}
private void Submit() => MudDialog.Close(DialogResult.Ok(true));
private void Cancel() => MudDialog.Cancel();
}

View File

@@ -1,24 +0,0 @@
@page "/"
@using CaddyManager.Contracts.Caddy
@inject ICaddyService CaddyService
<PageTitle>Home</PageTitle>
<MudList T="string">
@foreach(var caddyConfig in _availableCaddyConfigurations)
{
<MudListItem Text="@caddyConfig" Icon="@Icons.Material.Filled.InsertDriveFile"/>
<MudDivider/>
}
</MudList>
@code
{
List<string> _availableCaddyConfigurations = [];
protected override Task OnInitializedAsync()
{
_availableCaddyConfigurations = CaddyService.GetExistingCaddyConfigurations();
return base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,14 @@
@page "/"
@attribute [StreamRendering]
@using CaddyManager.Contracts.Caddy
@inject ICaddyService CaddyService
<PageTitle>Reverse proxy confiurations</PageTitle>
<MudList T="string">
@foreach(var caddyConfig in _availableCaddyConfigurations)
{
<ReverseProxyItem FileName="@caddyConfig"/>
<MudDivider/>
}
</MudList>

View File

@@ -0,0 +1,12 @@
namespace CaddyManager.Components.Pages.ReverseProxies;
public partial class ReverseProxiesPage
{
private List<string> _availableCaddyConfigurations = [];
protected override Task OnInitializedAsync()
{
_availableCaddyConfigurations = CaddyService.GetExistingCaddyConfigurations();
return base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,6 @@
@using CaddyManager.Contracts.Caddy
@attribute [StreamRendering]
@inject ICaddyService CaddyService
@inject IDialogService DialogService
<MudListItem Text="@FileName" Icon="@Icons.Material.Filled.InsertDriveFile" T="string" OnClick="Edit"/>

View File

@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace CaddyManager.Components.Pages.ReverseProxies;
public partial class ReverseProxyItem : ComponentBase
{
/// <summary>
/// File path of the Caddy configuration file
/// </summary>
[Parameter]
public string FileName { get; set; } = string.Empty;
private Task Edit()
{
return DialogService.ShowAsync<CaddyfileEditor.CaddyfileEditor>(FileName, options: new DialogOptions
{
FullWidth = true,
MaxWidth = MaxWidth.Medium,
}, parameters: new DialogParameters
{
{ "FileName", FileName }
});
}
}

View File

@@ -8,4 +8,7 @@
@using Microsoft.JSInterop
@using CaddyManager
@using CaddyManager.Components
@using MudBlazor
@using MudBlazor
@using BlazorMonaco
@using BlazorMonaco.Editor
@using BlazorMonaco.Languages

View File

@@ -10,4 +10,18 @@ public interface ICaddyService
/// </summary>
/// <returns></returns>
List<string> GetExistingCaddyConfigurations();
/// <summary>
/// Method to get the content of a Caddy configuration file by its name
/// The expected path to be [ConfigDir]/[configurationName].caddy
/// </summary>
/// <param name="configurationName"></param>
/// <returns></returns>
string GetCaddyConfigurationContent(string configurationName);
/// <summary>
/// Method to get the content of the global Caddy configuration file
/// </summary>
/// <returns></returns>
string GetCaddyGlobalConfigurationContent();
}

View File

@@ -18,6 +18,12 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Blazor": {
"commandName": "Executable",
"workingDirectory": "$(ProjectDir)",
"executablePath": "/Users/ebolo/.dotnet/dotnet",
"commandLineArgs": "watch run debug --launch-profile http"
}
}
}

View File

@@ -7,11 +7,37 @@ namespace CaddyManager.Services;
/// <inheritdoc />
public class CaddyService(IConfigurationsService configurationsService) : ICaddyService
{
private CaddyServiceConfigurations _configurations => configurationsService.CaddyServiceConfigurations;
/// <summary>
/// File name of the global configuration Caddyfile
/// </summary>
public const string CaddyGlobalConfigName = "Caddyfile";
private CaddyServiceConfigurations Configurations => configurationsService.CaddyServiceConfigurations;
/// <inheritdoc />
public List<string> GetExistingCaddyConfigurations()
{
return Directory.GetFiles(_configurations.ConfigDir).ToList();
return Directory.GetFiles(Configurations.ConfigDir)
.Where(filePath => Path.GetFileName(filePath) != CaddyGlobalConfigName)
.Select(Path.GetFileNameWithoutExtension)
.ToList()!;
}
/// <inheritdoc />
public string GetCaddyConfigurationContent(string configurationName)
{
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;
}
/// <inheritdoc />
public string GetCaddyGlobalConfigurationContent() => GetCaddyConfigurationContent(CaddyGlobalConfigName);
}

View File

@@ -0,0 +1,7 @@
.caddy-file-editor {
height: 400px;
}
.caddy-file-editor.global-caddy {
height: 600px;
}