fix logger and env vars config section saving

use escaped periods in keys to retain them in the config file (eg "frigate.embeddings")
This commit is contained in:
Josh Hawkins 2026-02-11 10:05:52 -06:00
parent cb231ffa66
commit 2ae972c068
2 changed files with 71 additions and 3 deletions

View File

@ -195,7 +195,8 @@ def flatten_config_data(
) -> Dict[str, Any]:
items = []
for key, value in config_data.items():
new_key = f"{parent_key}.{key}" if parent_key else key
escaped_key = escape_config_key_segment(str(key))
new_key = f"{parent_key}.{escaped_key}" if parent_key else escaped_key
if isinstance(value, dict):
items.extend(flatten_config_data(value, new_key).items())
else:
@ -203,6 +204,41 @@ def flatten_config_data(
return dict(items)
def escape_config_key_segment(segment: str) -> str:
"""Escape dots and backslashes so they can be treated as literal key chars."""
return segment.replace("\\", "\\\\").replace(".", "\\.")
def split_config_key_path(key_path_str: str) -> list[str]:
"""Split a dotted config path, honoring \\. as a literal dot in a key."""
parts: list[str] = []
current: list[str] = []
escaped = False
for char in key_path_str:
if escaped:
current.append(char)
escaped = False
continue
if char == "\\":
escaped = True
continue
if char == ".":
parts.append("".join(current))
current = []
continue
current.append(char)
if escaped:
current.append("\\")
parts.append("".join(current))
return parts
def update_yaml_file_bulk(file_path: str, updates: Dict[str, Any]):
yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
@ -218,7 +254,7 @@ def update_yaml_file_bulk(file_path: str, updates: Dict[str, Any]):
# Apply all updates
for key_path_str, new_value in updates.items():
key_path = key_path_str.split(".")
key_path = split_config_key_path(key_path_str)
for i in range(len(key_path)):
try:
index = int(key_path[i])

View File

@ -9,7 +9,7 @@
import { RJSFSchema } from "@rjsf/utils";
import { applySchemaDefaults } from "@/lib/config-schema";
import { isJsonObject } from "@/lib/utils";
import { JsonObject } from "@/types/configForm";
import { JsonObject, JsonValue } from "@/types/configForm";
/**
* Sections that require special handling at the global level.
@ -146,6 +146,22 @@ export function sanitizeOverridesForSection(
};
}
const flattenRecordWithDots = (
value: JsonObject,
prefix: string = "",
): JsonObject => {
const flattened: JsonObject = {};
Object.entries(value).forEach(([key, entry]) => {
const nextKey = prefix ? `${prefix}.${key}` : key;
if (isJsonObject(entry)) {
Object.assign(flattened, flattenRecordWithDots(entry, nextKey));
} else {
flattened[nextKey] = entry as JsonValue;
}
});
return flattened;
};
// detectors: Strip readonly model fields that are generated on startup
// and should never be persisted back to the config file.
if (sectionPath === "detectors") {
@ -167,5 +183,21 @@ export function sanitizeOverridesForSection(
return cleaned;
}
if (sectionPath === "logger") {
const overridesObj = overrides as JsonObject;
const logs = overridesObj.logs;
if (isJsonObject(logs)) {
return {
...overridesObj,
logs: flattenRecordWithDots(logs),
};
}
}
if (sectionPath === "environment_vars") {
const overridesObj = overrides as JsonObject;
return flattenRecordWithDots(overridesObj);
}
return overrides;
}