mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 17:12:16 +03:00
Miscellaneous fixes (0.17 beta) (#21934)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
* improve chip tooltip display - use formatList to use i18n separators instead of commas - ensure the correct event type is used so sublabels are not run through normalization - remove smart-capitalization classes as translated labels use i18n (which includes capitalization) - give icons an optional key so that the console doesn't complain about duplication when rendering * Add grace period for recording segment checks to prevent spurious ffmpeg restarts * add admin precedence to proxy role_map resolution to prevent downgrade * clean up * formatting * work around radix pointer events issue when dialog is opened from drawer fixes https://github.com/blakeblackshear/frigate/discussions/21940 * prevent console warnings about missing titles and descriptions make these invisible with sr-only * remove duplicate language * Adjust handling for device sizes * Cleanup --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
co-authored by
Nicolas Mowen
parent
e1005ac2a5
commit
67e3f8eefa
+11
-4
@@ -439,10 +439,11 @@ def resolve_role(
|
||||
Determine the effective role for a request based on proxy headers and configuration.
|
||||
|
||||
Order of resolution:
|
||||
1. If a role header is defined in proxy_config.header_map.role:
|
||||
- If a role_map is configured, treat the header as group claims
|
||||
(split by proxy_config.separator) and map to roles.
|
||||
- If no role_map is configured, treat the header as role names directly.
|
||||
1. If a role header is defined in proxy_config.header_map.role:
|
||||
- If a role_map is configured, treat the header as group claims
|
||||
(split by proxy_config.separator) and map to roles.
|
||||
Admin matches short-circuit to admin.
|
||||
- If no role_map is configured, treat the header as role names directly.
|
||||
2. If no valid role is found, return proxy_config.default_role if it's valid in config_roles, else 'viewer'.
|
||||
|
||||
Args:
|
||||
@@ -492,6 +493,12 @@ def resolve_role(
|
||||
}
|
||||
logger.debug("Matched roles from role_map: %s", matched_roles)
|
||||
|
||||
# If admin matches, prioritize it to avoid accidental downgrade when
|
||||
# users belong to both admin and lower-privilege groups.
|
||||
if "admin" in matched_roles and "admin" in config_roles:
|
||||
logger.debug("Resolved role (with role_map) to 'admin'.")
|
||||
return "admin"
|
||||
|
||||
if matched_roles:
|
||||
resolved = next(
|
||||
(r for r in config_roles if r in matched_roles), validated_default
|
||||
|
||||
@@ -31,6 +31,21 @@ class TestProxyRoleResolution(unittest.TestCase):
|
||||
role = resolve_role(headers, self.proxy_config, self.config_roles)
|
||||
self.assertEqual(role, "admin")
|
||||
|
||||
def test_role_map_or_matching(self):
|
||||
config = self.proxy_config
|
||||
config.header_map.role_map = {
|
||||
"admin": ["group_admin", "group_privileged"],
|
||||
}
|
||||
|
||||
# OR semantics: a single matching group should map to the role
|
||||
headers = {"x-remote-role": "group_admin"}
|
||||
role = resolve_role(headers, config, self.config_roles)
|
||||
self.assertEqual(role, "admin")
|
||||
|
||||
headers = {"x-remote-role": "group_admin|group_privileged"}
|
||||
role = resolve_role(headers, config, self.config_roles)
|
||||
self.assertEqual(role, "admin")
|
||||
|
||||
def test_direct_role_header_with_separator(self):
|
||||
config = self.proxy_config
|
||||
config.header_map.role_map = None # disable role_map
|
||||
|
||||
+21
-3
@@ -214,6 +214,7 @@ class CameraWatchdog(threading.Thread):
|
||||
self.latest_valid_segment_time: float = 0
|
||||
self.latest_invalid_segment_time: float = 0
|
||||
self.latest_cache_segment_time: float = 0
|
||||
self.record_enable_time: datetime | None = None
|
||||
|
||||
def _update_enabled_state(self) -> bool:
|
||||
"""Fetch the latest config and update enabled state."""
|
||||
@@ -261,6 +262,9 @@ class CameraWatchdog(threading.Thread):
|
||||
def run(self) -> None:
|
||||
if self._update_enabled_state():
|
||||
self.start_all_ffmpeg()
|
||||
# If recording is enabled at startup, set the grace period timer
|
||||
if self.config.record.enabled:
|
||||
self.record_enable_time = datetime.now().astimezone(timezone.utc)
|
||||
|
||||
time.sleep(self.sleeptime)
|
||||
while not self.stop_event.wait(self.sleeptime):
|
||||
@@ -270,13 +274,15 @@ class CameraWatchdog(threading.Thread):
|
||||
self.logger.debug(f"Enabling camera {self.config.name}")
|
||||
self.start_all_ffmpeg()
|
||||
|
||||
# reset all timestamps
|
||||
# reset all timestamps and record the enable time for grace period
|
||||
self.latest_valid_segment_time = 0
|
||||
self.latest_invalid_segment_time = 0
|
||||
self.latest_cache_segment_time = 0
|
||||
self.record_enable_time = datetime.now().astimezone(timezone.utc)
|
||||
else:
|
||||
self.logger.debug(f"Disabling camera {self.config.name}")
|
||||
self.stop_all_ffmpeg()
|
||||
self.record_enable_time = None
|
||||
|
||||
# update camera status
|
||||
self.requestor.send_data(
|
||||
@@ -361,6 +367,12 @@ class CameraWatchdog(threading.Thread):
|
||||
if self.config.record.enabled and "record" in p["roles"]:
|
||||
now_utc = datetime.now().astimezone(timezone.utc)
|
||||
|
||||
# Check if we're within the grace period after enabling recording
|
||||
# Grace period: 90 seconds allows time for ffmpeg to start and create first segment
|
||||
in_grace_period = self.record_enable_time is not None and (
|
||||
now_utc - self.record_enable_time
|
||||
) < timedelta(seconds=90)
|
||||
|
||||
latest_cache_dt = (
|
||||
datetime.fromtimestamp(
|
||||
self.latest_cache_segment_time, tz=timezone.utc
|
||||
@@ -386,10 +398,16 @@ class CameraWatchdog(threading.Thread):
|
||||
)
|
||||
|
||||
# ensure segments are still being created and that they have valid video data
|
||||
cache_stale = now_utc > (latest_cache_dt + timedelta(seconds=120))
|
||||
valid_stale = now_utc > (latest_valid_dt + timedelta(seconds=120))
|
||||
# Skip checks during grace period to allow segments to start being created
|
||||
cache_stale = not in_grace_period and now_utc > (
|
||||
latest_cache_dt + timedelta(seconds=120)
|
||||
)
|
||||
valid_stale = not in_grace_period and now_utc > (
|
||||
latest_valid_dt + timedelta(seconds=120)
|
||||
)
|
||||
invalid_stale_condition = (
|
||||
self.latest_invalid_segment_time > 0
|
||||
and not in_grace_period
|
||||
and now_utc > (latest_invalid_dt + timedelta(seconds=120))
|
||||
and self.latest_valid_segment_time
|
||||
<= self.latest_invalid_segment_time
|
||||
|
||||
Reference in New Issue
Block a user