diff --git a/src/Backend/AutopilotMonitor.Functions/Services/GatherRuleService.cs b/src/Backend/AutopilotMonitor.Functions/Services/GatherRuleService.cs index d5f1b8a4..3d4560f5 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/GatherRuleService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/GatherRuleService.cs @@ -81,11 +81,20 @@ namespace AutopilotMonitor.Functions.Services /// Updates a gather rule. /// For built-in/community rules: only the enabled/disabled state is stored (per tenant). /// For custom rules: the full rule is updated in the tenant partition. + /// The rule type is determined from existing data (not the incoming payload) to prevent + /// partial payloads (e.g. toggle requests with only { enabled: bool }) from being + /// misclassified and overwriting rule data. /// public async Task UpdateRuleAsync(string tenantId, GatherRule rule) { - if (rule.IsBuiltIn || rule.IsCommunity) + // Always check global rules for type determination — the incoming payload + // may not include isBuiltIn/isCommunity (e.g. toggle requests). + var globalRules = await _storageService.GetGatherRulesAsync("global"); + var globalRule = globalRules.FirstOrDefault(r => r.RuleId == rule.RuleId); + + if (globalRule != null && (globalRule.IsBuiltIn || globalRule.IsCommunity)) { + // Built-in/community rule: only persist enabled state per tenant return await _storageService.StoreRuleStateAsync(tenantId, rule.RuleId, rule.Enabled); } diff --git a/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs b/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs index b51e5701..136d2dc4 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs @@ -239,7 +239,8 @@ namespace AutopilotMonitor.Functions.Services { "SendTraceEvents", config.SendTraceEvents }, { "EnableLocalAdminAnalyzer", config.EnableLocalAdminAnalyzer }, { "LocalAdminAllowedAccountsJson", config.LocalAdminAllowedAccountsJson }, - { "BootstrapTokenEnabled", config.BootstrapTokenEnabled } + { "BootstrapTokenEnabled", config.BootstrapTokenEnabled }, + { "UnrestrictedMode", config.UnrestrictedMode } }; return entity; @@ -297,7 +298,8 @@ namespace AutopilotMonitor.Functions.Services SendTraceEvents = entity.GetBoolean("SendTraceEvents") ?? true, EnableLocalAdminAnalyzer = entity.GetBoolean("EnableLocalAdminAnalyzer"), LocalAdminAllowedAccountsJson = entity.GetString("LocalAdminAllowedAccountsJson"), - BootstrapTokenEnabled = entity.GetBoolean("BootstrapTokenEnabled") ?? false + BootstrapTokenEnabled = entity.GetBoolean("BootstrapTokenEnabled") ?? false, + UnrestrictedMode = entity.GetBoolean("UnrestrictedMode") ?? false }; } } diff --git a/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx b/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx index a7858c70..954b0263 100644 --- a/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx +++ b/src/Web/autopilot-monitor-web/app/gather-rules/page.tsx @@ -136,7 +136,7 @@ export default function GatherRulesPage() { { method: "PUT", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ enabled: !rule.enabled }), + body: JSON.stringify({ enabled: !rule.enabled, isBuiltIn: rule.isBuiltIn, isCommunity: rule.isCommunity }), } ); if (result !== null) { @@ -295,6 +295,9 @@ export default function GatherRulesPage() { outputEventType: form.outputEventType, outputSeverity: form.outputSeverity, version: bumpVersion(rule.version), + enabled: rule.enabled, + author: rule.author, + createdAt: rule.createdAt, }; const result = await mutate(