2023-08-16 18:52:31 -04:00
|
|
|
import { useCallback, useEffect, useState } from "react"
|
2023-08-29 18:02:01 -04:00
|
|
|
import { apiFetch, setUnraidCsrfToken } from "src/api"
|
2023-08-15 08:24:32 -07:00
|
|
|
|
|
|
|
export type NodeData = {
|
|
|
|
Profile: UserProfile
|
2023-11-08 17:33:27 -05:00
|
|
|
Status: NodeState
|
2023-08-15 08:24:32 -07:00
|
|
|
DeviceName: string
|
2023-11-08 17:33:27 -05:00
|
|
|
OS: string
|
2023-08-15 08:24:32 -07:00
|
|
|
IP: string
|
2023-11-08 17:33:27 -05:00
|
|
|
IPv6: string
|
|
|
|
ID: string
|
|
|
|
KeyExpiry: string
|
|
|
|
KeyExpired: boolean
|
2023-08-15 08:24:32 -07:00
|
|
|
AdvertiseExitNode: boolean
|
|
|
|
AdvertiseRoutes: string
|
|
|
|
LicensesURL: string
|
|
|
|
TUNMode: boolean
|
|
|
|
IsSynology: boolean
|
|
|
|
DSMVersion: number
|
|
|
|
IsUnraid: boolean
|
|
|
|
UnraidToken: string
|
|
|
|
IPNVersion: string
|
2023-11-07 11:11:33 -08:00
|
|
|
URLPrefix: string
|
2023-11-09 16:19:22 -05:00
|
|
|
DomainName: string
|
2023-11-08 17:33:27 -05:00
|
|
|
TailnetName: string
|
|
|
|
IsTagged: boolean
|
|
|
|
Tags: string[]
|
2023-11-13 14:54:24 -05:00
|
|
|
RunningSSHServer: boolean
|
2023-09-26 15:57:40 -04:00
|
|
|
|
|
|
|
DebugMode: "" | "login" | "full" // empty when not running in any debug mode
|
2023-08-15 08:24:32 -07:00
|
|
|
}
|
|
|
|
|
2023-11-08 17:33:27 -05:00
|
|
|
type NodeState =
|
|
|
|
| "NoState"
|
|
|
|
| "NeedsLogin"
|
|
|
|
| "NeedsMachineAuth"
|
|
|
|
| "Stopped"
|
|
|
|
| "Starting"
|
|
|
|
| "Running"
|
|
|
|
|
2023-08-15 11:38:13 -04:00
|
|
|
export type UserProfile = {
|
|
|
|
LoginName: string
|
|
|
|
DisplayName: string
|
|
|
|
ProfilePicURL: string
|
2023-08-15 08:24:32 -07:00
|
|
|
}
|
|
|
|
|
2023-08-16 18:52:31 -04:00
|
|
|
export type NodeUpdate = {
|
|
|
|
AdvertiseRoutes?: string
|
|
|
|
AdvertiseExitNode?: boolean
|
|
|
|
Reauthenticate?: boolean
|
|
|
|
ForceLogout?: boolean
|
|
|
|
}
|
|
|
|
|
2023-11-13 14:54:24 -05:00
|
|
|
export type PrefsUpdate = {
|
|
|
|
RunSSHSet?: boolean
|
|
|
|
RunSSH?: boolean
|
|
|
|
}
|
|
|
|
|
2023-08-15 08:24:32 -07:00
|
|
|
// useNodeData returns basic data about the current node.
|
|
|
|
export default function useNodeData() {
|
2023-08-15 11:38:13 -04:00
|
|
|
const [data, setData] = useState<NodeData>()
|
2023-08-16 18:52:31 -04:00
|
|
|
const [isPosting, setIsPosting] = useState<boolean>(false)
|
2023-08-15 11:38:13 -04:00
|
|
|
|
2023-08-28 16:44:48 -04:00
|
|
|
const refreshData = useCallback(
|
2023-08-29 13:20:25 -04:00
|
|
|
() =>
|
2023-08-29 18:02:01 -04:00
|
|
|
apiFetch("/data", "GET")
|
2023-08-29 13:20:25 -04:00
|
|
|
.then((r) => r.json())
|
2023-08-29 18:02:01 -04:00
|
|
|
.then((d: NodeData) => {
|
|
|
|
setData(d)
|
|
|
|
setUnraidCsrfToken(d.IsUnraid ? d.UnraidToken : undefined)
|
|
|
|
})
|
2023-08-29 13:20:25 -04:00
|
|
|
.catch((error) => console.error(error)),
|
|
|
|
[setData]
|
|
|
|
)
|
2023-08-16 18:52:31 -04:00
|
|
|
|
|
|
|
const updateNode = useCallback(
|
|
|
|
(update: NodeUpdate) => {
|
|
|
|
// The contents of this function are mostly copied over
|
|
|
|
// from the legacy client's web.html file.
|
|
|
|
// It makes all data updates through one API endpoint.
|
|
|
|
// As we build out the web client in React,
|
|
|
|
// this endpoint will eventually be deprecated.
|
|
|
|
|
|
|
|
if (isPosting || !data) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
setIsPosting(true)
|
|
|
|
|
|
|
|
update = {
|
2023-08-22 11:44:36 -04:00
|
|
|
...update,
|
2023-08-16 18:52:31 -04:00
|
|
|
// Default to current data value for any unset fields.
|
|
|
|
AdvertiseRoutes:
|
|
|
|
update.AdvertiseRoutes !== undefined
|
|
|
|
? update.AdvertiseRoutes
|
|
|
|
: data.AdvertiseRoutes,
|
|
|
|
AdvertiseExitNode:
|
|
|
|
update.AdvertiseExitNode !== undefined
|
|
|
|
? update.AdvertiseExitNode
|
|
|
|
: data.AdvertiseExitNode,
|
|
|
|
}
|
|
|
|
|
2023-11-07 11:16:23 -05:00
|
|
|
return apiFetch("/data", "POST", update, { up: "true" })
|
2023-08-16 18:52:31 -04:00
|
|
|
.then((r) => r.json())
|
|
|
|
.then((r) => {
|
|
|
|
setIsPosting(false)
|
|
|
|
const err = r["error"]
|
|
|
|
if (err) {
|
|
|
|
throw new Error(err)
|
|
|
|
}
|
|
|
|
const url = r["url"]
|
|
|
|
if (url) {
|
2023-08-23 17:17:52 -04:00
|
|
|
window.open(url, "_blank")
|
2023-08-16 18:52:31 -04:00
|
|
|
}
|
2023-08-28 16:44:48 -04:00
|
|
|
refreshData()
|
2023-08-16 18:52:31 -04:00
|
|
|
})
|
2023-11-07 11:16:23 -05:00
|
|
|
.catch((err) => {
|
2023-11-13 14:54:24 -05:00
|
|
|
setIsPosting(false)
|
2023-11-07 11:16:23 -05:00
|
|
|
alert("Failed operation: " + err.message)
|
|
|
|
throw err
|
|
|
|
})
|
2023-08-16 18:52:31 -04:00
|
|
|
},
|
|
|
|
[data]
|
|
|
|
)
|
|
|
|
|
2023-11-13 14:54:24 -05:00
|
|
|
const updatePrefs = useCallback(
|
|
|
|
(p: PrefsUpdate) => {
|
|
|
|
setIsPosting(true)
|
|
|
|
if (data) {
|
|
|
|
const optimisticUpdates = data
|
|
|
|
if (p.RunSSHSet) {
|
|
|
|
optimisticUpdates.RunningSSHServer = Boolean(p.RunSSH)
|
|
|
|
}
|
|
|
|
// Reflect the pref change immediatley on the frontend,
|
|
|
|
// then make the prefs PATCH. If the request fails,
|
|
|
|
// data will be updated to it's previous value in
|
|
|
|
// onComplete below.
|
|
|
|
setData(optimisticUpdates)
|
|
|
|
}
|
|
|
|
|
|
|
|
const onComplete = () => {
|
|
|
|
setIsPosting(false)
|
|
|
|
refreshData() // refresh data after PATCH finishes
|
|
|
|
}
|
|
|
|
|
|
|
|
return apiFetch("/local/v0/prefs", "PATCH", p)
|
|
|
|
.then(onComplete)
|
|
|
|
.catch(() => {
|
|
|
|
onComplete()
|
|
|
|
alert("Failed to update prefs")
|
|
|
|
})
|
|
|
|
},
|
|
|
|
[setIsPosting, refreshData, setData, data]
|
|
|
|
)
|
|
|
|
|
2023-08-16 18:52:31 -04:00
|
|
|
useEffect(
|
2023-08-24 11:18:38 -04:00
|
|
|
() => {
|
|
|
|
// Initial data load.
|
2023-08-28 16:44:48 -04:00
|
|
|
refreshData()
|
2023-08-24 11:18:38 -04:00
|
|
|
|
|
|
|
// Refresh on browser tab focus.
|
|
|
|
const onVisibilityChange = () => {
|
2023-08-28 16:44:48 -04:00
|
|
|
document.visibilityState === "visible" && refreshData()
|
2023-08-24 11:18:38 -04:00
|
|
|
}
|
|
|
|
window.addEventListener("visibilitychange", onVisibilityChange)
|
|
|
|
return () => {
|
|
|
|
// Cleanup browser tab listener.
|
|
|
|
window.removeEventListener("visibilitychange", onVisibilityChange)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Run once.
|
2023-08-16 18:52:31 -04:00
|
|
|
[]
|
|
|
|
)
|
2023-08-15 11:38:13 -04:00
|
|
|
|
2023-11-13 14:54:24 -05:00
|
|
|
return { data, refreshData, updateNode, updatePrefs, isPosting }
|
2023-08-15 08:24:32 -07:00
|
|
|
}
|