mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
[dashboard] support ssh copy-paste with ssh keys
This commit is contained in:
parent
28f9ddc77c
commit
924b20eaee
@ -13,6 +13,7 @@ export default function Modal(props: {
|
|||||||
// specify a key if having the same title and window.location
|
// specify a key if having the same title and window.location
|
||||||
specify?: string;
|
specify?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
hideDivider?: boolean;
|
||||||
buttons?: React.ReactChild[] | React.ReactChild;
|
buttons?: React.ReactChild[] | React.ReactChild;
|
||||||
children: React.ReactChild[] | React.ReactChild;
|
children: React.ReactChild[] | React.ReactChild;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@ -94,7 +95,12 @@ export default function Modal(props: {
|
|||||||
{props.title ? (
|
{props.title ? (
|
||||||
<>
|
<>
|
||||||
<h3 className="pb-2">{props.title}</h3>
|
<h3 className="pb-2">{props.title}</h3>
|
||||||
<div className="border-t border-b border-gray-200 dark:border-gray-800 mt-2 -mx-6 px-6 py-4">
|
<div
|
||||||
|
className={
|
||||||
|
"border-gray-200 dark:border-gray-800 -mx-6 px-6 " +
|
||||||
|
(props.hideDivider ? "" : "border-t border-b mt-2 py-4")
|
||||||
|
}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end mt-6 space-x-2">{props.buttons}</div>
|
<div className="flex justify-end mt-6 space-x-2">{props.buttons}</div>
|
||||||
|
|||||||
@ -4,12 +4,14 @@
|
|||||||
* See License-AGPL.txt in the project root for license information.
|
* See License-AGPL.txt in the project root for license information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
import Tooltip from "../components/Tooltip";
|
import Tooltip from "../components/Tooltip";
|
||||||
import copy from "../images/copy.svg";
|
import copy from "../images/copy.svg";
|
||||||
import AlertBox from "../components/AlertBox";
|
import Alert from "../components/Alert";
|
||||||
import InfoBox from "../components/InfoBox";
|
import TabMenuItem from "../components/TabMenuItem";
|
||||||
|
import { settingsPathSSHKeys } from "../settings/settings.routes";
|
||||||
|
import { getGitpodService } from "../service/service";
|
||||||
|
|
||||||
function InputWithCopy(props: { value: string; tip?: string; className?: string }) {
|
function InputWithCopy(props: { value: string; tip?: string; className?: string }) {
|
||||||
const [copied, setCopied] = useState<boolean>(false);
|
const [copied, setCopied] = useState<boolean>(false);
|
||||||
@ -35,7 +37,7 @@ function InputWithCopy(props: { value: string; tip?: string; className?: string
|
|||||||
autoFocus
|
autoFocus
|
||||||
className="w-full pr-8 overscroll-none"
|
className="w-full pr-8 overscroll-none"
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={props.value}
|
value={props.value}
|
||||||
/>
|
/>
|
||||||
<div className="cursor-pointer" onClick={() => copyToClipboard(props.value)}>
|
<div className="cursor-pointer" onClick={() => copyToClipboard(props.value)}>
|
||||||
<div className="absolute top-1/3 right-3">
|
<div className="absolute top-1/3 right-3">
|
||||||
@ -55,40 +57,80 @@ interface SSHProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SSHView(props: SSHProps) {
|
function SSHView(props: SSHProps) {
|
||||||
const sshCommand = `ssh '${props.workspaceId}#${props.ownerToken}@${props.ideUrl.replace(
|
const [hasSSHKey, setHasSSHKey] = useState(true);
|
||||||
props.workspaceId,
|
const [selectSSHKey, setSelectSSHKey] = useState(true);
|
||||||
props.workspaceId + ".ssh",
|
|
||||||
)}'`;
|
useEffect(() => {
|
||||||
|
getGitpodService()
|
||||||
|
.server.hasSSHPublicKey()
|
||||||
|
.then((d) => {
|
||||||
|
setHasSSHKey(d);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const host = props.ideUrl.replace(props.workspaceId, props.workspaceId + ".ssh");
|
||||||
|
const sshAccessTokenCommand = `ssh '${props.workspaceId}#${props.ownerToken}@${host}'`;
|
||||||
|
const sshKeyCommand = `ssh '${props.workspaceId}@${host}'`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t border-b border-gray-200 dark:border-gray-800 mt-2 -mx-6 px-6 py-6">
|
<>
|
||||||
<div className="mt-1 mb-4">
|
<div className="flex flex-row">
|
||||||
<AlertBox>
|
<TabMenuItem
|
||||||
<p className="text-red-500 whitespace-normal text-base">
|
key="ssh_key"
|
||||||
|
name="SSH Key"
|
||||||
|
selected={selectSSHKey}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectSSHKey(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TabMenuItem
|
||||||
|
key="access_token"
|
||||||
|
name="Access Token"
|
||||||
|
selected={!selectSSHKey}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectSSHKey(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="border-gray-200 dark:border-gray-800 border-b"></div>
|
||||||
|
<div className="space-y-4 mt-4">
|
||||||
|
{!selectSSHKey && (
|
||||||
|
<Alert type="warning" className="whitespace-normal">
|
||||||
<b>Anyone</b> on the internet with this command can access the running workspace. The command
|
<b>Anyone</b> on the internet with this command can access the running workspace. The command
|
||||||
includes a generated access token that resets on every workspace restart.
|
includes a generated access token that resets on every workspace restart.
|
||||||
</p>
|
</Alert>
|
||||||
</AlertBox>
|
)}
|
||||||
<InfoBox className="mt-4">
|
{!hasSSHKey && selectSSHKey && (
|
||||||
<p className="text-gray-500 whitespace-normal text-base">
|
<Alert type="warning" className="whitespace-normal">
|
||||||
Before connecting via SSH, make sure you have an existing SSH private key on your machine. You
|
You don't have any public SSH keys in your Gitpod account. You can{" "}
|
||||||
can create one using
|
<a href={settingsPathSSHKeys} target="setting-keys" className="gp-link">
|
||||||
<a
|
add a new public key
|
||||||
href="https://en.wikipedia.org/wiki/Ssh-keygen"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="gp-link"
|
|
||||||
>
|
|
||||||
ssh-keygen
|
|
||||||
</a>
|
</a>
|
||||||
.
|
, or use a generated access token.
|
||||||
</p>
|
</Alert>
|
||||||
</InfoBox>
|
)}
|
||||||
<p className="mt-4 text-gray-500 whitespace-normal text-base">
|
|
||||||
The following shell command can be used to SSH into this workspace.
|
<p className="text-gray-500 whitespace-normal text-base">
|
||||||
|
{!selectSSHKey ? (
|
||||||
|
"The following shell command can be used to SSH into this workspace."
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
The following shell command can be used to SSH into this workspace with a{" "}
|
||||||
|
<a href={settingsPathSSHKeys} target="setting-keys" className="gp-link">
|
||||||
|
ssh key
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<InputWithCopy value={sshCommand} tip="Copy SSH Command" />
|
<InputWithCopy
|
||||||
</div>
|
className="my-2"
|
||||||
|
value={!selectSSHKey ? sshAccessTokenCommand : sshKeyCommand}
|
||||||
|
tip="Copy SSH Command"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,14 +141,19 @@ export default function ConnectToSSHModal(props: {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
// TODO: Use title and buttons props
|
<Modal
|
||||||
<Modal visible={true} onClose={props.onClose}>
|
title="Connect via SSH"
|
||||||
<h3 className="mb-4">Connect via SSH</h3>
|
hideDivider
|
||||||
<SSHView workspaceId={props.workspaceId} ownerToken={props.ownerToken} ideUrl={props.ideUrl} />
|
buttons={
|
||||||
<div className="flex justify-end mt-6">
|
|
||||||
<button className={"ml-2 secondary"} onClick={() => props.onClose()}>
|
<button className={"ml-2 secondary"} onClick={() => props.onClose()}>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
|
visible={true}
|
||||||
|
onClose={props.onClose}
|
||||||
|
>
|
||||||
|
<div className="border-gray-200 dark:border-gray-800 -mx-6 px-6 border-b pb-4">
|
||||||
|
<SSHView workspaceId={props.workspaceId} ownerToken={props.ownerToken} ideUrl={props.ideUrl} />
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user