mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-03 02:21:58 +00:00
client/web: add Tailscale SSH view
Updates tailscale/corp#14335 Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:
committed by
Sonia Appasamy
parent
103c00a175
commit
c9bfb7c683
@@ -5,6 +5,7 @@ import DeviceDetailsView from "src/components/views/device-details-view"
|
||||
import HomeView from "src/components/views/home-view"
|
||||
import LegacyClientView from "src/components/views/legacy-client-view"
|
||||
import LoginClientView from "src/components/views/login-client-view"
|
||||
import SSHView from "src/components/views/ssh-view"
|
||||
import useAuth, { AuthResponse } from "src/hooks/auth"
|
||||
import useNodeData, { NodeData } from "src/hooks/node-data"
|
||||
import { ReactComponent as TailscaleIcon } from "src/icons/tailscale-icon.svg"
|
||||
@@ -31,7 +32,7 @@ function WebClient({
|
||||
auth: AuthResponse
|
||||
newSession: () => Promise<void>
|
||||
}) {
|
||||
const { data, refreshData, updateNode } = useNodeData()
|
||||
const { data, refreshData, updateNode, updatePrefs } = useNodeData()
|
||||
useEffect(() => {
|
||||
refreshData()
|
||||
}, [auth, refreshData])
|
||||
@@ -72,7 +73,13 @@ function WebClient({
|
||||
<DeviceDetailsView readonly={!auth.canManageNode} node={data} />
|
||||
</Route>
|
||||
<Route path="/subnets">{/* TODO */}Subnet router</Route>
|
||||
<Route path="/ssh">{/* TODO */}Tailscale SSH server</Route>
|
||||
<Route path="/ssh">
|
||||
<SSHView
|
||||
readonly={!auth.canManageNode}
|
||||
runningSSH={data.RunningSSHServer}
|
||||
updatePrefs={updatePrefs}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/serve">{/* TODO */}Share local content</Route>
|
||||
<Route>
|
||||
<h2 className="mt-8">Page not found</h2>
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function DeviceDetailsView({
|
||||
const [, setLocation] = useLocation()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<h1 className="mb-10">Device details</h1>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="card">
|
||||
@@ -123,6 +123,6 @@ export default function DeviceDetailsView({
|
||||
in the admin console.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,14 @@ export default function HomeView({
|
||||
className="mb-3"
|
||||
title="Tailscale SSH server"
|
||||
body="Run a Tailscale SSH server on this device and allow other devices in your tailnet to SSH into it."
|
||||
badge={
|
||||
node.RunningSSHServer
|
||||
? {
|
||||
text: "Running",
|
||||
icon: <div className="w-2 h-2 bg-emerald-500 rounded-full" />,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<SettingsCard
|
||||
link="/serve"
|
||||
@@ -71,11 +79,16 @@ function SettingsCard({
|
||||
title,
|
||||
link,
|
||||
body,
|
||||
badge,
|
||||
className,
|
||||
}: {
|
||||
title: string
|
||||
link: string
|
||||
body: string
|
||||
badge?: {
|
||||
text: string
|
||||
icon?: JSX.Element
|
||||
}
|
||||
className?: string
|
||||
}) {
|
||||
return (
|
||||
@@ -87,9 +100,19 @@ function SettingsCard({
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<p className="text-neutral-800 font-medium leading-tight mb-2">
|
||||
{title}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<p className="text-neutral-800 font-medium leading-tight mb-2">
|
||||
{title}
|
||||
</p>
|
||||
{badge && (
|
||||
<div className="h-5 px-2 bg-stone-100 rounded-full flex items-center gap-2">
|
||||
{badge.icon}
|
||||
<div className="text-neutral-500 text-xs font-medium">
|
||||
{badge.text}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-neutral-500 text-sm leading-tight">{body}</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
51
client/web/src/components/views/ssh-view.tsx
Normal file
51
client/web/src/components/views/ssh-view.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from "react"
|
||||
import { PrefsUpdate } from "src/hooks/node-data"
|
||||
import Toggle from "src/ui/toggle"
|
||||
|
||||
export default function SSHView({
|
||||
readonly,
|
||||
runningSSH,
|
||||
updatePrefs,
|
||||
}: {
|
||||
readonly: boolean
|
||||
runningSSH: boolean
|
||||
updatePrefs: (p: PrefsUpdate) => Promise<void>
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<h1 className="mb-1">Tailscale SSH server</h1>
|
||||
<p className="description mb-10">
|
||||
Run a Tailscale SSH server on this device and allow other devices in
|
||||
your tailnet to SSH into it.{" "}
|
||||
<a
|
||||
href="https://tailscale.com/kb/1193/tailscale-ssh/"
|
||||
className="text-indigo-700"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more →
|
||||
</a>
|
||||
</p>
|
||||
<div className="-mx-5 px-4 py-3 bg-white rounded-lg border border-gray-200 flex gap-2.5 mb-3">
|
||||
<Toggle
|
||||
checked={runningSSH}
|
||||
onChange={() => updatePrefs({ RunSSHSet: true, RunSSH: !runningSSH })}
|
||||
disabled={readonly}
|
||||
/>
|
||||
<div className="text-black text-sm font-medium leading-tight">
|
||||
Run Tailscale SSH server
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-neutral-500 text-sm leading-tight">
|
||||
Remember to make sure that the{" "}
|
||||
<a
|
||||
href="https://login.tailscale.com/admin/acls/"
|
||||
className="text-indigo-700"
|
||||
target="_blank"
|
||||
>
|
||||
tailnet policy file
|
||||
</a>{" "}
|
||||
allows other devices to SSH into this device.
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user