mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-19 12:21:30 +10:00
Refactor: Bootstrap 5.3.8, Bootstrap Icons, Client/Http/Configuration layout
- Update Bootstrap to 5.3.8, add Bootstrap Icons; replace custom SVGs - Fix settings modal: load editing state only when modal opens (avoid refresh overwrite) - Home: StatusSection and LogsSection components with own timers - Move HTTP clients under Client/Http with Abstractions namespace pattern - Rename Options to Configuration; remove empty Options dir - Components: Modals/, Home/; consistent Abstractions (Services, Client.Services, Client.Http)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using DnsmasqWebUI.Models;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for GET api/config/set.</summary>
|
||||
public interface IConfigSetClient
|
||||
{
|
||||
Task<DnsmasqConfigSet> GetConfigSetAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using DnsmasqWebUI.Models;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for GET/PUT api/dhcp/hosts.</summary>
|
||||
public interface IDhcpHostsClient
|
||||
{
|
||||
Task<IReadOnlyList<DhcpHostEntry>> GetDhcpHostsAsync(CancellationToken ct = default);
|
||||
Task<SaveWithReloadResult> SaveDhcpHostsAsync(IReadOnlyList<DhcpHostEntry> entries, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using DnsmasqWebUI.Models;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for GET/PUT api/hosts.</summary>
|
||||
public interface IHostsClient
|
||||
{
|
||||
Task<IReadOnlyList<HostEntry>> GetHostsAsync(CancellationToken ct = default);
|
||||
Task<SaveWithReloadResult> SaveHostsAsync(IReadOnlyList<HostEntry> entries, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using DnsmasqWebUI.Models;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for GET api/leases.</summary>
|
||||
public interface ILeasesClient
|
||||
{
|
||||
Task<LeasesResult> GetLeasesAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using DnsmasqWebUI.Services.Abstractions;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for POST api/reload.</summary>
|
||||
public interface IReloadClient
|
||||
{
|
||||
Task<ReloadResult> ReloadAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using DnsmasqWebUI.Models;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
||||
|
||||
/// <summary>Typed client for GET api/status.</summary>
|
||||
public interface IStatusClient
|
||||
{
|
||||
Task<DnsmasqServiceStatus> GetStatusAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Models;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class ConfigSetClient : IConfigSetClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public ConfigSetClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<DnsmasqConfigSet> GetConfigSetAsync(CancellationToken ct = default) =>
|
||||
await _http.GetFromJsonAsync<DnsmasqConfigSet>("api/config/set", ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/config/set.");
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Models;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class DhcpHostsClient : IDhcpHostsClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public DhcpHostsClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<IReadOnlyList<DhcpHostEntry>> GetDhcpHostsAsync(CancellationToken ct = default)
|
||||
{
|
||||
var list = await _http.GetFromJsonAsync<List<DhcpHostEntry>>("api/dhcp/hosts", ct);
|
||||
return list ?? new List<DhcpHostEntry>();
|
||||
}
|
||||
|
||||
public async Task<SaveWithReloadResult> SaveDhcpHostsAsync(IReadOnlyList<DhcpHostEntry> entries, CancellationToken ct = default)
|
||||
{
|
||||
var response = await _http.PutAsJsonAsync("api/dhcp/hosts", entries, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/dhcp/hosts.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Models;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class HostsClient : IHostsClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public HostsClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<IReadOnlyList<HostEntry>> GetHostsAsync(CancellationToken ct = default)
|
||||
{
|
||||
var list = await _http.GetFromJsonAsync<List<HostEntry>>("api/hosts", ct);
|
||||
return list ?? new List<HostEntry>();
|
||||
}
|
||||
|
||||
public async Task<SaveWithReloadResult> SaveHostsAsync(IReadOnlyList<HostEntry> entries, CancellationToken ct = default)
|
||||
{
|
||||
var response = await _http.PutAsJsonAsync("api/hosts", entries, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/hosts.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Models;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class LeasesClient : ILeasesClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public LeasesClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<LeasesResult> GetLeasesAsync(CancellationToken ct = default) =>
|
||||
await _http.GetFromJsonAsync<LeasesResult>("api/leases", ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/leases.");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Services.Abstractions;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class ReloadClient : IReloadClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public ReloadClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<ReloadResult> ReloadAsync(CancellationToken ct = default)
|
||||
{
|
||||
var response = await _http.PostAsync("api/reload", null, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ReloadResult>(ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/reload.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Net;
|
||||
using DnsmasqWebUI.Services.Abstractions;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
/// <summary>
|
||||
/// DelegatingHandler that rewrites relative request URIs to the current request's scheme, host, and path base.
|
||||
/// Used so Blazor/API callers can use relative paths (e.g. "/api/status") and hit the same host.
|
||||
/// Resolved in the same scope as the code that requested the HttpClient (e.g. Blazor component),
|
||||
/// so IHttpContextAccessor has the current request when available.
|
||||
/// </summary>
|
||||
public sealed class SameHostBaseAddressHandler(IHttpContextAccessor httpContextAccessor) : DelegatingHandler, IApplicationScopedConcrete
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var context = httpContextAccessor.HttpContext;
|
||||
if (context != null && request.RequestUri != null)
|
||||
{
|
||||
var baseUri = new Uri($"{context.Request.Scheme}://{context.Request.Host.Value}{context.Request.PathBase.Value ?? ""}");
|
||||
request.RequestUri = request.RequestUri.IsAbsoluteUri
|
||||
? new Uri(baseUri, request.RequestUri.PathAndQuery)
|
||||
: new Uri(baseUri, request.RequestUri);
|
||||
}
|
||||
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
||||
using DnsmasqWebUI.Models;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace DnsmasqWebUI.Client.Http;
|
||||
|
||||
public sealed class StatusClient : IStatusClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public StatusClient(HttpClient http) => _http = http;
|
||||
|
||||
public async Task<DnsmasqServiceStatus> GetStatusAsync(CancellationToken ct = default) =>
|
||||
await _http.GetFromJsonAsync<DnsmasqServiceStatus>("api/status", ct)
|
||||
?? throw new InvalidOperationException("Unexpected null from api/status.");
|
||||
}
|
||||
Reference in New Issue
Block a user