2023-11-28 18:15:19 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
2023-12-05 23:03:05 +00:00
|
|
|
import { SWRConfiguration } from "swr"
|
|
|
|
|
|
|
|
export const swrConfig: SWRConfiguration = {
|
|
|
|
fetcher: (url: string) => apiFetch(url, "GET"),
|
|
|
|
onError: (err, _) => console.error(err),
|
|
|
|
}
|
|
|
|
|
2023-08-16 22:52:31 +00:00
|
|
|
let csrfToken: string
|
2023-11-01 19:54:27 +00:00
|
|
|
let synoToken: string | undefined // required for synology API requests
|
2023-08-29 22:02:01 +00:00
|
|
|
let unraidCsrfToken: string | undefined // required for unraid POST requests (#8062)
|
2023-08-16 22:52:31 +00:00
|
|
|
|
2023-08-29 17:20:25 +00:00
|
|
|
// apiFetch wraps the standard JS fetch function with csrf header
|
|
|
|
// management and param additions specific to the web client.
|
|
|
|
//
|
|
|
|
// apiFetch adds the `api` prefix to the request URL,
|
|
|
|
// so endpoint should be provided without the `api` prefix
|
|
|
|
// (i.e. provide `/data` rather than `api/data`).
|
2023-12-05 23:03:05 +00:00
|
|
|
export function apiFetch<T>(
|
2023-08-29 17:20:25 +00:00
|
|
|
endpoint: string,
|
2023-11-13 19:54:24 +00:00
|
|
|
method: "GET" | "POST" | "PATCH",
|
2023-12-05 23:03:05 +00:00
|
|
|
body?: any
|
|
|
|
): Promise<T> {
|
2023-08-29 17:20:25 +00:00
|
|
|
const urlParams = new URLSearchParams(window.location.search)
|
2023-12-05 23:03:05 +00:00
|
|
|
const nextParams = new URLSearchParams()
|
2023-11-01 19:54:27 +00:00
|
|
|
if (synoToken) {
|
|
|
|
nextParams.set("SynoToken", synoToken)
|
|
|
|
} else {
|
|
|
|
const token = urlParams.get("SynoToken")
|
|
|
|
if (token) {
|
|
|
|
nextParams.set("SynoToken", token)
|
|
|
|
}
|
2023-08-29 17:20:25 +00:00
|
|
|
}
|
|
|
|
const search = nextParams.toString()
|
|
|
|
const url = `api${endpoint}${search ? `?${search}` : ""}`
|
|
|
|
|
2023-08-29 22:02:01 +00:00
|
|
|
var contentType: string
|
2023-08-31 21:27:41 +00:00
|
|
|
if (unraidCsrfToken && method === "POST") {
|
2023-08-29 22:02:01 +00:00
|
|
|
const params = new URLSearchParams()
|
|
|
|
params.append("csrf_token", unraidCsrfToken)
|
|
|
|
if (body) {
|
|
|
|
params.append("ts_data", JSON.stringify(body))
|
|
|
|
}
|
|
|
|
body = params.toString()
|
|
|
|
contentType = "application/x-www-form-urlencoded;charset=UTF-8"
|
|
|
|
} else {
|
|
|
|
body = body ? JSON.stringify(body) : undefined
|
|
|
|
contentType = "application/json"
|
|
|
|
}
|
|
|
|
|
2023-08-29 17:20:25 +00:00
|
|
|
return fetch(url, {
|
2023-08-29 22:02:01 +00:00
|
|
|
method: method,
|
|
|
|
headers: {
|
|
|
|
Accept: "application/json",
|
|
|
|
"Content-Type": contentType,
|
|
|
|
"X-CSRF-Token": csrfToken,
|
|
|
|
},
|
2023-12-05 23:03:05 +00:00
|
|
|
body: body,
|
2023-08-16 22:52:31 +00:00
|
|
|
})
|
2023-12-05 23:03:05 +00:00
|
|
|
.then((r) => {
|
|
|
|
updateCsrfToken(r)
|
|
|
|
if (!r.ok) {
|
|
|
|
return r.text().then((err) => {
|
|
|
|
throw new Error(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
})
|
|
|
|
.then((r) => {
|
|
|
|
if (r.headers.get("Content-Type") === "application/json") {
|
|
|
|
return r.json()
|
|
|
|
}
|
|
|
|
})
|
2023-08-16 22:52:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function updateCsrfToken(r: Response) {
|
|
|
|
const tok = r.headers.get("X-CSRF-Token")
|
|
|
|
if (tok) {
|
|
|
|
csrfToken = tok
|
|
|
|
}
|
|
|
|
}
|
2023-08-29 22:02:01 +00:00
|
|
|
|
2023-11-01 19:54:27 +00:00
|
|
|
export function setSynoToken(token?: string) {
|
|
|
|
synoToken = token
|
|
|
|
}
|
|
|
|
|
2023-08-29 22:02:01 +00:00
|
|
|
export function setUnraidCsrfToken(token?: string) {
|
|
|
|
unraidCsrfToken = token
|
|
|
|
}
|
2023-12-05 15:28:19 +00:00
|
|
|
|
|
|
|
// incrementMetric hits the client metrics local API endpoint to
|
|
|
|
// increment the given counter metric by one.
|
|
|
|
export function incrementMetric(metricName: MetricName) {
|
2023-12-06 17:20:44 +00:00
|
|
|
const postData: MetricsPOSTData[] = [
|
|
|
|
{
|
|
|
|
Name: metricName,
|
|
|
|
Type: "counter",
|
|
|
|
Value: 1,
|
|
|
|
},
|
|
|
|
]
|
2023-12-05 15:28:19 +00:00
|
|
|
|
2023-12-06 17:20:44 +00:00
|
|
|
apiFetch("/local/v0/upload-client-metrics", "POST", postData).catch(
|
|
|
|
(error) => {
|
|
|
|
console.error(error)
|
|
|
|
}
|
|
|
|
)
|
2023-12-05 15:28:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type MetricsPOSTData = {
|
|
|
|
Name: MetricName
|
|
|
|
Type: MetricType
|
|
|
|
Value: number
|
|
|
|
}
|
|
|
|
|
|
|
|
type MetricType = "counter" | "gauge"
|
|
|
|
|
|
|
|
export type MetricName =
|
|
|
|
| "web_client_advertise_exitnode_enable"
|
|
|
|
| "web_client_advertise_exitnode_disable"
|
2023-12-07 16:24:25 +00:00
|
|
|
| "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"
|