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:
2026-02-05 23:08:40 +10:00
parent 23bf6bb9e0
commit b44dde2c77
106 changed files with 377 additions and 271 deletions
@@ -0,0 +1,65 @@
using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services;
public class ReloadService : IReloadService
{
private readonly DnsmasqOptions _options;
private readonly IProcessRunner _processRunner;
private readonly ILogger<ReloadService> _logger;
private readonly SemaphoreSlim _reloadLock = new(1, 1);
public ReloadService(IProcessRunner processRunner, IOptions<DnsmasqOptions> options, ILogger<ReloadService> logger)
{
_processRunner = processRunner;
_options = options.Value;
_logger = logger;
}
public async Task<ReloadResult> ReloadAsync(CancellationToken ct = default)
{
if (string.IsNullOrWhiteSpace(_options.ReloadCommand))
{
_logger.LogDebug("Reload command not configured");
return new ReloadResult(true, 0, null, "Reload command not configured");
}
if (!await _reloadLock.WaitAsync(TimeSpan.Zero, ct))
{
_logger.LogDebug("Reload already in progress, rejecting concurrent request");
return new ReloadResult(false, -1, null, "Reload already in progress.");
}
try
{
var result = await _processRunner.RunAsync(_options.ReloadCommand, TimeSpan.FromSeconds(30), ct);
var stderr = result.Stderr;
if (result.TimedOut)
stderr = (string.IsNullOrEmpty(stderr) ? "" : stderr + "\n") + "Reload command timed out after 30 seconds.";
if (result.ExceptionMessage != null)
stderr = (string.IsNullOrEmpty(stderr) ? "" : stderr + "\n") + result.ExceptionMessage;
if (result.ExitCode != 0 && result.ExitCode.HasValue)
_logger.LogWarning("Reload command exited with {ExitCode}: {Stderr}", result.ExitCode.Value, stderr);
else if (result.ExitCode == 0)
_logger.LogInformation("Reload command succeeded");
return new ReloadResult(
result.ExitCode == 0,
result.ExitCode ?? -1,
result.Stdout,
stderr);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to run reload command");
return new ReloadResult(false, -1, null, ex.Message);
}
finally
{
_reloadLock.Release();
}
}
}