feat: asset-based image/video sources, notification sounds, UI improvements
Lint & Test / test (push) Has been cancelled
Lint & Test / test (push) Has been cancelled
- Replace URL-based image_source/url fields with image_asset_id/video_asset_id on StaticImagePictureSource and VideoCaptureSource (clean break, no migration) - Resolve asset IDs to file paths at runtime via AssetStore.get_file_path() - Add EntitySelect asset pickers for image/video in stream editor modal - Add notification sound configuration (global sound + per-app overrides) - Unify per-app color and sound overrides into single "Per-App Overrides" section - Persist notification history between server restarts - Add asset management system (upload, edit, delete, soft-delete) - Replace emoji buttons with SVG icons throughout UI - Various backend improvements: SQLite stores, auth, backup, MQTT, webhooks
This commit is contained in:
@@ -6,6 +6,7 @@ automations that have a webhook condition. No API-key auth is required —
|
||||
the secret token itself authenticates the caller.
|
||||
"""
|
||||
|
||||
import secrets
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
@@ -43,6 +44,12 @@ def _check_rate_limit(client_ip: str) -> None:
|
||||
)
|
||||
_rate_hits[client_ip].append(now)
|
||||
|
||||
# Periodic cleanup: remove IPs with no recent hits to prevent unbounded growth
|
||||
if len(_rate_hits) > 100:
|
||||
stale = [ip for ip, ts in _rate_hits.items() if not ts or ts[-1] < window_start]
|
||||
for ip in stale:
|
||||
del _rate_hits[ip]
|
||||
|
||||
|
||||
class WebhookPayload(BaseModel):
|
||||
action: str = Field(description="'activate' or 'deactivate'")
|
||||
@@ -68,7 +75,7 @@ async def handle_webhook(
|
||||
# Find the automation that owns this token
|
||||
for automation in store.get_all_automations():
|
||||
for condition in automation.conditions:
|
||||
if isinstance(condition, WebhookCondition) and condition.token == token:
|
||||
if isinstance(condition, WebhookCondition) and secrets.compare_digest(condition.token, token):
|
||||
active = body.action == "activate"
|
||||
await engine.set_webhook_state(token, active)
|
||||
logger.info(
|
||||
|
||||
Reference in New Issue
Block a user