mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-30 02:10:35 +03:00
Show the analytics preview in telemetry settings
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Telemetry settings: the analytics preview shows the report Frigate would
|
||||
* send, fetched only when opened.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { test, expect } from "../../fixtures/frigate-test";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const CONFIG_SCHEMA = JSON.parse(
|
||||
readFileSync(
|
||||
resolve(__dirname, "../../fixtures/mock-data/config-schema.json"),
|
||||
"utf-8",
|
||||
),
|
||||
);
|
||||
|
||||
const PREVIEW = {
|
||||
schema_version: 1,
|
||||
install_id: "0".repeat(32),
|
||||
install: { version: "0.19.0-test" },
|
||||
};
|
||||
|
||||
test.describe("telemetry settings: analytics preview @medium", () => {
|
||||
test("shows the report on demand", async ({ frigateApp }) => {
|
||||
let requests = 0;
|
||||
const page = frigateApp.page;
|
||||
|
||||
await page.route("**/api/config/schema.json", (route) =>
|
||||
route.fulfill({ json: CONFIG_SCHEMA }),
|
||||
);
|
||||
await page.route("**/api/config/raw_paths", (route) =>
|
||||
route.fulfill({ json: { telemetry: {} } }),
|
||||
);
|
||||
await page.route("**/api/analytics/preview", (route) => {
|
||||
requests += 1;
|
||||
return route.fulfill({ json: PREVIEW });
|
||||
});
|
||||
|
||||
await frigateApp.goto("/settings?page=systemTelemetry");
|
||||
|
||||
const button = page.getByRole("button", { name: "Preview the report" });
|
||||
await expect(button).toBeVisible();
|
||||
expect(requests).toBe(0);
|
||||
|
||||
await button.click();
|
||||
|
||||
await expect(page.getByTestId("analytics-preview")).toContainText(
|
||||
'"schema_version": 1',
|
||||
);
|
||||
expect(requests).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -222,6 +222,10 @@
|
||||
"telemetry": {
|
||||
"label": "Telemetry",
|
||||
"description": "System telemetry and stats options including GPU and network bandwidth monitoring.",
|
||||
"analytics": {
|
||||
"label": "Share anonymous analytics",
|
||||
"description": "Send one anonymous usage report a day to help the Frigate maintainers decide what to support. Nothing is sent until this is on."
|
||||
},
|
||||
"network_interfaces": {
|
||||
"label": "Network interfaces",
|
||||
"description": "List of network interface name prefixes to monitor for bandwidth statistics."
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"analyticsPreview": {
|
||||
"show": "Preview the report",
|
||||
"desc": "Frigate sends this report once a day while sharing is on. It never includes camera names, zones, labels, or addresses.",
|
||||
"error": "Unable to build the preview"
|
||||
},
|
||||
"documentTitle": {
|
||||
"default": "Settings - Frigate",
|
||||
"authentication": "Authentication Settings - Frigate",
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
"object_processing_behind": "Tracked object processing could not keep up, so camera frames were dropped. Check CPU utilization, and whether CPU limits or pinning on the container or VM are leaving Frigate too little CPU",
|
||||
"failed_login_one": "Failed login attempt for {{user}}",
|
||||
"failed_login_other": "Failed login attempts for {{user}}",
|
||||
"update_available": "Frigate {{version}} is available"
|
||||
"update_available": "Frigate {{version}} is available",
|
||||
"analytics_prompt": "Share anonymous usage statistics to help improve Frigate"
|
||||
},
|
||||
"streamPrefix": "Stream {{index}}: {{message}}",
|
||||
"streamPrefixRestream": "Stream {{index}} (via go2rtc): {{message}}",
|
||||
|
||||
@@ -3,9 +3,17 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const telemetry: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/advanced/reference",
|
||||
fieldDocs: {
|
||||
analytics: "/configuration/advanced/analytics",
|
||||
},
|
||||
restartRequired: ["version_check"],
|
||||
fieldOrder: ["network_interfaces", "stats", "version_check"],
|
||||
fieldOrder: ["analytics", "network_interfaces", "stats", "version_check"],
|
||||
advancedFields: [],
|
||||
uiSchema: {
|
||||
analytics: {
|
||||
"ui:after": { render: "AnalyticsPreview" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
|
||||
export default function AnalyticsPreview() {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// built on demand, since building runs every collector on the server
|
||||
const { data, error } = useSWR<Record<string, unknown>>(
|
||||
open ? "analytics/preview" : null,
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
return (
|
||||
<Collapsible open={open} onOpenChange={setOpen} className="mt-2">
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="gap-1">
|
||||
{open ? (
|
||||
<LuChevronDown className="size-4" />
|
||||
) : (
|
||||
<LuChevronRight className="size-4" />
|
||||
)}
|
||||
{t("analyticsPreview.show")}
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
{t("analyticsPreview.desc")}
|
||||
</div>
|
||||
<CollapsibleContent className="mt-2">
|
||||
{error ? (
|
||||
<div className="text-sm text-danger">
|
||||
{t("analyticsPreview.error")}
|
||||
</div>
|
||||
) : data ? (
|
||||
<pre
|
||||
data-testid="analytics-preview"
|
||||
className="max-h-96 overflow-auto rounded-md bg-secondary p-3 text-xs"
|
||||
>
|
||||
{JSON.stringify(data, null, 2)}
|
||||
</pre>
|
||||
) : (
|
||||
<ActivityIndicator className="size-5" />
|
||||
)}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ComponentType } from "react";
|
||||
import AnalyticsPreview from "./AnalyticsPreview";
|
||||
import SemanticSearchReindex from "./SemanticSearchReindex.tsx";
|
||||
import CameraReviewStatusToggles from "./CameraReviewStatusToggles";
|
||||
import ProxyRoleMap from "./ProxyRoleMap";
|
||||
@@ -56,6 +57,9 @@ export const sectionRenderers: SectionRenderers = {
|
||||
birdseye: {
|
||||
BirdseyeCameraReorder,
|
||||
},
|
||||
telemetry: {
|
||||
AnalyticsPreview,
|
||||
},
|
||||
};
|
||||
|
||||
export default sectionRenderers;
|
||||
|
||||
@@ -662,6 +662,7 @@ export interface FrigateConfig {
|
||||
};
|
||||
|
||||
telemetry: {
|
||||
analytics: boolean;
|
||||
network_interfaces: string[];
|
||||
stats: {
|
||||
amd_gpu_stats: boolean;
|
||||
|
||||
Reference in New Issue
Block a user