Skip to content

Commit a8855a4

Browse files
committed
[dashboard] support ssh copy-paste with ssh keys
1 parent f5eed0a commit a8855a4

File tree

1 file changed

+48
-39
lines changed

1 file changed

+48
-39
lines changed

components/dashboard/src/workspaces/ConnectToSSHModal.tsx

+48-39
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import { useState } from "react";
7+
import { useEffect, useState } from "react";
88
import Modal from "../components/Modal";
99
import Tooltip from "../components/Tooltip";
1010
import copy from "../images/copy.svg";
11-
import AlertBox from "../components/AlertBox";
12-
import InfoBox from "../components/InfoBox";
11+
import Alert from "../components/Alert";
12+
import { getGitpodService } from "../service/service";
1313

1414
function InputWithCopy(props: { value: string; tip?: string; className?: string }) {
1515
const [copied, setCopied] = useState<boolean>(false);
@@ -35,7 +35,7 @@ function InputWithCopy(props: { value: string; tip?: string; className?: string
3535
autoFocus
3636
className="w-full pr-8 overscroll-none"
3737
type="text"
38-
defaultValue={props.value}
38+
value={props.value}
3939
/>
4040
<div className="cursor-pointer" onClick={() => copyToClipboard(props.value)}>
4141
<div className="absolute top-1/3 right-3">
@@ -55,39 +55,46 @@ interface SSHProps {
5555
}
5656

5757
function SSHView(props: SSHProps) {
58-
const sshCommand = `ssh '${props.workspaceId}#${props.ownerToken}@${props.ideUrl.replace(
59-
props.workspaceId,
60-
props.workspaceId + ".ssh",
61-
)}'`;
58+
const [hasSSHKey, setHasSSHKey] = useState(false);
59+
60+
useEffect(() => {
61+
getGitpodService()
62+
.server.hasSSHPublicKey()
63+
.then((d) => {
64+
setHasSSHKey(d);
65+
})
66+
.catch(console.error);
67+
}, []);
68+
69+
const host = props.ideUrl.replace(props.workspaceId, props.workspaceId + ".ssh");
70+
const sshPswCommand = `ssh '${props.workspaceId}#${props.ownerToken}@${host}'`;
71+
const sshKeyCommand = `ssh '${props.workspaceId}@${host}'`;
72+
6273
return (
63-
<div className="border-t border-b border-gray-200 dark:border-gray-800 mt-2 -mx-6 px-6 py-6">
64-
<div className="mt-1 mb-4">
65-
<AlertBox>
66-
<p className="text-red-500 whitespace-normal text-base">
67-
<b>Anyone</b> on the internet with this command can access the running workspace. The command
68-
includes a generated access token that resets on every workspace restart.
69-
</p>
70-
</AlertBox>
71-
<InfoBox className="mt-4">
72-
<p className="text-gray-500 whitespace-normal text-base">
73-
Before connecting via SSH, make sure you have an existing SSH private key on your machine. You
74-
can create one using&nbsp;
75-
<a
76-
href="https://en.wikipedia.org/wiki/Ssh-keygen"
77-
target="_blank"
78-
rel="noopener noreferrer"
79-
className="gp-link"
80-
>
81-
ssh-keygen
82-
</a>
83-
.
84-
</p>
85-
</InfoBox>
86-
<p className="mt-4 text-gray-500 whitespace-normal text-base">
74+
<div>
75+
<div className="space-y-4">
76+
<Alert type="warning" className="whitespace-normal">
77+
<b>Anyone</b> on the internet with this command can access the running workspace. The command
78+
includes a generated access token that resets on every workspace restart.
79+
</Alert>
80+
<Alert type="info" className="whitespace-normal">
81+
Before connecting via SSH, make sure you have an existing SSH private key on your machine. You can
82+
create one using&nbsp;
83+
<a
84+
href="https://en.wikipedia.org/wiki/Ssh-keygen"
85+
target="_blank"
86+
rel="noopener noreferrer"
87+
className="gp-link"
88+
>
89+
ssh-keygen
90+
</a>
91+
.
92+
</Alert>
93+
<p className="text-gray-500 whitespace-normal text-base">
8794
The following shell command can be used to SSH into this workspace.
8895
</p>
8996
</div>
90-
<InputWithCopy value={sshCommand} tip="Copy SSH Command" />
97+
<InputWithCopy className="my-2" value={hasSSHKey ? sshKeyCommand : sshPswCommand} tip="Copy SSH Command" />
9198
</div>
9299
);
93100
}
@@ -99,15 +106,17 @@ export default function ConnectToSSHModal(props: {
99106
onClose: () => void;
100107
}) {
101108
return (
102-
// TODO: Use title and buttons props
103-
<Modal visible={true} onClose={props.onClose}>
104-
<h3 className="mb-4">Connect via SSH</h3>
105-
<SSHView workspaceId={props.workspaceId} ownerToken={props.ownerToken} ideUrl={props.ideUrl} />
106-
<div className="flex justify-end mt-6">
109+
<Modal
110+
title="Connect via SSH"
111+
buttons={
107112
<button className={"ml-2 secondary"} onClick={() => props.onClose()}>
108113
Close
109114
</button>
110-
</div>
115+
}
116+
visible={true}
117+
onClose={props.onClose}
118+
>
119+
<SSHView workspaceId={props.workspaceId} ownerToken={props.ownerToken} ideUrl={props.ideUrl} />
111120
</Modal>
112121
);
113122
}

0 commit comments

Comments
 (0)