Add the analytics preview API

This commit is contained in:
Josh Hawkins
2026-09-22 08:22:45 -05:00
parent f0d070899f
commit 1834d9a22e
6 changed files with 100 additions and 2 deletions
+19
View File
@@ -10,6 +10,25 @@ servers:
- url: https://demo.frigate.video/api
- url: http://localhost:5001/api
paths:
/analytics/preview:
get:
tags:
- Analytics
summary: Get Analytics Preview
description: |-
**Access:** Admin role required.
Get the analytics report Frigate would send next, without sending it.
operationId: get_analytics_preview_analytics_preview_get
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
security:
- frigateAdminAuth: []
x-required-role: admin
/auth/first_time_login:
get:
tags:
+25
View File
@@ -0,0 +1,25 @@
"""Analytics APIs."""
import logging
from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse
from frigate.analytics.report import preview_report
from frigate.api.auth import require_role
from frigate.api.defs.tags import Tags
logger = logging.getLogger(__name__)
router = APIRouter(tags=[Tags.analytics])
@router.get("/analytics/preview", dependencies=[Depends(require_role(["admin"]))])
def get_analytics_preview(request: Request) -> JSONResponse:
"""Get the analytics report Frigate would send next, without sending it."""
report = preview_report(
request.app.frigate_config,
request.app.stats_emitter,
request.app.notice_registry,
)
return JSONResponse(content=report.model_dump(mode="json"))
+1
View File
@@ -2,6 +2,7 @@ from enum import Enum
class Tags(Enum):
analytics = "Analytics"
app = "App"
auth = "Auth"
camera = "Camera"
+3 -1
View File
@@ -12,8 +12,8 @@ from slowapi.middleware import SlowAPIMiddleware
from starlette_context import middleware, plugins
from starlette_context.plugins import Plugin
from frigate.api import app as main_app
from frigate.api import (
analytics,
auth,
camera,
chat,
@@ -30,6 +30,7 @@ from frigate.api import (
record,
review,
)
from frigate.api import app as main_app
from frigate.api.auth import get_jwt_secret, limiter, require_admin_by_default
from frigate.comms.dispatcher import Dispatcher
from frigate.comms.event_metadata_updater import (
@@ -140,6 +141,7 @@ def create_fastapi_app(
# Routes
# Order of include_router matters: https://fastapi.tiangolo.com/tutorial/path-params/#order-matters
app.include_router(analytics.router)
app.include_router(auth.router)
app.include_router(camera.router)
app.include_router(chat.router)
@@ -0,0 +1,49 @@
"""Tests for the analytics preview API."""
from unittest.mock import Mock, patch
from frigate.analytics import report
from frigate.analytics.collectors import hardware
from frigate.models import Notice, NoticeStats, User
from frigate.notices.registry import NoticeRegistry
from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp
class TestHttpAnalytics(BaseTestHttp):
def setUp(self):
super().setUp([Notice, NoticeStats, User])
stats = Mock()
stats.get_latest_stats.return_value = self.test_stats
self.app = self.create_app(stats=stats, notice_registry=NoticeRegistry())
for target, name, value in (
(report, "load_state", None),
(hardware.hardware_prober, "probe", []),
(hardware, "hwaccel_options", ("", [])),
):
patcher = patch.object(target, name, return_value=value)
patcher.start()
self.addCleanup(patcher.stop)
def test_preview_returns_the_report_without_sending_it(self):
with (
patch("frigate.analytics.transport.requests.post") as post,
AuthTestClient(self.app) as client,
):
response = client.get("/analytics/preview")
self.assertEqual(response.status_code, 200)
body = response.json()
self.assertEqual(body["install_id"], report.PREVIEW_INSTALL_ID)
self.assertEqual(body["schema_version"], 1)
self.assertEqual(body["cameras"]["total"], 1)
post.assert_not_called()
def test_preview_is_admin_only(self):
with AuthTestClient(self.app) as client:
response = client.get(
"/analytics/preview",
headers={"remote-user": "viewer", "remote-role": "viewer"},
)
self.assertEqual(response.status_code, 403)
+3 -1
View File
@@ -47,8 +47,8 @@ from fastapi.routing import APIRoute
from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import LiteralScalarString
from frigate.api import app as main_app
from frigate.api import (
analytics,
auth,
camera,
chat,
@@ -65,6 +65,7 @@ from frigate.api import (
record,
review,
)
from frigate.api import app as main_app
from frigate.api.auth import require_admin_by_default
logging.basicConfig(level=logging.INFO, format="%(message)s")
@@ -145,6 +146,7 @@ def build_app() -> FastAPI:
"""
app = FastAPI()
routers = [
analytics.router,
auth.router,
camera.router,
chat.router,