mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-03 22:12:00 +10:00
Refactor: reorganise namespaces and move service DTOs to Models/Contracts
- Move Client/Http, Services, Parsers under Infrastructure/ (Client, Services, Parsers) - Move config helpers (DnsmasqConfKeys, DnsmasqOptionTooltips, DnsmasqFileEncoding, EffectiveConfigFieldBuilder) to Infrastructure/Helpers/Config; ApiJsonOptions to Helpers/Http - Move Configuration types (ApplicationOptions, DnsmasqOptions, DnsmasqOptionsValidator) to Models/Config - Merge Models/Status and Models/EffectiveConfig into Models/Dnsmasq and Models/Dnsmasq/EffectiveConfig - Move Client/Models and Client/Services to Models/Client and Infrastructure/Services - Move ConfigSetSnapshot, HostsSnapshot, ProcessRunResult, ManagedConfigContent from Infrastructure/Services/Abstractions to Models/Contracts - Update all namespaces and usings; tests pass
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
@@ -145,14 +147,57 @@ public class DnsmasqConfIncludeParserTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetIncludedPaths_testdata_dnsmasq_conf_ReturnsMainOnlyWhenConfDirMissing()
|
public void GetIncludedPaths_ConfDirMissing_ReturnsMainOnly()
|
||||||
{
|
{
|
||||||
var mainPath = TestDataHelper.GetPath("dnsmasq.conf");
|
var baseDir = Path.Combine(Path.GetTempPath(), "dnsmasq-confdir-missing-" + Guid.NewGuid().ToString("N"));
|
||||||
if (!File.Exists(mainPath))
|
Directory.CreateDirectory(baseDir);
|
||||||
return; // testdata not copied
|
try
|
||||||
var paths = DnsmasqConfIncludeParser.GetIncludedPaths(mainPath);
|
{
|
||||||
Assert.Single(paths);
|
var main = Path.Combine(baseDir, "dnsmasq.conf");
|
||||||
Assert.Equal(Path.GetFullPath(mainPath), paths[0]);
|
File.WriteAllText(main, "conf-dir=nonexistent-subdir\n");
|
||||||
|
var paths = DnsmasqConfIncludeParser.GetIncludedPaths(main);
|
||||||
|
Assert.Single(paths);
|
||||||
|
Assert.Equal(Path.GetFullPath(main), paths[0]);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(baseDir, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Mirrors harness layout: main + conf-dir with multiple files (including lan-dns.conf) + conf-file. Asserts all appear in load order with correct sources.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void GetIncludedPathsWithSource_ConfDirAndConfFile_ReturnsAllFilesInLoadOrderWithSources()
|
||||||
|
{
|
||||||
|
var baseDir = Path.Combine(Path.GetTempPath(), "dnsmasq-harness-layout-" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(baseDir);
|
||||||
|
var confDir = Path.Combine(baseDir, "dnsmasq.d");
|
||||||
|
Directory.CreateDirectory(confDir);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path.Combine(confDir, "01-other.conf"), "# other\n");
|
||||||
|
File.WriteAllText(Path.Combine(confDir, "02-servers.conf"), "# servers\n");
|
||||||
|
File.WriteAllText(Path.Combine(confDir, "lan-dns.conf"), "# lan dns\n");
|
||||||
|
File.WriteAllText(Path.Combine(baseDir, "zz-dnsmasq-webui.conf"), "# managed\n");
|
||||||
|
var main = Path.Combine(baseDir, "dnsmasq.conf");
|
||||||
|
File.WriteAllText(main, "conf-dir=dnsmasq.d\nconf-file=zz-dnsmasq-webui.conf\n");
|
||||||
|
var withSource = DnsmasqConfIncludeParser.GetIncludedPathsWithSource(main);
|
||||||
|
Assert.Equal(5, withSource.Count);
|
||||||
|
Assert.Equal(Path.GetFullPath(main), withSource[0].Path);
|
||||||
|
Assert.Equal(DnsmasqConfFileSource.Main, withSource[0].Source);
|
||||||
|
Assert.Equal(Path.GetFullPath(Path.Combine(confDir, "01-other.conf")), withSource[1].Path);
|
||||||
|
Assert.Equal(DnsmasqConfFileSource.ConfDir, withSource[1].Source);
|
||||||
|
Assert.Equal(Path.GetFullPath(Path.Combine(confDir, "02-servers.conf")), withSource[2].Path);
|
||||||
|
Assert.Equal(DnsmasqConfFileSource.ConfDir, withSource[2].Source);
|
||||||
|
Assert.Equal(Path.GetFullPath(Path.Combine(confDir, "lan-dns.conf")), withSource[3].Path);
|
||||||
|
Assert.Equal(DnsmasqConfFileSource.ConfDir, withSource[3].Source);
|
||||||
|
Assert.Equal(Path.GetFullPath(Path.Combine(baseDir, "zz-dnsmasq-webui.conf")), withSource[4].Path);
|
||||||
|
Assert.Equal(DnsmasqConfFileSource.ConfFile, withSource[4].Source);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(baseDir, recursive: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using DnsmasqWebUI.Configuration;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Tests;
|
namespace DnsmasqWebUI.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
|
||||||
using DnsmasqWebUI.Models.Status;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
|
||||||
|
|
||||||
public sealed class StatusClient : IStatusClient
|
|
||||||
{
|
|
||||||
private readonly HttpClient _http;
|
|
||||||
|
|
||||||
/// <summary>Matches API serialization (camelCase) so nested EffectiveConfigSources and tuple (value, source) deserialize correctly.</summary>
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
public StatusClient(HttpClient http) => _http = http;
|
|
||||||
|
|
||||||
public async Task<DnsmasqServiceStatus> GetStatusAsync(CancellationToken ct = default) =>
|
|
||||||
await _http.GetFromJsonAsync<DnsmasqServiceStatus>("api/status", JsonOptions, ct)
|
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/status.");
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
@* Reusable value-only display for boolean/flag options. Edit mode: checkbox (unless readonly); View: "Enabled" / "Disabled". *@
|
@* Reusable value-only display for boolean/flag options. Edit mode: checkbox (unless readonly); View: "Enabled" / "Disabled". *@
|
||||||
@if (DisplayMode == EffectiveConfigDisplayMode.Edit && Descriptor.GetSource()?.IsReadOnly != true)
|
@if (DisplayMode == EffectiveConfigDisplayMode.Edit && Descriptor.GetSource()?.IsReadOnly != true)
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
@* Value-only display for the port option. Edit mode: number input (unless readonly); View: text with "(default DNS port)". *@
|
@* Value-only display for the port option. Edit mode: number input (unless readonly); View: text with "(default DNS port)". *@
|
||||||
@if (DisplayMode == EffectiveConfigDisplayMode.Edit && Descriptor.GetSource()?.IsReadOnly != true)
|
@if (DisplayMode == EffectiveConfigDisplayMode.Edit && Descriptor.GetSource()?.IsReadOnly != true)
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
@using DnsmasqWebUI.Models.Config
|
@using DnsmasqWebUI.Models.Config
|
||||||
@using DnsmasqWebUI.Services.Abstractions
|
@using DnsmasqWebUI.Infrastructure.Services.Abstractions
|
||||||
@inject IEffectiveConfigRenderFragmentRegistry RenderFragmentRegistry
|
@inject IEffectiveConfigRenderFragmentRegistry RenderFragmentRegistry
|
||||||
|
|
||||||
@* Custom fragment renders only the value; we keep the row shell (label + value slot + badge) here for consistency. *@
|
@* Custom fragment renders only the value; we keep the row shell (label + value slot + badge) here for consistency. *@
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
<span class="ec-field-value-item" title="@(Source?.GetReadOnlyTooltip())">
|
<span class="ec-field-value-item" title="@(Source?.GetReadOnlyTooltip())">
|
||||||
@Value
|
@Value
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
<li class="ec-field">
|
<li class="ec-field">
|
||||||
<span class="ec-field-label" title="@LabelTooltip"><strong>@Label:</strong></span>
|
<span class="ec-field-label" title="@LabelTooltip"><strong>@Label:</strong></span>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
<li class="ec-field" title="@(Source?.GetReadOnlyTooltip())">
|
<li class="ec-field" title="@(Source?.GetReadOnlyTooltip())">
|
||||||
<span class="ec-field-label" title="@(LabelTooltip ?? Source?.GetReadOnlyTooltip())"><strong>@Label:</strong></span>
|
<span class="ec-field-label" title="@(LabelTooltip ?? Source?.GetReadOnlyTooltip())"><strong>@Label:</strong></span>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.Status
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
|
||||||
|
|
||||||
@if (Status != null)
|
@if (Status != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
|
||||||
<CascadingValue Value="DisplayMode">
|
<CascadingValue Value="DisplayMode">
|
||||||
<div class="ec-section @(IsOpen ? "ec-section-open" : "")">
|
<div class="ec-section @(IsOpen ? "ec-section-open" : "")">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Components.Dnsmasq;
|
namespace DnsmasqWebUI.Components.Dnsmasq;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Components.Dnsmasq;
|
namespace DnsmasqWebUI.Components.Dnsmasq;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Client.Models;
|
using DnsmasqWebUI.Models.Client;
|
||||||
using DnsmasqWebUI.Client.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
|
|||||||
@@ -24,9 +24,12 @@ else if (_status != null)
|
|||||||
<h2 class="page-section-title">Service configuration</h2>
|
<h2 class="page-section-title">Service configuration</h2>
|
||||||
<p class="mb-1"><strong>Config files (load order):</strong></p>
|
<p class="mb-1"><strong>Config files (load order):</strong></p>
|
||||||
<ul class="list-unstyled small ms-2 mb-0">
|
<ul class="list-unstyled small ms-2 mb-0">
|
||||||
@if (_configSet != null && _configSet.Files?.Count > 0)
|
@{
|
||||||
|
var configFileList = _status.ConfigFiles?.Count > 0 ? _status.ConfigFiles : _configSet?.Files;
|
||||||
|
}
|
||||||
|
@if (configFileList != null && configFileList.Count > 0)
|
||||||
{
|
{
|
||||||
@foreach (var f in _configSet.Files)
|
@foreach (var f in configFileList)
|
||||||
{
|
{
|
||||||
var isMain = string.Equals(f.Path, _status.MainConfigPath, StringComparison.Ordinal);
|
var isMain = string.Equals(f.Path, _status.MainConfigPath, StringComparison.Ordinal);
|
||||||
var isManaged = f.IsManaged;
|
var isManaged = f.IsManaged;
|
||||||
|
|||||||
@@ -7,19 +7,20 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using DnsmasqWebUI
|
@using DnsmasqWebUI
|
||||||
@using DnsmasqWebUI.Client.Models
|
@using DnsmasqWebUI.Models.Client
|
||||||
@using DnsmasqWebUI.Client.Services.Abstractions
|
@using DnsmasqWebUI.Infrastructure.Services.Abstractions
|
||||||
@using DnsmasqWebUI.Components
|
@using DnsmasqWebUI.Components
|
||||||
@using DnsmasqWebUI.Components.Modals
|
@using DnsmasqWebUI.Components.Modals
|
||||||
@using DnsmasqWebUI.Components.Dnsmasq
|
@using DnsmasqWebUI.Components.Dnsmasq
|
||||||
@using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
@using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Components.Hosts
|
@using DnsmasqWebUI.Components.Hosts
|
||||||
|
@using DnsmasqWebUI.Infrastructure
|
||||||
|
@using DnsmasqWebUI.Infrastructure.Helpers.Config
|
||||||
@using DnsmasqWebUI.Models
|
@using DnsmasqWebUI.Models
|
||||||
@using DnsmasqWebUI.Models.EffectiveConfig
|
@using DnsmasqWebUI.Models.Dnsmasq
|
||||||
|
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
|
||||||
@using DnsmasqWebUI.Models.Hosts
|
@using DnsmasqWebUI.Models.Hosts
|
||||||
@using DnsmasqWebUI.Models.Dhcp
|
@using DnsmasqWebUI.Models.Dhcp
|
||||||
@using DnsmasqWebUI.Models.Status
|
@using DnsmasqWebUI.Models.Config
|
||||||
@using DnsmasqWebUI.Configuration
|
@using DnsmasqWebUI.Infrastructure.Client.Abstractions
|
||||||
@using DnsmasqWebUI.Client.Http.Abstractions
|
|
||||||
@using DnsmasqWebUI.Services.Abstractions
|
|
||||||
@using Microsoft.Extensions.Options
|
@using Microsoft.Extensions.Options
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Controllers;
|
namespace DnsmasqWebUI.Controllers;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Controllers;
|
namespace DnsmasqWebUI.Controllers;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Controllers;
|
namespace DnsmasqWebUI.Controllers;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Controllers;
|
namespace DnsmasqWebUI.Controllers;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Controllers;
|
namespace DnsmasqWebUI.Controllers;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using DnsmasqWebUI.Configuration;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ public class StatusController : ControllerBase
|
|||||||
ManagedFilePathExists: !string.IsNullOrEmpty(set.ManagedFilePath) && System.IO.File.Exists(set.ManagedFilePath),
|
ManagedFilePathExists: !string.IsNullOrEmpty(set.ManagedFilePath) && System.IO.File.Exists(set.ManagedFilePath),
|
||||||
LeasesPathConfigured: !string.IsNullOrEmpty(effectiveLeasesPath),
|
LeasesPathConfigured: !string.IsNullOrEmpty(effectiveLeasesPath),
|
||||||
LeasesPathExists: !string.IsNullOrEmpty(effectiveLeasesPath) && System.IO.File.Exists(effectiveLeasesPath),
|
LeasesPathExists: !string.IsNullOrEmpty(effectiveLeasesPath) && System.IO.File.Exists(effectiveLeasesPath),
|
||||||
|
ConfigFiles: set.Files.Count > 0 ? set.Files : null,
|
||||||
ReloadCommandConfigured: !string.IsNullOrWhiteSpace(_options.ReloadCommand),
|
ReloadCommandConfigured: !string.IsNullOrWhiteSpace(_options.ReloadCommand),
|
||||||
StatusCommandConfigured: !string.IsNullOrWhiteSpace(_options.StatusCommand),
|
StatusCommandConfigured: !string.IsNullOrWhiteSpace(_options.StatusCommand),
|
||||||
StatusShowConfigured: !string.IsNullOrWhiteSpace(_options.StatusShowCommand),
|
StatusShowConfigured: !string.IsNullOrWhiteSpace(_options.StatusShowCommand),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using DnsmasqWebUI.Client.Http;
|
using DnsmasqWebUI.Infrastructure.Client;
|
||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for GET api/config/set.</summary>
|
/// <summary>Typed client for GET api/config/set.</summary>
|
||||||
public interface IConfigSetClient
|
public interface IConfigSetClient
|
||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for GET/PUT api/dhcp/hosts.</summary>
|
/// <summary>Typed client for GET/PUT api/dhcp/hosts.</summary>
|
||||||
public interface IDhcpHostsClient
|
public interface IDhcpHostsClient
|
||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for GET/PUT api/hosts.</summary>
|
/// <summary>Typed client for GET/PUT api/hosts.</summary>
|
||||||
public interface IHostsClient
|
public interface IHostsClient
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for GET api/leases.</summary>
|
/// <summary>Typed client for GET api/leases.</summary>
|
||||||
public interface ILeasesClient
|
public interface ILeasesClient
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for POST api/reload.</summary>
|
/// <summary>Typed client for POST api/reload.</summary>
|
||||||
public interface IReloadClient
|
public interface IReloadClient
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
|
||||||
/// <summary>Typed client for GET api/status.</summary>
|
/// <summary>Typed client for GET api/status.</summary>
|
||||||
public interface IStatusClient
|
public interface IStatusClient
|
||||||
+6
-4
@@ -1,8 +1,10 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
public sealed class ConfigSetClient : IConfigSetClient
|
public sealed class ConfigSetClient : IConfigSetClient
|
||||||
{
|
{
|
||||||
@@ -11,6 +13,6 @@ public sealed class ConfigSetClient : IConfigSetClient
|
|||||||
public ConfigSetClient(HttpClient http) => _http = http;
|
public ConfigSetClient(HttpClient http) => _http = http;
|
||||||
|
|
||||||
public async Task<DnsmasqConfigSet> GetConfigSetAsync(CancellationToken ct = default) =>
|
public async Task<DnsmasqConfigSet> GetConfigSetAsync(CancellationToken ct = default) =>
|
||||||
await _http.GetFromJsonAsync<DnsmasqConfigSet>("api/config/set", ct)
|
await _http.GetFromJsonAsync<DnsmasqConfigSet>("api/config/set", ApiJsonOptions.ClientOptions, ct)
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/config/set.");
|
?? throw new InvalidOperationException("Unexpected null from api/config/set.");
|
||||||
}
|
}
|
||||||
+9
-7
@@ -1,10 +1,12 @@
|
|||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
public sealed class DhcpHostsClient : IDhcpHostsClient
|
public sealed class DhcpHostsClient : IDhcpHostsClient
|
||||||
{
|
{
|
||||||
@@ -14,13 +16,13 @@ public sealed class DhcpHostsClient : IDhcpHostsClient
|
|||||||
|
|
||||||
public async Task<IReadOnlyList<DhcpHostEntry>> GetDhcpHostsAsync(CancellationToken ct = default)
|
public async Task<IReadOnlyList<DhcpHostEntry>> GetDhcpHostsAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var list = await _http.GetFromJsonAsync<List<DhcpHostEntry>>("api/dhcp/hosts", ct);
|
var list = await _http.GetFromJsonAsync<List<DhcpHostEntry>>("api/dhcp/hosts", ApiJsonOptions.ClientOptions, ct);
|
||||||
return list ?? new List<DhcpHostEntry>();
|
return list ?? new List<DhcpHostEntry>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SaveWithReloadResult> SaveDhcpHostsAsync(IReadOnlyList<DhcpHostEntry> entries, CancellationToken ct = default)
|
public async Task<SaveWithReloadResult> SaveDhcpHostsAsync(IReadOnlyList<DhcpHostEntry> entries, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var response = await _http.PutAsJsonAsync("api/dhcp/hosts", entries, ct);
|
var response = await _http.PutAsJsonAsync("api/dhcp/hosts", entries, ApiJsonOptions.ClientOptions, ct);
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var body = await response.Content.ReadAsStringAsync(ct);
|
var body = await response.Content.ReadAsStringAsync(ct);
|
||||||
@@ -29,7 +31,7 @@ public sealed class DhcpHostsClient : IDhcpHostsClient
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var err = System.Text.Json.JsonSerializer.Deserialize<JsonError>(body);
|
var err = System.Text.Json.JsonSerializer.Deserialize<JsonError>(body, ApiJsonOptions.ClientOptions);
|
||||||
if (!string.IsNullOrEmpty(err?.Error))
|
if (!string.IsNullOrEmpty(err?.Error))
|
||||||
msg = err.Error;
|
msg = err.Error;
|
||||||
}
|
}
|
||||||
@@ -37,7 +39,7 @@ public sealed class DhcpHostsClient : IDhcpHostsClient
|
|||||||
}
|
}
|
||||||
throw new HttpRequestException(msg);
|
throw new HttpRequestException(msg);
|
||||||
}
|
}
|
||||||
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ct)
|
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ApiJsonOptions.ClientOptions, ct)
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/dhcp/hosts.");
|
?? throw new InvalidOperationException("Unexpected null from api/dhcp/hosts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
+9
-7
@@ -1,9 +1,11 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
public sealed class HostsClient : IHostsClient
|
public sealed class HostsClient : IHostsClient
|
||||||
{
|
{
|
||||||
@@ -13,21 +15,21 @@ public sealed class HostsClient : IHostsClient
|
|||||||
|
|
||||||
public async Task<IReadOnlyList<HostEntry>> GetHostsAsync(CancellationToken ct = default)
|
public async Task<IReadOnlyList<HostEntry>> GetHostsAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var list = await _http.GetFromJsonAsync<List<HostEntry>>("api/hosts", ct);
|
var list = await _http.GetFromJsonAsync<List<HostEntry>>("api/hosts", ApiJsonOptions.ClientOptions, ct);
|
||||||
return list ?? new List<HostEntry>();
|
return list ?? new List<HostEntry>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyList<ReadOnlyHostsFile>> GetReadOnlyHostsAsync(CancellationToken ct = default)
|
public async Task<IReadOnlyList<ReadOnlyHostsFile>> GetReadOnlyHostsAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var list = await _http.GetFromJsonAsync<List<ReadOnlyHostsFile>>("api/hosts/readonly", ct);
|
var list = await _http.GetFromJsonAsync<List<ReadOnlyHostsFile>>("api/hosts/readonly", ApiJsonOptions.ClientOptions, ct);
|
||||||
return list ?? new List<ReadOnlyHostsFile>();
|
return list ?? new List<ReadOnlyHostsFile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SaveWithReloadResult> SaveHostsAsync(IReadOnlyList<HostEntry> entries, CancellationToken ct = default)
|
public async Task<SaveWithReloadResult> SaveHostsAsync(IReadOnlyList<HostEntry> entries, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var response = await _http.PutAsJsonAsync("api/hosts", entries, ct);
|
var response = await _http.PutAsJsonAsync("api/hosts", entries, ApiJsonOptions.ClientOptions, ct);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ct)
|
return await response.Content.ReadFromJsonAsync<SaveWithReloadResult>(ApiJsonOptions.ClientOptions, ct)
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/hosts.");
|
?? throw new InvalidOperationException("Unexpected null from api/hosts.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-3
@@ -1,8 +1,9 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
public sealed class LeasesClient : ILeasesClient
|
public sealed class LeasesClient : ILeasesClient
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ public sealed class LeasesClient : ILeasesClient
|
|||||||
public async Task<LeasesResult> GetLeasesAsync(bool forceRefresh = false, CancellationToken ct = default)
|
public async Task<LeasesResult> GetLeasesAsync(bool forceRefresh = false, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var url = forceRefresh ? "api/leases?refresh=true" : "api/leases";
|
var url = forceRefresh ? "api/leases?refresh=true" : "api/leases";
|
||||||
return await _http.GetFromJsonAsync<LeasesResult>(url, ct)
|
return await _http.GetFromJsonAsync<LeasesResult>(url, ApiJsonOptions.ClientOptions, ct)
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/leases.");
|
?? throw new InvalidOperationException("Unexpected null from api/leases.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-4
@@ -1,8 +1,9 @@
|
|||||||
using DnsmasqWebUI.Client.Http.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
public sealed class ReloadClient : IReloadClient
|
public sealed class ReloadClient : IReloadClient
|
||||||
{
|
{
|
||||||
@@ -14,7 +15,7 @@ public sealed class ReloadClient : IReloadClient
|
|||||||
{
|
{
|
||||||
var response = await _http.PostAsync("api/reload", null, ct);
|
var response = await _http.PostAsync("api/reload", null, ct);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
return await response.Content.ReadFromJsonAsync<ReloadResult>(ct)
|
return await response.Content.ReadFromJsonAsync<ReloadResult>(ApiJsonOptions.ClientOptions, ct)
|
||||||
?? throw new InvalidOperationException("Unexpected null from api/reload.");
|
?? throw new InvalidOperationException("Unexpected null from api/reload.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Http;
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DelegatingHandler that rewrites relative request URIs to the current request's scheme, host, and path base.
|
/// DelegatingHandler that rewrites relative request URIs to the current request's scheme, host, and path base.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using DnsmasqWebUI.Infrastructure.Client.Abstractions;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Infrastructure.Client;
|
||||||
|
|
||||||
|
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", ApiJsonOptions.ClientOptions, ct)
|
||||||
|
?? throw new InvalidOperationException("Unexpected null from api/status.");
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Infrastructure;
|
|
||||||
|
|
||||||
/// <summary>Encoding for dnsmasq config and hosts files. UTF-8 without BOM so dnsmasq does not see a BOM as "bad option" on line 1.</summary>
|
|
||||||
public static class DnsmasqFileEncoding
|
|
||||||
{
|
|
||||||
public static readonly Encoding Utf8NoBom = new UTF8Encoding(false);
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.Config;
|
namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Literal dnsmasq .conf option names (config keys). Use these everywhere we reference option names
|
/// Literal dnsmasq .conf option names (config keys). Use these everywhere we reference option names
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
|
|
||||||
|
/// <summary>Encoding and reading helpers for dnsmasq config and hosts files. UTF-8 without BOM so dnsmasq does not see a BOM as "bad option" on line 1.</summary>
|
||||||
|
public static class DnsmasqFileEncoding
|
||||||
|
{
|
||||||
|
private const char Utf8BomChar = '\uFEFF';
|
||||||
|
|
||||||
|
public static readonly Encoding Utf8NoBom = new UTF8Encoding(false);
|
||||||
|
|
||||||
|
/// <summary>Read config file as UTF-8 and strip UTF-8 BOM from first line if present, so conf-dir= etc. are recognized.</summary>
|
||||||
|
public static string[] ReadConfigLines(string path)
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines(path, Encoding.UTF8);
|
||||||
|
StripBomFromFirstLine(lines);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Strip UTF-8 BOM from first line if present (mutates list). Use so we do not perpetuate BOM when writing.</summary>
|
||||||
|
public static void StripBomFromFirstLine(IList<string> lines)
|
||||||
|
{
|
||||||
|
if (lines.Count == 0) return;
|
||||||
|
var first = lines[0];
|
||||||
|
if (first.Length > 0 && first[0] == Utf8BomChar)
|
||||||
|
lines[0] = first[1..];
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Models.Config;
|
namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Short explainer tooltips for dnsmasq option names. Shown on hover over the field label.
|
/// Short explainer tooltips for dnsmasq option names. Shown on hover over the field label.
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds field descriptors for viewing and editing. Mapping is done once here via delegates passed to each descriptor.
|
/// Builds field descriptors for viewing and editing. Mapping is done once here via delegates passed to each descriptor.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Infrastructure.Helpers.Http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shared JSON options for API serialization (Controllers) and client deserialization (HTTP clients).
|
||||||
|
/// Keeps camelCase and enum-as-string in one place so server and client stay in sync.
|
||||||
|
/// </summary>
|
||||||
|
public static class ApiJsonOptions
|
||||||
|
{
|
||||||
|
/// <summary>Options for Controllers (AddJsonOptions). Apply via ConfigureServer(JsonSerializerOptions).</summary>
|
||||||
|
public static void ConfigureServer(JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||||
|
options.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Options for HTTP clients when deserializing API responses (camelCase, case-insensitive, enum as string).</summary>
|
||||||
|
public static JsonSerializerOptions ClientOptions { get; } = new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
Converters = { new JsonStringEnumConverter() }
|
||||||
|
};
|
||||||
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@ using Superpower;
|
|||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shared Superpower text-parser helpers used by .conf and related parsers.
|
/// Shared Superpower text-parser helpers used by .conf and related parsers.
|
||||||
+1
-1
@@ -3,7 +3,7 @@ using Superpower;
|
|||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a single <c>dhcp-host=</c> line from a dnsmasq .conf file. Same format as long option without <c>--</c>.
|
/// Parses a single <c>dhcp-host=</c> line from a dnsmasq .conf file. Same format as long option without <c>--</c>.
|
||||||
+1
-1
@@ -2,7 +2,7 @@ using Superpower;
|
|||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Key/value parsing for dnsmasq .conf lines. Used by <see cref="DnsmasqConfIncludeParser"/> to build effective config.
|
/// Key/value parsing for dnsmasq .conf lines. Used by <see cref="DnsmasqConfIncludeParser"/> to build effective config.
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using Superpower;
|
using Superpower;
|
||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a dnsmasq .conf file line-by-line into <see cref="DnsmasqConfLine"/> variants (BlankLine, CommentLine, DhcpHostLine, OtherLine).
|
/// Parses a dnsmasq .conf file line-by-line into <see cref="DnsmasqConfLine"/> variants (BlankLine, CommentLine, DhcpHostLine, OtherLine).
|
||||||
+8
-5
@@ -1,10 +1,11 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using Superpower;
|
using Superpower;
|
||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the main dnsmasq .conf file only for <c>conf-file=</c> and <c>conf-dir=</c> directives
|
/// Parses the main dnsmasq .conf file only for <c>conf-file=</c> and <c>conf-dir=</c> directives
|
||||||
@@ -21,6 +22,7 @@ public static class DnsmasqConfIncludeParser
|
|||||||
private static string[] GetLines(string path, IReadOnlyDictionary<string, string[]> pathToLines) =>
|
private static string[] GetLines(string path, IReadOnlyDictionary<string, string[]> pathToLines) =>
|
||||||
pathToLines.TryGetValue(path, out var L) ? L : Array.Empty<string>();
|
pathToLines.TryGetValue(path, out var L) ? L : Array.Empty<string>();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the ordered list of absolute config file paths dnsmasq loads, in the exact order
|
/// Returns the ordered list of absolute config file paths dnsmasq loads, in the exact order
|
||||||
/// dnsmasq reads them (main interleaved with conf-file and conf-dir).
|
/// dnsmasq reads them (main interleaved with conf-file and conf-dir).
|
||||||
@@ -71,7 +73,7 @@ public static class DnsmasqConfIncludeParser
|
|||||||
if (!seenPaths.Add(canonicalPath))
|
if (!seenPaths.Add(canonicalPath))
|
||||||
yield break; // dnsmasq skips re-reading same file (by inode; we use path)
|
yield break; // dnsmasq skips re-reading same file (by inode; we use path)
|
||||||
|
|
||||||
var lines = File.ReadAllLines(currentFilePath);
|
var lines = DnsmasqFileEncoding.ReadConfigLines(currentFilePath);
|
||||||
var dir = Path.GetDirectoryName(canonicalPath) ?? "";
|
var dir = Path.GetDirectoryName(canonicalPath) ?? "";
|
||||||
|
|
||||||
// Yield "file started" so callers (e.g. GetIncludedPathsWithSource) can add this file even if it only has conf-file/conf-dir lines.
|
// Yield "file started" so callers (e.g. GetIncludedPathsWithSource) can add this file even if it only has conf-file/conf-dir lines.
|
||||||
@@ -100,6 +102,7 @@ public static class DnsmasqConfIncludeParser
|
|||||||
var (directory, matchSuffix, ignoreSuffix) = ParseConfDirValue(value);
|
var (directory, matchSuffix, ignoreSuffix) = ParseConfDirValue(value);
|
||||||
if (string.IsNullOrEmpty(directory)) continue;
|
if (string.IsNullOrEmpty(directory)) continue;
|
||||||
var resolvedDir = ResolvePath(directory, dir) ?? directory;
|
var resolvedDir = ResolvePath(directory, dir) ?? directory;
|
||||||
|
resolvedDir = Path.GetFullPath(resolvedDir);
|
||||||
if (!Directory.Exists(resolvedDir)) continue;
|
if (!Directory.Exists(resolvedDir)) continue;
|
||||||
var files = GetConfDirFilesSorted(resolvedDir, matchSuffix, ignoreSuffix);
|
var files = GetConfDirFilesSorted(resolvedDir, matchSuffix, ignoreSuffix);
|
||||||
foreach (var f in files)
|
foreach (var f in files)
|
||||||
@@ -191,7 +194,7 @@ public static class DnsmasqConfIncludeParser
|
|||||||
if (!File.Exists(mainFull))
|
if (!File.Exists(mainFull))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
foreach (var line in File.ReadAllLines(mainFull))
|
foreach (var line in DnsmasqFileEncoding.ReadConfigLines(mainFull))
|
||||||
{
|
{
|
||||||
var kv = DnsmasqConfDirectiveParser.TryParseKeyValue(line);
|
var kv = DnsmasqConfDirectiveParser.TryParseKeyValue(line);
|
||||||
if (kv == null)
|
if (kv == null)
|
||||||
+1
-1
@@ -3,7 +3,7 @@ using Superpower;
|
|||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a single line of the dnsmasq DHCPv4 lease file (e.g. /var/lib/misc/dnsmasq.leases).
|
/// Parses a single line of the dnsmasq DHCPv4 lease file (e.g. /var/lib/misc/dnsmasq.leases).
|
||||||
+1
-1
@@ -3,7 +3,7 @@ using Superpower;
|
|||||||
using Superpower.Model;
|
using Superpower.Model;
|
||||||
using Superpower.Parsers;
|
using Superpower.Parsers;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Parsers;
|
namespace DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses a single line of an /etc/hosts-style file. Format: IP_address canonical_hostname [aliases...]
|
/// Parses a single line of an /etc/hosts-style file. Format: IP_address canonical_hostname [aliases...]
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker interface for application hosted services that are registered via assembly scanning.
|
/// Marker interface for application hosted services that are registered via assembly scanning.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker for types that are registered as their own concrete type with scoped lifetime.
|
/// Marker for types that are registered as their own concrete type with scoped lifetime.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker interface for application services that are registered as scoped.
|
/// Marker interface for application services that are registered as scoped.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker interface for application services that are registered as singleton.
|
/// Marker interface for application services that are registered as singleton.
|
||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
using DnsmasqWebUI.Client.Models;
|
using DnsmasqWebUI.Models.Client;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads and saves client-side preferences (e.g. polling intervals) to browser local storage.
|
/// Loads and saves client-side preferences (e.g. polling intervals) to browser local storage.
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for the config set (main + conf-dir + managed file). Invalidates on file watchers, staleness, or manual Invalidate.
|
/// Singleton cache for the config set (main + conf-dir + managed file). Invalidates on file watchers, staleness, or manual Invalidate.
|
||||||
+4
-5
@@ -1,8 +1,10 @@
|
|||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
public interface IDnsmasqConfigService : IApplicationScopedService
|
public interface IDnsmasqConfigService : IApplicationScopedService
|
||||||
{
|
{
|
||||||
@@ -11,6 +13,3 @@ public interface IDnsmasqConfigService : IApplicationScopedService
|
|||||||
Task<ManagedConfigContent> ReadManagedConfigAsync(CancellationToken ct = default);
|
Task<ManagedConfigContent> ReadManagedConfigAsync(CancellationToken ct = default);
|
||||||
Task WriteManagedConfigAsync(IReadOnlyList<DnsmasqConfLine> lines, CancellationToken ct = default);
|
Task WriteManagedConfigAsync(IReadOnlyList<DnsmasqConfLine> lines, CancellationToken ct = default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Full managed file content. EffectiveHostsPathInFile is parsed from AddnHosts line if present (for display only).</summary>
|
|
||||||
public record ManagedConfigContent(IReadOnlyList<DnsmasqConfLine> Lines, string EffectiveHostsPathInFile);
|
|
||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>Discovers the dnsmasq config set (main + conf-file/conf-dir) and the managed file path.</summary>
|
/// <summary>Discovers the dnsmasq config set (main + conf-file/conf-dir) and the managed file path.</summary>
|
||||||
public interface IDnsmasqConfigSetService : IApplicationSingleton
|
public interface IDnsmasqConfigSetService : IApplicationSingleton
|
||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lookup for custom display fragments per effective-config field (section + option).
|
/// Lookup for custom display fragments per effective-config field (section + option).
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for hosts: managed hosts file + read-only (system + addn-hosts). Invalidates on watchers, staleness, or Invalidate.
|
/// Singleton cache for hosts: managed hosts file + read-only (system + addn-hosts). Invalidates on watchers, staleness, or Invalidate.
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
public interface IHostsFileService : IApplicationScopedService
|
public interface IHostsFileService : IApplicationScopedService
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for the leases file; invalidates when the file changes (e.g. via file watcher).
|
/// Singleton cache for the leases file; invalidates when the file changes (e.g. via file watcher).
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
public interface ILeasesFileService : IApplicationScopedService
|
public interface ILeasesFileService : IApplicationScopedService
|
||||||
{
|
{
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
/// <summary>Runs a shell command with timeout and cancellation. Used for status, reload, status show, and logs commands.</summary>
|
/// <summary>Runs a shell command with timeout and cancellation. Used for status, reload, status show, and logs commands.</summary>
|
||||||
public interface IProcessRunner : IApplicationScopedService
|
public interface IProcessRunner : IApplicationScopedService
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
public interface IReloadService : IApplicationScopedService
|
public interface IReloadService : IApplicationScopedService
|
||||||
{
|
{
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DnsmasqWebUI.Client.Models;
|
using DnsmasqWebUI.Models.Client;
|
||||||
using DnsmasqWebUI.Client.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Client.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads and saves client-side settings to browser local storage via JS interop.
|
/// Loads and saves client-side settings to browser local storage via JS interop.
|
||||||
+8
-7
@@ -1,13 +1,14 @@
|
|||||||
using System.Text;
|
|
||||||
using DnsmasqWebUI.Configuration;
|
|
||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for the config set (main + conf-dir + managed file). Reads all files once per refresh,
|
/// Singleton cache for the config set (main + conf-dir + managed file). Reads all files once per refresh,
|
||||||
@@ -203,7 +204,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
dict[canonical] = File.ReadAllLines(path, Encoding.UTF8);
|
dict[canonical] = DnsmasqFileEncoding.ReadConfigLines(path);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
+10
-19
@@ -1,12 +1,14 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using DnsmasqWebUI.Infrastructure;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
public class DnsmasqConfigService : IDnsmasqConfigService
|
public class DnsmasqConfigService : IDnsmasqConfigService
|
||||||
{
|
{
|
||||||
@@ -113,7 +115,9 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
|||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
var read = await File.ReadAllLinesAsync(path, DnsmasqFileEncoding.Utf8NoBom, ct);
|
var read = await File.ReadAllLinesAsync(path, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||||
rawLines = StripBomFromFirstLine(read);
|
var list = read.ToList();
|
||||||
|
DnsmasqFileEncoding.StripBomFromFirstLine(list);
|
||||||
|
rawLines = list;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
rawLines = Array.Empty<string>();
|
rawLines = Array.Empty<string>();
|
||||||
@@ -203,17 +207,4 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
|||||||
_logger.LogInformation("Wrote managed config file: {Path}", path);
|
_logger.LogInformation("Wrote managed config file: {Path}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Strip UTF-8 BOM from first line if present.</summary>
|
|
||||||
private static IReadOnlyList<string> StripBomFromFirstLine(IReadOnlyList<string> lines)
|
|
||||||
{
|
|
||||||
if (lines.Count == 0) return lines;
|
|
||||||
var first = lines[0];
|
|
||||||
if (first.Length > 0 && first[0] == '\uFEFF')
|
|
||||||
{
|
|
||||||
var list = lines.ToList();
|
|
||||||
list[0] = first[1..];
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+5
-3
@@ -1,7 +1,9 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
public class DnsmasqConfigSetService : IDnsmasqConfigSetService
|
public class DnsmasqConfigSetService : IDnsmasqConfigSetService
|
||||||
{
|
{
|
||||||
+5
-4
@@ -1,10 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays;
|
using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps (sectionId, optionName) to custom effective-config value display component types and builds
|
/// Maps (sectionId, optionName) to custom effective-config value display component types and builds
|
||||||
+6
-16
@@ -1,13 +1,12 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using DnsmasqWebUI.Infrastructure;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.Config;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
using DnsmasqWebUI.Configuration;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs once at startup: ensures the managed file is included only by a conf-file= directive (never conf-dir=),
|
/// Runs once at startup: ensures the managed file is included only by a conf-file= directive (never conf-dir=),
|
||||||
@@ -52,7 +51,7 @@ public class EnsureManagedConfigHostedService : IApplicationHostedService
|
|||||||
var lines = File.Exists(mainFull)
|
var lines = File.Exists(mainFull)
|
||||||
? (await File.ReadAllLinesAsync(mainFull, DnsmasqFileEncoding.Utf8NoBom, cancellationToken)).ToList()
|
? (await File.ReadAllLinesAsync(mainFull, DnsmasqFileEncoding.Utf8NoBom, cancellationToken)).ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
StripBomFromFirstLine(lines);
|
DnsmasqFileEncoding.StripBomFromFirstLine(lines);
|
||||||
|
|
||||||
// Remove any conf-file= line that points to our managed path (we will add it as the last line).
|
// Remove any conf-file= line that points to our managed path (we will add it as the last line).
|
||||||
for (var i = lines.Count - 1; i >= 0; i--)
|
for (var i = lines.Count - 1; i >= 0; i--)
|
||||||
@@ -96,14 +95,5 @@ public class EnsureManagedConfigHostedService : IApplicationHostedService
|
|||||||
await configService.WriteManagedConfigAsync(Array.Empty<DnsmasqConfLine>(), cancellationToken);
|
await configService.WriteManagedConfigAsync(Array.Empty<DnsmasqConfLine>(), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Strip UTF-8 BOM from first line if present, so we do not perpetuate it when writing.</summary>
|
|
||||||
private static void StripBomFromFirstLine(List<string> lines)
|
|
||||||
{
|
|
||||||
if (lines.Count == 0) return;
|
|
||||||
var first = lines[0];
|
|
||||||
if (first.Length > 0 && first[0] == '\uFEFF')
|
|
||||||
lines[0] = first[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
}
|
}
|
||||||
+5
-4
@@ -1,11 +1,12 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using DnsmasqWebUI.Configuration;
|
using DnsmasqWebUI.Models.Config;
|
||||||
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for hosts: managed hosts file + read-only (system + addn-hosts). Invalidates on watchers (managed + system path), staleness, or Invalidate.
|
/// Singleton cache for hosts: managed hosts file + read-only (system + addn-hosts). Invalidates on watchers (managed + system path), staleness, or Invalidate.
|
||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
using DnsmasqWebUI.Infrastructure;
|
using DnsmasqWebUI.Infrastructure.Helpers.Config;
|
||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
public class HostsFileService : IHostsFileService
|
public class HostsFileService : IHostsFileService
|
||||||
{
|
{
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Parsers;
|
using DnsmasqWebUI.Infrastructure.Parsers;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton cache for the leases file with a <see cref="FileSystemWatcher"/> so we re-read only when the file changes.
|
/// Singleton cache for the leases file with a <see cref="FileSystemWatcher"/> so we re-read only when the file changes.
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
public class LeasesFileService : ILeasesFileService
|
public class LeasesFileService : ILeasesFileService
|
||||||
{
|
{
|
||||||
+3
-2
@@ -1,9 +1,10 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Models.Contracts;
|
||||||
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
/// <summary>Runs shell commands via /bin/sh with async output capture and timeout. Used by StatusController and ReloadService.</summary>
|
/// <summary>Runs shell commands via /bin/sh with async output capture and timeout. Used by StatusController and ReloadService.</summary>
|
||||||
public sealed class ProcessRunner : IProcessRunner
|
public sealed class ProcessRunner : IProcessRunner
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
using DnsmasqWebUI.Configuration;
|
using DnsmasqWebUI.Models.Config;
|
||||||
using DnsmasqWebUI.Services.Abstractions;
|
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services;
|
namespace DnsmasqWebUI.Infrastructure.Services;
|
||||||
|
|
||||||
public class ReloadService : IReloadService
|
public class ReloadService : IReloadService
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Client.Models;
|
namespace DnsmasqWebUI.Models.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Client-side preferences persisted in browser local storage.
|
/// Client-side preferences persisted in browser local storage.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Client.Models;
|
namespace DnsmasqWebUI.Models.Client;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Which subset of client settings to show in the settings modal.
|
/// Which subset of client settings to show in the settings modal.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Configuration;
|
namespace DnsmasqWebUI.Models.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Application-level settings (title, etc.). Bound from "Application" config section
|
/// Application-level settings (title, etc.). Bound from "Application" config section
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Configuration;
|
namespace DnsmasqWebUI.Models.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configuration for dnsmasq paths and reload/status commands.
|
/// Configuration for dnsmasq paths and reload/status commands.
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Configuration;
|
namespace DnsmasqWebUI.Models.Config;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates required dnsmasq options at startup. If config is missing or default paths don't point at existing files,
|
/// Validates required dnsmasq options at startup. If config is missing or default paths don't point at existing files,
|
||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
using DnsmasqWebUI.Models.Dhcp;
|
using DnsmasqWebUI.Models.Dhcp;
|
||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
/// <summary>Immutable snapshot from the config set cache: config set, effective config, sources, managed file content, and DHCP host entries (one read per refresh).</summary>
|
/// <summary>Immutable snapshot from the config set cache: config set, effective config, sources, managed file content, and DHCP host entries (one read per refresh).</summary>
|
||||||
public record ConfigSetSnapshot(
|
public record ConfigSetSnapshot(
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Hosts;
|
using DnsmasqWebUI.Models.Hosts;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
/// <summary>Immutable snapshot from the hosts cache: managed hosts file entries and read-only hosts files (system + addn-hosts).</summary>
|
/// <summary>Immutable snapshot from the hosts cache: managed hosts file entries and read-only hosts files (system + addn-hosts).</summary>
|
||||||
public record HostsSnapshot(
|
public record HostsSnapshot(
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using DnsmasqWebUI.Models.Config;
|
||||||
|
|
||||||
|
namespace DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
|
/// <summary>Full managed file content. EffectiveHostsPathInFile is parsed from AddnHosts line if present (for display only).</summary>
|
||||||
|
public record ManagedConfigContent(IReadOnlyList<DnsmasqConfLine> Lines, string EffectiveHostsPathInFile);
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
namespace DnsmasqWebUI.Services.Abstractions;
|
namespace DnsmasqWebUI.Models.Contracts;
|
||||||
|
|
||||||
/// <summary>Result of running a shell command via <see cref="IProcessRunner"/>.</summary>
|
/// <summary>Result of running a shell command via IProcessRunner.</summary>
|
||||||
/// <param name="ExitCode">Process exit code, or null if timed out or failed to start.</param>
|
/// <param name="ExitCode">Process exit code, or null if timed out or failed to start.</param>
|
||||||
/// <param name="Stdout">Standard output.</param>
|
/// <param name="Stdout">Standard output.</param>
|
||||||
/// <param name="Stderr">Standard error.</param>
|
/// <param name="Stderr">Standard error.</param>
|
||||||
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Models.Status;
|
namespace DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dnsmasq service and config status returned by GET api/status.
|
/// Dnsmasq service and config status returned by GET api/status.
|
||||||
@@ -21,6 +21,7 @@ namespace DnsmasqWebUI.Models.Status;
|
|||||||
/// <param name="ManagedFilePathExists">True if <paramref name="ManagedFilePath"/> exists on disk.</param>
|
/// <param name="ManagedFilePathExists">True if <paramref name="ManagedFilePath"/> exists on disk.</param>
|
||||||
/// <param name="LeasesPathConfigured">True when dhcp-leasefile is set in effective config.</param>
|
/// <param name="LeasesPathConfigured">True when dhcp-leasefile is set in effective config.</param>
|
||||||
/// <param name="LeasesPathExists">True if <paramref name="LeasesPath"/> is set and the file exists on disk.</param>
|
/// <param name="LeasesPathExists">True if <paramref name="LeasesPath"/> is set and the file exists on disk.</param>
|
||||||
|
/// <param name="ConfigFiles">Ordered list of config files (main + conf-file + conf-dir) for "Config files (load order)" in the UI. Null or empty when config set is unavailable.</param>
|
||||||
/// <param name="ReloadCommandConfigured">True when ReloadCommand is set in app config (reload after save).</param>
|
/// <param name="ReloadCommandConfigured">True when ReloadCommand is set in app config (reload after save).</param>
|
||||||
/// <param name="StatusCommandConfigured">True when StatusCommand is set (used to determine dnsmasq active/inactive).</param>
|
/// <param name="StatusCommandConfigured">True when StatusCommand is set (used to determine dnsmasq active/inactive).</param>
|
||||||
/// <param name="StatusShowConfigured">True when StatusShowCommand is set (full service status output on Dnsmasq page).</param>
|
/// <param name="StatusShowConfigured">True when StatusShowCommand is set (full service status output on Dnsmasq page).</param>
|
||||||
@@ -52,6 +53,7 @@ public record DnsmasqServiceStatus(
|
|||||||
bool ManagedFilePathExists,
|
bool ManagedFilePathExists,
|
||||||
bool LeasesPathConfigured,
|
bool LeasesPathConfigured,
|
||||||
bool LeasesPathExists,
|
bool LeasesPathExists,
|
||||||
|
IReadOnlyList<DnsmasqConfigSetEntry>? ConfigFiles,
|
||||||
bool ReloadCommandConfigured,
|
bool ReloadCommandConfigured,
|
||||||
bool StatusCommandConfigured,
|
bool StatusCommandConfigured,
|
||||||
bool StatusShowConfigured,
|
bool StatusShowConfigured,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Where an effective config value came from (which file and line). Used so the UI can show exactly which file
|
/// Where an effective config value came from (which file and line). Used so the UI can show exactly which file
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>Source of a file in the dnsmasq config set: main config, conf-file=, or conf-dir=.</summary>
|
/// <summary>Source of a file in the dnsmasq config set: main config, conf-file=, or conf-dir=.</summary>
|
||||||
public enum DnsmasqConfFileSource
|
public enum DnsmasqConfFileSource
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>Ordered set of dnsmasq config files (main + conf-file + conf-dir). ManagedFilePath is the single config file we read/write; ManagedHostsFilePath is the single hosts file we read/write.</summary>
|
/// <summary>Ordered set of dnsmasq config files (main + conf-file + conf-dir). ManagedFilePath is the single config file we read/write; ManagedHostsFilePath is the single hosts file we read/write.</summary>
|
||||||
public record DnsmasqConfigSet(
|
public record DnsmasqConfigSet(
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>One file in the dnsmasq config set (main or included). IsManaged is true only for the app-managed file.</summary>
|
/// <summary>One file in the dnsmasq config set (main or included). IsManaged is true only for the app-managed file.</summary>
|
||||||
public record DnsmasqConfigSetEntry(string Path, string FileName, DnsmasqConfFileSource Source, bool IsManaged);
|
public record DnsmasqConfigSetEntry(string Path, string FileName, DnsmasqConfFileSource Source, bool IsManaged);
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Display mode for effective-config fields. Cascaded from the parent so that
|
/// Display mode for effective-config fields. Cascaded from the parent so that
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using DnsmasqWebUI.Models.Status;
|
using DnsmasqWebUI.Models.Dnsmasq;
|
||||||
|
|
||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Glue for rendering one effective-config field: metadata (section, option name) plus delegates that resolve value, source, and items from Status at render time.
|
/// Glue for rendering one effective-config field: metadata (section, option name) plus delegates that resolve value, source, and items from Status at render time.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace DnsmasqWebUI.Models.EffectiveConfig;
|
namespace DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Source file for each field in <see cref="EffectiveDnsmasqConfig"/>.
|
/// Source file for each field in <see cref="EffectiveDnsmasqConfig"/>.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user