4
4
* See License-AGPL.txt in the project root for license information.
5
5
*/
6
6
7
- import { useState } from "react" ;
7
+ import { useEffect , useState } from "react" ;
8
8
import Modal from "../components/Modal" ;
9
9
import Tooltip from "../components/Tooltip" ;
10
10
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 TabMenuItem from "../components/TabMenuItem" ;
13
+ import { settingsPathSSHKeys } from "../settings/settings.routes" ;
14
+ import { getGitpodService } from "../service/service" ;
13
15
14
16
function InputWithCopy ( props : { value : string ; tip ?: string ; className ?: string } ) {
15
17
const [ copied , setCopied ] = useState < boolean > ( false ) ;
@@ -35,7 +37,7 @@ function InputWithCopy(props: { value: string; tip?: string; className?: string
35
37
autoFocus
36
38
className = "w-full pr-8 overscroll-none"
37
39
type = "text"
38
- defaultValue = { props . value }
40
+ value = { props . value }
39
41
/>
40
42
< div className = "cursor-pointer" onClick = { ( ) => copyToClipboard ( props . value ) } >
41
43
< div className = "absolute top-1/3 right-3" >
@@ -55,40 +57,80 @@ interface SSHProps {
55
57
}
56
58
57
59
function 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
+
62
76
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" >
67
100
< b > Anyone</ b > on the internet with this command can access the running workspace. The command
68
101
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
82
109
</ 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
+ ) }
88
126
</ p >
89
127
</ 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
+ </ >
92
134
) ;
93
135
}
94
136
@@ -99,14 +141,19 @@ export default function ConnectToSSHModal(props: {
99
141
onClose : ( ) => void ;
100
142
} ) {
101
143
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 = {
107
148
< button className = { "ml-2 secondary" } onClick = { ( ) => props . onClose ( ) } >
108
149
Close
109
150
</ 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 } />
110
157
</ div >
111
158
</ Modal >
112
159
) ;
0 commit comments