|
|
@@ -1,232 +0,0 @@
|
|
|
-import cx from "classnames"
|
|
|
-import React from "react"
|
|
|
-import { apiFetch } from "src/api"
|
|
|
-import { NodeData, NodeUpdate } from "src/hooks/node-data"
|
|
|
-
|
|
|
-// TODO(tailscale/corp#13775): legacy.tsx contains a set of components
|
|
|
-// that (crudely) implement the pre-2023 web client. These are implemented
|
|
|
-// purely to ease migration to the new React-based web client, and will
|
|
|
-// eventually be completely removed.
|
|
|
-
|
|
|
-export default function LegacyClientView({
|
|
|
- data,
|
|
|
- refreshData,
|
|
|
- updateNode,
|
|
|
-}: {
|
|
|
- data: NodeData
|
|
|
- refreshData: () => void
|
|
|
- updateNode: (update: NodeUpdate) => void
|
|
|
-}) {
|
|
|
- return (
|
|
|
- <div className="container max-w-lg mx-auto mb-8 py-6 px-8 bg-white rounded-md shadow-2xl">
|
|
|
- <Header data={data} refreshData={refreshData} updateNode={updateNode} />
|
|
|
- <IP data={data} />
|
|
|
- {data.Status === "NeedsMachineAuth" ? (
|
|
|
- <div className="mb-4">
|
|
|
- This device is authorized, but needs approval from a network admin
|
|
|
- before it can connect to the network.
|
|
|
- </div>
|
|
|
- ) : (
|
|
|
- <>
|
|
|
- <div className="mb-4">
|
|
|
- <p>
|
|
|
- You are connected! Access this device over Tailscale using the
|
|
|
- device name or IP address above.
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- <button
|
|
|
- className={cx("button button-medium mb-4", {
|
|
|
- "button-red": data.AdvertiseExitNode,
|
|
|
- "button-blue": !data.AdvertiseExitNode,
|
|
|
- })}
|
|
|
- id="enabled"
|
|
|
- onClick={() =>
|
|
|
- updateNode({ AdvertiseExitNode: !data.AdvertiseExitNode })
|
|
|
- }
|
|
|
- >
|
|
|
- {data.AdvertiseExitNode
|
|
|
- ? "Stop advertising Exit Node"
|
|
|
- : "Advertise as Exit Node"}
|
|
|
- </button>
|
|
|
- </>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-export function Header({
|
|
|
- data,
|
|
|
- refreshData,
|
|
|
- updateNode,
|
|
|
-}: {
|
|
|
- data: NodeData
|
|
|
- refreshData: () => void
|
|
|
- updateNode: (update: NodeUpdate) => void
|
|
|
-}) {
|
|
|
- return (
|
|
|
- <header className="flex justify-between items-center min-width-0 py-2 mb-8">
|
|
|
- <svg
|
|
|
- width="26"
|
|
|
- height="26"
|
|
|
- viewBox="0 0 23 23"
|
|
|
- fill="none"
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
- className="flex-shrink-0 mr-4"
|
|
|
- >
|
|
|
- <circle
|
|
|
- opacity="0.2"
|
|
|
- cx="3.4"
|
|
|
- cy="3.25"
|
|
|
- r="2.7"
|
|
|
- fill="currentColor"
|
|
|
- ></circle>
|
|
|
- <circle cx="3.4" cy="11.3" r="2.7" fill="currentColor"></circle>
|
|
|
- <circle
|
|
|
- opacity="0.2"
|
|
|
- cx="3.4"
|
|
|
- cy="19.5"
|
|
|
- r="2.7"
|
|
|
- fill="currentColor"
|
|
|
- ></circle>
|
|
|
- <circle cx="11.5" cy="11.3" r="2.7" fill="currentColor"></circle>
|
|
|
- <circle cx="11.5" cy="19.5" r="2.7" fill="currentColor"></circle>
|
|
|
- <circle
|
|
|
- opacity="0.2"
|
|
|
- cx="11.5"
|
|
|
- cy="3.25"
|
|
|
- r="2.7"
|
|
|
- fill="currentColor"
|
|
|
- ></circle>
|
|
|
- <circle
|
|
|
- opacity="0.2"
|
|
|
- cx="19.5"
|
|
|
- cy="3.25"
|
|
|
- r="2.7"
|
|
|
- fill="currentColor"
|
|
|
- ></circle>
|
|
|
- <circle cx="19.5" cy="11.3" r="2.7" fill="currentColor"></circle>
|
|
|
- <circle
|
|
|
- opacity="0.2"
|
|
|
- cx="19.5"
|
|
|
- cy="19.5"
|
|
|
- r="2.7"
|
|
|
- fill="currentColor"
|
|
|
- ></circle>
|
|
|
- </svg>
|
|
|
- <div className="flex items-center justify-end space-x-2 w-2/3">
|
|
|
- {data.Profile &&
|
|
|
- data.Status !== "NoState" &&
|
|
|
- data.Status !== "NeedsLogin" && (
|
|
|
- <>
|
|
|
- <div className="text-right w-full leading-4">
|
|
|
- <h4 className="truncate leading-normal">
|
|
|
- {data.Profile.LoginName}
|
|
|
- </h4>
|
|
|
- <div className="text-xs text-gray-500 text-right">
|
|
|
- <button
|
|
|
- onClick={() => updateNode({ Reauthenticate: true })}
|
|
|
- className="hover:text-gray-700"
|
|
|
- >
|
|
|
- Switch account
|
|
|
- </button>{" "}
|
|
|
- |{" "}
|
|
|
- <button
|
|
|
- onClick={() => updateNode({ Reauthenticate: true })}
|
|
|
- className="hover:text-gray-700"
|
|
|
- >
|
|
|
- Reauthenticate
|
|
|
- </button>{" "}
|
|
|
- |{" "}
|
|
|
- <button
|
|
|
- onClick={() =>
|
|
|
- apiFetch("/local/v0/logout", "POST")
|
|
|
- .then(refreshData)
|
|
|
- .catch((err) => alert("Logout failed: " + err.message))
|
|
|
- }
|
|
|
- className="hover:text-gray-700"
|
|
|
- >
|
|
|
- Logout
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div className="relative flex-shrink-0 w-8 h-8 rounded-full overflow-hidden">
|
|
|
- {data.Profile.ProfilePicURL ? (
|
|
|
- <div
|
|
|
- className="w-8 h-8 flex pointer-events-none rounded-full bg-gray-200"
|
|
|
- style={{
|
|
|
- backgroundImage: `url(${data.Profile.ProfilePicURL})`,
|
|
|
- backgroundSize: "cover",
|
|
|
- }}
|
|
|
- />
|
|
|
- ) : (
|
|
|
- <div className="w-8 h-8 flex pointer-events-none rounded-full border border-gray-400 border-dashed" />
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </header>
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
-export function IP(props: { data: NodeData }) {
|
|
|
- const { data } = props
|
|
|
-
|
|
|
- if (!data.IP) {
|
|
|
- return null
|
|
|
- }
|
|
|
-
|
|
|
- return (
|
|
|
- <>
|
|
|
- <div className="border border-gray-200 bg-gray-50 rounded-md p-2 pl-3 pr-3 width-full flex items-center justify-between">
|
|
|
- <div className="flex items-center min-width-0">
|
|
|
- <svg
|
|
|
- className="flex-shrink-0 text-gray-600 mr-3 ml-1"
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
- width="20"
|
|
|
- height="20"
|
|
|
- viewBox="0 0 24 24"
|
|
|
- fill="none"
|
|
|
- stroke="currentColor"
|
|
|
- strokeWidth="2"
|
|
|
- strokeLinecap="round"
|
|
|
- strokeLinejoin="round"
|
|
|
- >
|
|
|
- <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
|
|
|
- <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
|
|
|
- <line x1="6" y1="6" x2="6.01" y2="6"></line>
|
|
|
- <line x1="6" y1="18" x2="6.01" y2="18"></line>
|
|
|
- </svg>
|
|
|
- <h4 className="font-semibold truncate mr-2">
|
|
|
- {data.DeviceName || "Your device"}
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
- <h5>{data.IP}</h5>
|
|
|
- </div>
|
|
|
- <p className="mt-1 ml-1 mb-6 text-xs text-gray-600">
|
|
|
- Debug info: Tailscale {data.IPNVersion}, tun={data.TUNMode.toString()}
|
|
|
- {data.IsSynology && (
|
|
|
- <>
|
|
|
- , DSM{data.DSMVersion}
|
|
|
- {data.TUNMode || (
|
|
|
- <>
|
|
|
- {" "}
|
|
|
- (
|
|
|
- <a
|
|
|
- href="https://tailscale.com/kb/1152/synology-outbound/"
|
|
|
- className="link-underline text-gray-600"
|
|
|
- target="_blank"
|
|
|
- aria-label="Configure outbound synology traffic"
|
|
|
- rel="noopener noreferrer"
|
|
|
- >
|
|
|
- outgoing access not configured
|
|
|
- </a>
|
|
|
- )
|
|
|
- </>
|
|
|
- )}
|
|
|
- </>
|
|
|
- )}
|
|
|
- </p>
|
|
|
- </>
|
|
|
- )
|
|
|
-}
|