mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-24 17:18:23 +03:00
fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included all Pydantic defaults. When the frontend merged these over the camera's base config, explicitly-set base values were lost. Now profile overrides are re-dumped with exclude_unset=True so only user-specified fields are returned. Also fixes the Save All path generating spurious deletion markers for restart-required fields that are hidden during profile editing but not excluded from the raw data sanitization in prepareSectionSavePayload.
This commit is contained in:
parent
091e0b80d2
commit
12e9bb3944
@ -158,6 +158,18 @@ def config(request: Request):
|
|||||||
for zone_name, zone in config_obj.cameras[camera_name].zones.items():
|
for zone_name, zone in config_obj.cameras[camera_name].zones.items():
|
||||||
camera_dict["zones"][zone_name]["color"] = zone.color
|
camera_dict["zones"][zone_name]["color"] = zone.color
|
||||||
|
|
||||||
|
# Re-dump profile overrides with exclude_unset so that only
|
||||||
|
# explicitly-set fields are returned (not Pydantic defaults).
|
||||||
|
# Without this, the frontend merges defaults (e.g. threshold=30)
|
||||||
|
# over the camera's actual base values (e.g. threshold=20).
|
||||||
|
if camera.profiles:
|
||||||
|
for profile_name, profile_config in camera.profiles.items():
|
||||||
|
camera_dict.setdefault("profiles", {})[profile_name] = (
|
||||||
|
profile_config.model_dump(
|
||||||
|
mode="json", warnings="none", exclude_unset=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# remove go2rtc stream passwords
|
# remove go2rtc stream passwords
|
||||||
go2rtc: dict[str, Any] = config_obj.go2rtc.model_dump(
|
go2rtc: dict[str, Any] = config_obj.go2rtc.model_dump(
|
||||||
mode="json", warnings="none", exclude_none=True
|
mode="json", warnings="none", exclude_none=True
|
||||||
@ -229,9 +241,7 @@ def set_profile(request: Request, body: ProfileSetBody):
|
|||||||
content={"success": False, "message": err},
|
content={"success": False, "message": err},
|
||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
request.app.dispatcher.publish(
|
request.app.dispatcher.publish("profile/state", body.profile or "none", retain=True)
|
||||||
"profile/state", body.profile or "none", retain=True
|
|
||||||
)
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={
|
content={
|
||||||
"success": True,
|
"success": True,
|
||||||
|
|||||||
@ -101,7 +101,9 @@ class ProfileManager:
|
|||||||
"""
|
"""
|
||||||
if profile_name is not None:
|
if profile_name is not None:
|
||||||
if profile_name not in self.config.profiles:
|
if profile_name not in self.config.profiles:
|
||||||
return f"Profile '{profile_name}' is not defined in the profiles section"
|
return (
|
||||||
|
f"Profile '{profile_name}' is not defined in the profiles section"
|
||||||
|
)
|
||||||
|
|
||||||
# Track which camera/section pairs get changed for ZMQ publishing
|
# Track which camera/section pairs get changed for ZMQ publishing
|
||||||
changed: dict[str, set[str]] = {}
|
changed: dict[str, set[str]] = {}
|
||||||
|
|||||||
@ -533,10 +533,21 @@ export function prepareSectionSavePayload(opts: {
|
|||||||
? {}
|
? {}
|
||||||
: rawSectionValue;
|
: rawSectionValue;
|
||||||
|
|
||||||
|
// For profile sections, also hide restart-required fields to match
|
||||||
|
// effectiveHiddenFields in BaseSection (prevents spurious deletion markers
|
||||||
|
// for fields that are hidden from the form during profile editing).
|
||||||
|
let hiddenFieldsForSanitize = sectionConfig.hiddenFields;
|
||||||
|
if (profileInfo.isProfile && sectionConfig.restartRequired?.length) {
|
||||||
|
const base = sectionConfig.hiddenFields ?? [];
|
||||||
|
hiddenFieldsForSanitize = [
|
||||||
|
...new Set([...base, ...sectionConfig.restartRequired]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize raw form data
|
// Sanitize raw form data
|
||||||
const rawData = sanitizeSectionData(
|
const rawData = sanitizeSectionData(
|
||||||
rawFormData as ConfigSectionData,
|
rawFormData as ConfigSectionData,
|
||||||
sectionConfig.hiddenFields,
|
hiddenFieldsForSanitize,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Compute schema defaults
|
// Compute schema defaults
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user