Effective config with source tracking, Dnsmasq page, and test harness

- Effective config: track value sources (ConfigValueSource, EffectiveConfigSources)
  and show readonly badges/tooltips for non-managed files. Multi-value options
  use ValueWithSource/PathWithSource for JSON. StatusClient uses camelCase
  serialization for reliable deserialization.
- Parsing: align with dnsmasq (conf-file/conf-dir order, suffix filter, case-
  sensitive keys). DnsmasqConfIncludeParser and DnsmasqConfigSetService build
  effective config and sources. New DnsmasqConfigSetServiceTests.
- UI: rename Overview/Home to Dnsmasq (page, nav, doc comments). Add
  EffectiveConfigOptionItem and EffectiveConfigMultiValueRow components.
  Dnsmasq.razor uses them; Hosts.razor shows addn-hosts with source. Encode
  server-derived strings in markup to avoid XSS.
- Home section namespace: Components.Home -> Components.Dnsmasq (LogsSection,
  StatusSection, _Imports).
- Test data: expand testdata (README, resolv.dnsmasq, hosts.extra, dnsmasq.d
  configs). dhcp.conf static IP aligned with testnet.
- agents.md: add agent instructions for test harness and conventions.
This commit is contained in:
2026-02-02 12:12:01 +10:00
parent a60d0e8db8
commit 888bc980cf
39 changed files with 1204 additions and 107 deletions
+9 -1
View File
@@ -1,3 +1,4 @@
using System.Text.Json;
using DnsmasqWebUI.Client.Http.Abstractions;
using DnsmasqWebUI.Models;
using System.Net.Http.Json;
@@ -8,9 +9,16 @@ 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", ct)
await _http.GetFromJsonAsync<DnsmasqServiceStatus>("api/status", JsonOptions, ct)
?? throw new InvalidOperationException("Unexpected null from api/status.");
}