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
@@ -31,7 +31,7 @@ public class StatusController : ControllerBase
{
var set = await _configSetService.GetConfigSetAsync(ct);
var effectiveLeasesPath = _configSetService.GetLeasesPath();
var effectiveConfig = _configSetService.GetEffectiveConfig();
var (effectiveConfig, effectiveConfigSources) = _configSetService.GetEffectiveConfigWithSources();
var (dhcpRangeStart, dhcpRangeEnd) = _configSetService.GetDhcpRange();
var systemHostsPath = _options.SystemHostsPath?.Trim();
@@ -65,6 +65,7 @@ public class StatusController : ControllerBase
NoHosts: effectiveConfig.NoHosts,
AddnHostsPaths: effectiveConfig.AddnHostsPaths,
EffectiveConfig: effectiveConfig,
EffectiveConfigSources: effectiveConfigSources,
MainConfigPath: _options.MainConfigPath,
ManagedFilePath: set.ManagedFilePath,
LeasesPath: effectiveLeasesPath,
@@ -127,7 +128,7 @@ public class StatusController : ControllerBase
[HttpGet("logs/download")]
public async Task<IActionResult> GetLogsDownload(CancellationToken ct)
{
var effectiveConfig = _configSetService.GetEffectiveConfig();
var (effectiveConfig, _) = _configSetService.GetEffectiveConfigWithSources();
var logsPath = EffectiveDnsmasqConfig.GetLogsPath(effectiveConfig);
if (string.IsNullOrEmpty(logsPath))
return NotFound();