mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-27 08:28:58 +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",
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
"shm_too_low": "/dev/shm allocation ({{total}} MB) should be increased to at least {{min}} MB",
|
||||
"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;
|
||||
|
||||
@@ -649,6 +649,7 @@ export interface FrigateConfig {
|
||||
};
|
||||
|
||||
telemetry: {
|
||||
analytics: boolean;
|
||||
network_interfaces: string[];
|
||||
stats: {
|
||||
amd_gpu_stats: boolean;
|
||||
|
||||
Reference in New Issue
Block a user