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" ;
88import Modal from "../components/Modal" ;
99import Tooltip from "../components/Tooltip" ;
1010import 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 TabMenuItem from "../components/TabMenuItem" ;
13+ import { settingsPathSSHKeys } from "../settings/settings.routes" ;
14+ import { getGitpodService } from "../service/service" ;
1315
1416function InputWithCopy ( props : { value : string ; tip ?: string ; className ?: string } ) {
1517 const [ copied , setCopied ] = useState < boolean > ( false ) ;
@@ -35,7 +37,7 @@ function InputWithCopy(props: { value: string; tip?: string; className?: string
3537 autoFocus
3638 className = "w-full pr-8 overscroll-none"
3739 type = "text"
38- defaultValue = { props . value }
40+ value = { props . value }
3941 />
4042 < div className = "cursor-pointer" onClick = { ( ) => copyToClipboard ( props . value ) } >
4143 < div className = "absolute top-1/3 right-3" >
@@ -55,40 +57,80 @@ interface SSHProps {
5557}
5658
5759function SSHView ( props : SSHProps ) {
58- const sshCommand = `ssh '${ props . workspaceId } #${ props . ownerToken } @${ props . ideUrl . replace (
59- props . workspaceId ,
60- props . workspaceId + ".ssh" ,
61- ) } '`;
60+ const [ hasSSHKey , setHasSSHKey ] = useState ( true ) ;
61+ const [ selectSSHKey , setSelectSSHKey ] = useState ( true ) ;
62+
63+ useEffect ( ( ) => {
64+ getGitpodService ( )
65+ . server . hasSSHPublicKey ( )
66+ . then ( ( d ) => {
67+ setHasSSHKey ( d ) ;
68+ } )
69+ . catch ( console . error ) ;
70+ } , [ ] ) ;
71+
72+ const host = props . ideUrl . replace ( props . workspaceId , props . workspaceId + ".ssh" ) ;
73+ const sshAccessTokenCommand = `ssh '${ props . workspaceId } #${ props . ownerToken } @${ host } '` ;
74+ const sshKeyCommand = `ssh '${ props . workspaceId } @${ host } '` ;
75+
6276 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" >
77+ < >
78+ < div className = "flex flex-row" >
79+ < TabMenuItem
80+ key = "ssh_key"
81+ name = "SSH Key"
82+ selected = { selectSSHKey }
83+ onClick = { ( ) => {
84+ setSelectSSHKey ( true ) ;
85+ } }
86+ />
87+ < TabMenuItem
88+ key = "access_token"
89+ name = "Access Token"
90+ selected = { ! selectSSHKey }
91+ onClick = { ( ) => {
92+ setSelectSSHKey ( false ) ;
93+ } }
94+ />
95+ </ div >
96+ < div className = "border-gray-200 dark:border-gray-800 border-b" > </ div >
97+ < div className = "space-y-4 mt-4" >
98+ { ! selectSSHKey && (
99+ < Alert type = "warning" className = "whitespace-normal" >
67100 < b > Anyone</ b > on the internet with this command can access the running workspace. The command
68101 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
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
102+ </ Alert >
103+ ) }
104+ { ! hasSSHKey && selectSSHKey && (
105+ < Alert type = "warning" className = "whitespace-normal" >
106+ You don't have any public SSH keys in your Gitpod account. You can{ " " }
107+ < a href = { settingsPathSSHKeys } target = "setting-keys" className = "gp-link" >
108+ add a new public key
82109 </ a >
83- .
84- </ p >
85- </ InfoBox >
86- < p className = "mt-4 text-gray-500 whitespace-normal text-base" >
87- The following shell command can be used to SSH into this workspace.
110+ , or use a generated access token.
111+ </ Alert >
112+ ) }
113+
114+ < p className = "text-gray-500 whitespace-normal text-base" >
115+ { ! selectSSHKey ? (
116+ "The following shell command can be used to SSH into this workspace."
117+ ) : (
118+ < >
119+ The following shell command can be used to SSH into this workspace with a{ " " }
120+ < a href = { settingsPathSSHKeys } target = "setting-keys" className = "gp-link" >
121+ ssh key
122+ </ a >
123+ .
124+ </ >
125+ ) }
88126 </ p >
89127 </ div >
90- < InputWithCopy value = { sshCommand } tip = "Copy SSH Command" />
91- </ div >
128+ < InputWithCopy
129+ className = "my-2"
130+ value = { ! selectSSHKey ? sshAccessTokenCommand : sshKeyCommand }
131+ tip = "Copy SSH Command"
132+ />
133+ </ >
92134 ) ;
93135}
94136
@@ -99,14 +141,19 @@ export default function ConnectToSSHModal(props: {
99141 onClose : ( ) => void ;
100142} ) {
101143 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" >
144+ < Modal
145+ title = "Connect via SSH"
146+ hideDivider
147+ buttons = {
107148 < button className = { "ml-2 secondary" } onClick = { ( ) => props . onClose ( ) } >
108149 Close
109150 </ button >
151+ }
152+ visible = { true }
153+ onClose = { props . onClose }
154+ >
155+ < div className = "border-gray-200 dark:border-gray-800 -mx-6 px-6 border-b pb-4" >
156+ < SSHView workspaceId = { props . workspaceId } ownerToken = { props . ownerToken } ideUrl = { props . ideUrl } />
110157 </ div >
111158 </ Modal >
112159 ) ;
0 commit comments