diff --git a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css index 2f06ea9..56e7222 100644 --- a/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css +++ b/src/DnsmasqWebUI/Components/EffectiveConfig/Composition/EffectiveConfigSection.razor.css @@ -211,6 +211,11 @@ word-break: break-word; } +/* Row-level ok/cancel (and revert) for multi-value: right-align on the row below the list */ +::deep .ec-field .ec-field-value-multi + .status-badge-inline { + margin-left: auto; +} + ::deep .ec-field-value-list { margin: 0; padding-left: 1.25rem; diff --git a/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs b/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs index 113cc2a..447859b 100644 --- a/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs +++ b/src/DnsmasqWebUI/Infrastructure/Helpers/Config/EffectiveConfigOptionKindMap.cs @@ -286,7 +286,7 @@ public static class EffectiveConfigParserBehaviorMap [DnsmasqConfKeys.LogDhcp] = EffectiveConfigParserBehavior.Flag, [DnsmasqConfKeys.KeepInForeground] = EffectiveConfigParserBehavior.Flag, [DnsmasqConfKeys.NoDaemon] = EffectiveConfigParserBehavior.Flag, - [DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.LastWins, + [DnsmasqConfKeys.Conntrack] = EffectiveConfigParserBehavior.Flag, }; /// Returns LastWins, Flag, or Multi for the given option name; defaults to LastWins if unknown. diff --git a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs index 5f7644a..a9ea0e9 100644 --- a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs +++ b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/ConfigSetCache.cs @@ -391,8 +391,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable var dhcpScriptPath = string.IsNullOrWhiteSpace(dhcpScriptVal) ? null : DnsmasqConfIncludeParser.ResolvePath(dhcpScriptVal?.Trim(), dhcpScriptDir); var (mxTargetVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.MxTarget); var mxTarget = string.IsNullOrWhiteSpace(mxTargetVal) ? null : mxTargetVal.Trim(); - var (conntrackVal, _) = ((string?, string?))ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack); - var conntrack = string.IsNullOrWhiteSpace(conntrackVal) ? null : conntrackVal.Trim(); + var conntrack = (bool)ParseOptionValue(paths, pathToLines, DnsmasqConfKeys.Conntrack); return new EffectiveDnsmasqConfig( noHosts, addnHosts, hostsdirPath, @@ -535,7 +534,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable var (_, fastDnsRetrySource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.FastDnsRetry, managedFilePath); var (_, dhcpScriptPathSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.DhcpScript, managedFilePath); var (_, mxTargetSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.MxTarget, managedFilePath); - var (_, conntrackSource) = ((string?, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath); + var (_, conntrackSource) = ((bool, ConfigValueSource?))ParseOptionWithSource(paths, pathToLines, DnsmasqConfKeys.Conntrack, managedFilePath); return new EffectiveConfigSources( noHostsSource, addnHostsWithSource.Select(t => new PathWithSource(t.Path, t.Source)).ToList(), hostsdirPathSource, @@ -718,7 +717,7 @@ public sealed class ConfigSetCache : IConfigSetCache, IDisposable LogFacility: null, LogQueries: null, AuthTtl: null, EdnsPacketMax: null, QueryPort: null, PortLimit: null, MinPort: null, MaxPort: null, LogAsync: null, LocalService: null, DhcpLeaseMax: null, NegTtl: null, MaxTtl: null, MaxCacheTtl: null, MinCacheTtl: null, DhcpTtl: null, TftpRootPath: null, PxePrompt: null, EnableDbus: null, EnableUbus: null, FastDnsRetry: null, - DhcpScriptPath: null, MxTarget: null, Conntrack: null + DhcpScriptPath: null, MxTarget: null, Conntrack: false ); private static EffectiveConfigSources CreateDefaultEffectiveConfigSources() => diff --git a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs index 0780c50..391aef8 100644 --- a/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs +++ b/src/DnsmasqWebUI/Infrastructure/Services/Dnsmasq/Config/DnsmasqConfigService.cs @@ -271,13 +271,23 @@ public class DnsmasqConfigService : IDnsmasqConfigService if (idx >= 0) list.RemoveAt(idx); continue; } + if (!isFlag) + { + var v = ToConfValue(c.NewValue); + if (string.IsNullOrWhiteSpace(v)) + { + // Non-flag option with no value: remove existing line if any; never write key-only (malformed). + if (idx >= 0) list.RemoveAt(idx); + continue; + } + } string rawLine; if (isFlag) rawLine = confKey; else { - var v = ToConfValue(c.NewValue); - rawLine = string.IsNullOrEmpty(v) ? confKey : confKey + "=" + v; + var v = ToConfValue(c.NewValue).Trim(); + rawLine = confKey + "=" + v; } var newLine = new OtherLine { LineNumber = maxLineNumber + 1, RawLine = rawLine }; maxLineNumber++; diff --git a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs index 74e8e33..d9fd6ec 100644 --- a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs +++ b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/EffectiveConfigRenderFragmentRegistry.cs @@ -75,6 +75,7 @@ public class EffectiveConfigRenderFragmentRegistry : IEffectiveConfigRenderFragm RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.LogDhcp); RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.KeepInForeground); RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.NoDaemon); + RegisterFlag(EffectiveConfigFieldBuilder.SectionProcess, DnsmasqConfKeys.Conntrack); RegisterFlag(EffectiveConfigFieldBuilder.SectionDnssec, DnsmasqConfKeys.ProxyDnssec); // log-queries: dropdown (Off / On / extra / proto / auth). diff --git a/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs b/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs index 5ccd81d..d4823cb 100644 --- a/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs +++ b/src/DnsmasqWebUI/Models/Dnsmasq/EffectiveConfig/EffectiveDnsmasqConfig.cs @@ -139,7 +139,7 @@ public record EffectiveDnsmasqConfig( string? FastDnsRetry, string? DhcpScriptPath, string? MxTarget, - string? Conntrack + bool Conntrack ) { ///