Refactor parsers to Superpower; config set; EnsureManagedConfig; options doc

- Parsers: Superpower for all (Directive, FileLine, Include, DhcpHost, Leases, Hosts)
- Shared ConfParserHelpers (Token, OptionalCommentPrefix)
- DnsmasqConfOptionRegistry + DnsmasqOptionKind: typed options (ConfFile, ConfDir,
  Domain, DhcpRange, Server, Local, Address, DhcpOption, DhcpHost, Path, Flag, etc.)
- EnsureManagedConfigHostedService: append conf-dir= to main config if missing,
  then create managed file; DnsmasqOptions + Dockerfile comments for permissions
- Renames: ConfigLine->DnsmasqConfLine, ConfigSet->DnsmasqConfigSet, etc.
- Test harness: entrypoint in scripts/, prepare-test-mount improvements
- Status/Reload API and UI: stdout+stderr, exit code
This commit is contained in:
2026-01-31 19:28:10 +10:00
parent f5bbe7d608
commit 40fbf14327
49 changed files with 2260 additions and 521 deletions
@@ -0,0 +1,69 @@
using DnsmasqWebUI.Models;
using DnsmasqWebUI.Services.Abstractions;
using Microsoft.AspNetCore.Mvc;
namespace DnsmasqWebUI.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ConfigController : ControllerBase
{
private readonly IDnsmasqConfigSetService _configSetService;
private readonly IDnsmasqConfigService _configService;
private readonly IReloadService _reloadService;
public ConfigController(IDnsmasqConfigSetService configSetService, IDnsmasqConfigService configService, IReloadService reloadService)
{
_configSetService = configSetService;
_configService = configService;
_reloadService = reloadService;
}
/// <summary>Returns the config set (main + conf-file/conf-dir) and managed file path. Read-only structure for UI.</summary>
[HttpGet("set")]
public async Task<ActionResult<DnsmasqConfigSet>> GetSet(CancellationToken ct)
{
try
{
var set = await _configSetService.GetConfigSetAsync(ct);
return Ok(set);
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
/// <summary>Returns the full content of the managed config file (parsed lines and effective addn-hosts path in file for display).</summary>
[HttpGet("managed")]
public async Task<ActionResult<ManagedConfigContent>> GetManaged(CancellationToken ct)
{
try
{
var content = await _configService.ReadManagedConfigAsync(ct);
return Ok(content);
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
/// <summary>Writes the full managed config file (ensures one addn-hosts line = HostsPath), then optionally triggers reload.</summary>
[HttpPut("managed")]
public async Task<ActionResult<object>> PutManaged([FromBody] List<DnsmasqConfLine>? lines, CancellationToken ct)
{
if (lines == null)
return BadRequest(new { error = "Body required" });
try
{
await _configService.WriteManagedConfigAsync(lines, ct);
var reload = await _reloadService.ReloadAsync(ct);
return Ok(new { saved = true, reload = new { reload.Success, reload.ExitCode, reload.StdErr } });
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
}