diff --git a/client/web/src/api.ts b/client/web/src/api.ts
index 95d3afc91..410a71d5e 100644
--- a/client/web/src/api.ts
+++ b/client/web/src/api.ts
@@ -119,3 +119,11 @@ type MetricType = "counter" | "gauge"
export type MetricName =
| "web_client_advertise_exitnode_enable"
| "web_client_advertise_exitnode_disable"
+ | "web_client_use_exitnode_enable"
+ | "web_client_use_exitnode_disable"
+ | "web_client_ssh_enable"
+ | "web_client_ssh_disable"
+ | "web_client_node_connect"
+ | "web_client_node_disconnect"
+ | "web_client_advertise_routes_change"
+ | "web_client_device_details_click"
diff --git a/client/web/src/components/views/device-details-view.tsx b/client/web/src/components/views/device-details-view.tsx
index d556b179e..83f431a4a 100644
--- a/client/web/src/components/views/device-details-view.tsx
+++ b/client/web/src/components/views/device-details-view.tsx
@@ -3,7 +3,7 @@
import cx from "classnames"
import React from "react"
-import { apiFetch } from "src/api"
+import { apiFetch, incrementMetric } from "src/api"
import ACLTag from "src/components/acl-tag"
import * as Control from "src/components/control-components"
import NiceIP from "src/components/nice-ip"
@@ -40,11 +40,13 @@ export default function DeviceDetailsView({
{!readonly && (
diff --git a/client/web/src/components/views/home-view.tsx b/client/web/src/components/views/home-view.tsx
index 30c431cb7..97eb9ebb2 100644
--- a/client/web/src/components/views/home-view.tsx
+++ b/client/web/src/components/views/home-view.tsx
@@ -3,6 +3,7 @@
import cx from "classnames"
import React, { useMemo } from "react"
+import { incrementMetric } from "src/api"
import { ReactComponent as ArrowRight } from "src/assets/icons/arrow-right.svg"
import { ReactComponent as Machine } from "src/assets/icons/machine.svg"
import AddressCard from "src/components/address-copy-card"
@@ -68,7 +69,11 @@ export default function HomeView({
disabled={readonly}
/>
)}
-
+ incrementMetric("web_client_device_details_click")}
+ >
View device details →
diff --git a/client/web/src/components/views/login-view.tsx b/client/web/src/components/views/login-view.tsx
index e18f7b03c..8537a662d 100644
--- a/client/web/src/components/views/login-view.tsx
+++ b/client/web/src/components/views/login-view.tsx
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
import React, { useCallback, useState } from "react"
-import { apiFetch } from "src/api"
+import { apiFetch, incrementMetric } from "src/api"
import { ReactComponent as TailscaleIcon } from "src/assets/icons/tailscale-icon.svg"
import { NodeData } from "src/hooks/node-data"
import Button from "src/ui/button"
@@ -25,7 +25,10 @@ export default function LoginView({
const login = useCallback(
(opt: TailscaleUpOptions) => {
- tailscaleUp(opt).then(refreshData)
+ tailscaleUp(opt).then(() => {
+ incrementMetric("web_client_node_connect")
+ refreshData()
+ })
},
[refreshData]
)
diff --git a/client/web/src/hooks/node-data.ts b/client/web/src/hooks/node-data.ts
index 3ad1646f2..f0237a6d1 100644
--- a/client/web/src/hooks/node-data.ts
+++ b/client/web/src/hooks/node-data.ts
@@ -148,9 +148,20 @@ export default function useNodeData() {
setIsPosting(false)
mutate() // refresh data after PATCH finishes
}
+ const updateMetrics = () => {
+ // only update metrics if values have changed
+ if (data?.RunningSSHServer !== d.RunSSH) {
+ incrementMetric(
+ d.RunSSH ? "web_client_ssh_enable" : "web_client_ssh_disable"
+ )
+ }
+ }
return apiFetch("/local/v0/prefs", "PATCH", d)
- .then(onComplete)
+ .then(() => {
+ updateMetrics()
+ onComplete()
+ })
.catch((err) => {
onComplete()
alert("Failed to update prefs")
@@ -176,6 +187,14 @@ export default function useNodeData() {
: "web_client_advertise_exitnode_disable"
)
}
+ // useExitNode is the ID of the exit node to use
+ if (data?.UsingExitNode?.ID !== d.UseExitNode) {
+ incrementMetric(
+ d.UseExitNode
+ ? "web_client_use_exitnode_enable"
+ : "web_client_use_exitnode_disable"
+ )
+ }
}
return apiFetch("/routes", "POST", d)
@@ -189,7 +208,7 @@ export default function useNodeData() {
throw err
})
},
- [mutate, data?.AdvertisingExitNode]
+ [mutate, data?.AdvertisingExitNode, data?.UsingExitNode?.ID]
)
const nodeUpdaters: NodeUpdaters = useMemo(
@@ -209,7 +228,7 @@ export default function useNodeData() {
AdvertiseRoutes: routes,
AdvertiseExitNode: data?.AdvertisingExitNode, // unchanged
UseExitNode: data?.UsingExitNode?.ID, // unchanged
- }),
+ }).then(() => incrementMetric("web_client_advertise_routes_change")),
}),
[
data?.AdvertisingExitNode,