Reorganise Infrastructure/Services into feature namespaces

- Move registration marker interfaces to Registration/Abstractions
- Add feature folders with Abstractions subfolders: EffectiveConfig,
  Dnsmasq/Config, Dnsmasq/Hosts, Dnsmasq/Leases, Dnsmasq/Reload,
  Logs, Updates, UI/Settings, Common/Process
- Place interfaces in each feature's Abstractions namespace; align
  implementation namespaces to folder paths
- Update Program, extensions, controllers, hubs, helpers, components,
  and tests to use new namespaces
- Remove old flat Services/Abstractions and root-level service files
This commit is contained in:
2026-02-28 22:17:20 +10:00
parent f8754d8d6e
commit 2cfbaf000c
67 changed files with 133 additions and 93 deletions
@@ -1,7 +1,7 @@
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -3,7 +3,7 @@
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig @using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Models.Config @using DnsmasqWebUI.Models.Config
@using DnsmasqWebUI.Infrastructure.Helpers.Config @using DnsmasqWebUI.Infrastructure.Helpers.Config
@using DnsmasqWebUI.Infrastructure.Services.Abstractions @using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.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. Pending value shown until save. *@ @* Custom fragment renders only the value; we keep the row shell (label + value slot + badge) here for consistency. Pending value shown until save. *@
@@ -1,7 +1,7 @@
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig @namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Models.Dnsmasq @using DnsmasqWebUI.Models.Dnsmasq
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig @using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Infrastructure.Services.Abstractions @using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions
@if (ShowModal) @if (ShowModal)
{ {
@@ -1,7 +1,7 @@
@namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig @namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Models.Dnsmasq @using DnsmasqWebUI.Models.Dnsmasq
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig @using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Infrastructure.Services.Abstractions @using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions
<div class="modal fade show" style="display: block;" tabindex="-1" role="dialog" aria-labelledby="ec-save-modal-title" aria-modal="true"> <div class="modal fade show" style="display: block;" tabindex="-1" role="dialog" aria-labelledby="ec-save-modal-title" aria-modal="true">
<div class="modal-dialog modal-dialog-scrollable" role="document"> <div class="modal-dialog modal-dialog-scrollable" role="document">
@@ -2,7 +2,7 @@
@using DnsmasqWebUI.Models.Dnsmasq @using DnsmasqWebUI.Models.Dnsmasq
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig @using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
@using DnsmasqWebUI.Infrastructure.Helpers.Config @using DnsmasqWebUI.Infrastructure.Helpers.Config
@using DnsmasqWebUI.Infrastructure.Services.Abstractions @using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions
@inject IEffectiveConfigEditSession Session @inject IEffectiveConfigEditSession Session
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig; namespace DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig;
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Extensions; using DnsmasqWebUI.Extensions;
using DnsmasqWebUI.Models.Client; using DnsmasqWebUI.Models.Client;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.UI.Settings.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;
+9 -1
View File
@@ -10,7 +10,15 @@
@using DnsmasqWebUI.Extensions @using DnsmasqWebUI.Extensions
@using DnsmasqWebUI.Models.Client @using DnsmasqWebUI.Models.Client
@using DnsmasqWebUI.Models.Client.Abstractions @using DnsmasqWebUI.Models.Client.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Abstractions @using DnsmasqWebUI.Infrastructure.Services.Common.Process.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.UI.Settings.Abstractions
@using DnsmasqWebUI.Infrastructure.Services.Updates.Abstractions
@using DnsmasqWebUI.Components @using DnsmasqWebUI.Components
@using DnsmasqWebUI.Components.Shared @using DnsmasqWebUI.Components.Shared
@using DnsmasqWebUI.Components.Modals @using DnsmasqWebUI.Components.Modals
@@ -1,4 +1,5 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
@@ -1,4 +1,5 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -1,4 +1,5 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Hosts; using DnsmasqWebUI.Models.Hosts;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -1,4 +1,4 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.Abstractions;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,4 +1,4 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,4 +1,5 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Common.Process.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
@@ -1,7 +1,7 @@
using System.Threading.Channels; using System.Threading.Channels;
using DnsmasqWebUI.Infrastructure.Logging; using DnsmasqWebUI.Infrastructure.Logging;
using DnsmasqWebUI.Infrastructure.Services; using DnsmasqWebUI.Infrastructure.Services.Logs;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -1,7 +1,7 @@
using System.Reflection; using System.Reflection;
using DnsmasqWebUI.Infrastructure.Client; using DnsmasqWebUI.Infrastructure.Client;
using DnsmasqWebUI.Infrastructure.Client.Abstractions; using DnsmasqWebUI.Infrastructure.Client.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
+1 -1
View File
@@ -1,4 +1,4 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
namespace DnsmasqWebUI.Hubs; namespace DnsmasqWebUI.Hubs;
@@ -1,4 +1,4 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Client.Abstractions; namespace DnsmasqWebUI.Infrastructure.Client.Abstractions;
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Infrastructure.Client.Abstractions; using DnsmasqWebUI.Infrastructure.Client.Abstractions;
using DnsmasqWebUI.Infrastructure.Helpers.Http; using DnsmasqWebUI.Infrastructure.Helpers.Http;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using System.Net.Http.Json; using System.Net.Http.Json;
namespace DnsmasqWebUI.Infrastructure.Client; namespace DnsmasqWebUI.Infrastructure.Client;
@@ -1,5 +1,5 @@
using System.Net; using System.Net;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Client; namespace DnsmasqWebUI.Infrastructure.Client;
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Helpers.Config; namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Helpers.Config; namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Helpers.Config; namespace DnsmasqWebUI.Infrastructure.Helpers.Config;
@@ -1,5 +1,5 @@
using System.Threading.Channels; using System.Threading.Channels;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -1,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Common.Process.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,10 +1,10 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Common.Process.Abstractions;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Common.Process;
/// <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
@@ -29,9 +29,9 @@ public sealed class ProcessRunner : IProcessRunner
var stdout = new StringBuilder(); var stdout = new StringBuilder();
var stderr = new StringBuilder(); var stderr = new StringBuilder();
using var process = new Process using var process = new System.Diagnostics.Process
{ {
StartInfo = new ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo
{ {
FileName = "/bin/sh", FileName = "/bin/sh",
Arguments = "-c \"" + command.Replace("\"", "\\\"") + "\"", Arguments = "-c \"" + command.Replace("\"", "\\\"") + "\"",
@@ -1,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.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.
@@ -2,9 +2,10 @@ using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
/// <summary>Reads and writes the managed dnsmasq config file and DHCP hosts entries (the app-managed config and dhcp-host= lines).</summary> /// <summary>Reads and writes the managed dnsmasq config file and DHCP hosts entries (the app-managed config and dhcp-host= lines).</summary>
public interface IDnsmasqConfigService : IApplicationScopedService public interface IDnsmasqConfigService : IApplicationScopedService
@@ -1,7 +1,8 @@
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.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
@@ -5,10 +5,10 @@ using DnsmasqWebUI.Models.Dhcp;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config;
/// <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,
@@ -1,7 +1,7 @@
using System.Text; using System.Text;
using DnsmasqWebUI.Infrastructure.Helpers.Config; using DnsmasqWebUI.Infrastructure.Helpers.Config;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
@@ -9,7 +9,7 @@ using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config;
public class DnsmasqConfigService : IDnsmasqConfigService public class DnsmasqConfigService : IDnsmasqConfigService
{ {
@@ -1,9 +1,9 @@
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config;
public class DnsmasqConfigSetService : IDnsmasqConfigSetService public class DnsmasqConfigSetService : IDnsmasqConfigSetService
{ {
@@ -1,13 +1,14 @@
using System.Text; using System.Text;
using DnsmasqWebUI.Infrastructure.Helpers.Config; using DnsmasqWebUI.Infrastructure.Helpers.Config;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config;
/// <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=),
@@ -1,7 +1,8 @@
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Hosts; using DnsmasqWebUI.Models.Hosts;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.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,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Hosts; using DnsmasqWebUI.Models.Hosts;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.Abstractions;
/// <summary>Reads and writes the app-managed hosts file (the single hosts file the app edits).</summary> /// <summary>Reads and writes the app-managed hosts file (the single hosts file the app edits).</summary>
public interface IHostsFileService : IApplicationScopedService public interface IHostsFileService : IApplicationScopedService
@@ -3,10 +3,11 @@ using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Contracts; using DnsmasqWebUI.Models.Contracts;
using DnsmasqWebUI.Models.Hosts; using DnsmasqWebUI.Models.Hosts;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts;
/// <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.
@@ -1,10 +1,11 @@
using DnsmasqWebUI.Infrastructure.Helpers.Config; using DnsmasqWebUI.Infrastructure.Helpers.Config;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts.Abstractions;
using DnsmasqWebUI.Models.Hosts; using DnsmasqWebUI.Models.Hosts;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Hosts;
public class HostsFileService : IHostsFileService public class HostsFileService : IHostsFileService
{ {
@@ -1,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.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,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.Abstractions;
/// <summary>Reads the dnsmasq DHCP leases file (path from effective config).</summary> /// <summary>Reads the dnsmasq DHCP leases file (path from effective config).</summary>
public interface ILeasesFileService : IApplicationScopedService public interface ILeasesFileService : IApplicationScopedService
@@ -1,9 +1,10 @@
using System.Text; using System.Text;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
using DnsmasqWebUI.Infrastructure.Parsers; using DnsmasqWebUI.Infrastructure.Parsers;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases;
/// <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.
@@ -1,8 +1,8 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases.Abstractions;
using DnsmasqWebUI.Models.Dhcp; using DnsmasqWebUI.Models.Dhcp;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Leases;
public class LeasesFileService : ILeasesFileService public class LeasesFileService : ILeasesFileService
{ {
@@ -1,4 +1,6 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
/// <summary>Runs the configured dnsmasq reload command (e.g. systemctl reload dnsmasq).</summary> /// <summary>Runs the configured dnsmasq reload command (e.g. systemctl reload dnsmasq).</summary>
public interface IReloadService : IApplicationScopedService public interface IReloadService : IApplicationScopedService
@@ -1,9 +1,10 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Common.Process.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload;
public class ReloadService : IReloadService public class ReloadService : IReloadService
{ {
@@ -1,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
/// <summary> /// <summary>
/// Scoped orchestrator for effective-config edit lifecycle: edit mode, active field, pending changes, and apply. /// Scoped orchestrator for effective-config edit lifecycle: edit mode, active field, pending changes, and apply.
@@ -1,9 +1,10 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig.Abstractions; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.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).
@@ -1,7 +1,8 @@
using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig;
public sealed class EffectiveConfigEditSession : IEffectiveConfigEditSession public sealed class EffectiveConfigEditSession : IEffectiveConfigEditSession
{ {
@@ -3,10 +3,10 @@ using DnsmasqWebUI.Infrastructure.Helpers.Config;
using DnsmasqWebUI.Models.Dnsmasq; using DnsmasqWebUI.Models.Dnsmasq;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig.Abstractions; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.EffectiveConfig.Abstractions;
using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays; using DnsmasqWebUI.Components.Dnsmasq.EffectiveConfig.CustomDisplays;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.EffectiveConfig;
/// <summary> /// <summary>
/// Maps (sectionId, optionName) to custom effective-config value display component types and descriptor factories. /// Maps (sectionId, optionName) to custom effective-config value display component types and descriptor factories.
@@ -1,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
/// <summary> /// <summary>
/// Ring buffer of recent app log lines. Filled by ILoggerProvider, drained for SignalR push. /// Ring buffer of recent app log lines. Filled by ILoggerProvider, drained for SignalR push.
@@ -1,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
/// <summary> /// <summary>
/// Runs LogsCommand, diffs output, chunks by lines, and pushes via SignalR. /// Runs LogsCommand, diffs output, chunks by lines, and pushes via SignalR.
@@ -1,7 +1,9 @@
namespace DnsmasqWebUI.Infrastructure.Services; using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
/// <inheritdoc cref="Abstractions.IAppLogsBuffer" /> namespace DnsmasqWebUI.Infrastructure.Services.Logs;
public sealed class AppLogsBuffer : Abstractions.IAppLogsBuffer
/// <inheritdoc cref="IAppLogsBuffer" />
public sealed class AppLogsBuffer : IAppLogsBuffer
{ {
private const int MaxLines = 1000; private const int MaxLines = 1000;
private readonly List<string> _lines = []; private readonly List<string> _lines = [];
@@ -1,9 +1,10 @@
using System.Threading.Channels; using System.Threading.Channels;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Logs;
/// <summary> /// <summary>
/// Listens for signals from AppLogsLoggerProvider; drains pending app logs and pushes via LogsService. /// Listens for signals from AppLogsLoggerProvider; drains pending app logs and pushes via LogsService.
@@ -1,12 +1,12 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Logs;
/// <summary>Logs application start and stop with runtime and configuration context.</summary> /// <summary>Logs application start and stop with runtime and configuration context.</summary>
public sealed class ApplicationLifecycleLoggingHostedService : IApplicationHostedService public sealed class ApplicationLifecycleLoggingHostedService : IApplicationHostedService
@@ -1,9 +1,11 @@
using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig; using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Config.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Logs;
/// <summary> /// <summary>
/// When LogsPath is set in effective config, watches that file and triggers LogsService push on change. /// When LogsPath is set in effective config, watches that file and triggers LogsService push on change.
@@ -1,5 +1,6 @@
using System.Text; using System.Text;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Common.Process.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Logs.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Models.Logs; using DnsmasqWebUI.Models.Logs;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
@@ -7,7 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Logs;
/// <summary> /// <summary>
/// Runs LogsCommand, diffs against cache, chunks by lines, and pushes via SignalR. /// Runs LogsCommand, diffs against cache, chunks by lines, and pushes via SignalR.
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Registration.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,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Registration.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,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
/// <summary> /// <summary>
/// Marker interface for application services that are registered as scoped. /// Marker interface for application services that are registered as scoped.
@@ -1,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
/// <summary> /// <summary>
/// Marker interface for application services that are registered as singleton. /// Marker interface for application services that are registered as singleton.
@@ -1,6 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Models.Client; using DnsmasqWebUI.Models.Client;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.UI.Settings.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.
@@ -1,6 +1,6 @@
using DnsmasqWebUI.Models.Client; using DnsmasqWebUI.Models.Client;
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.UI.Settings.Abstractions;
/// <summary> /// <summary>
/// Scoped service to open the app-level settings modal from anywhere (e.g. NavMenu, pages). /// Scoped service to open the app-level settings modal from anywhere (e.g. NavMenu, pages).
@@ -1,10 +1,10 @@
using System.Text.Json; using System.Text.Json;
using DnsmasqWebUI.Extensions; using DnsmasqWebUI.Extensions;
using DnsmasqWebUI.Models.Client; using DnsmasqWebUI.Models.Client;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.UI.Settings.Abstractions;
using Microsoft.JSInterop; using Microsoft.JSInterop;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.UI.Settings;
/// <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.
@@ -1,7 +1,7 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.UI.Settings.Abstractions;
using DnsmasqWebUI.Models.Client; using DnsmasqWebUI.Models.Client;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.UI.Settings;
/// <inheritdoc cref="ISettingsModalService" /> /// <inheritdoc cref="ISettingsModalService" />
public sealed class SettingsModalService : ISettingsModalService public sealed class SettingsModalService : ISettingsModalService
@@ -1,4 +1,4 @@
namespace DnsmasqWebUI.Infrastructure.Services.Abstractions; namespace DnsmasqWebUI.Infrastructure.Services.Updates.Abstractions;
/// <summary> /// <summary>
/// Service that checks GitHub for a newer release. Runs on a configurable interval (hosted service) /// Service that checks GitHub for a newer release. Runs on a configurable interval (hosted service)
@@ -1,8 +1,9 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Registration.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Updates.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Updates;
/// <summary> /// <summary>
/// Runs an update check shortly after startup, then periodically at the configured interval. /// Runs an update check shortly after startup, then periodically at the configured interval.
@@ -1,11 +1,11 @@
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using DnsmasqWebUI.Extensions; using DnsmasqWebUI.Extensions;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Updates.Abstractions;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace DnsmasqWebUI.Infrastructure.Services; namespace DnsmasqWebUI.Infrastructure.Services.Updates;
public sealed class UpdateCheckService : IUpdateCheckService public sealed class UpdateCheckService : IUpdateCheckService
{ {
@@ -1,4 +1,4 @@
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.Dnsmasq.Reload.Abstractions;
namespace DnsmasqWebUI.Models.Dnsmasq; namespace DnsmasqWebUI.Models.Dnsmasq;
+4 -2
View File
@@ -3,8 +3,10 @@ using DnsmasqWebUI.Hubs;
using DnsmasqWebUI.Models.Config; using DnsmasqWebUI.Models.Config;
using DnsmasqWebUI.Extensions; using DnsmasqWebUI.Extensions;
using DnsmasqWebUI.Infrastructure.Helpers.Http; using DnsmasqWebUI.Infrastructure.Helpers.Http;
using DnsmasqWebUI.Infrastructure.Services; using DnsmasqWebUI.Infrastructure.Services.UI.Settings;
using DnsmasqWebUI.Infrastructure.Services.Abstractions; using DnsmasqWebUI.Infrastructure.Services.UI.Settings.Abstractions;
using DnsmasqWebUI.Infrastructure.Services.Updates;
using DnsmasqWebUI.Infrastructure.Services.Updates.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
// When not in Development, use the app's directory (not CWD) so static assets work when run via symlink or from any CWD. // When not in Development, use the app's directory (not CWD) so static assets work when run via symlink or from any CWD.