@@ -2,22 +2,27 @@ package com.coder.gateway.views.steps
2
2
3
3
import com.coder.gateway.CoderGatewayBundle
4
4
import com.coder.gateway.models.CoderWorkspacesWizardModel
5
- import com.coder.gateway.models.LoginModel
6
5
import com.coder.gateway.sdk.CoderCLIManager
7
6
import com.coder.gateway.sdk.CoderRestClientService
8
7
import com.coder.gateway.sdk.OS
9
8
import com.coder.gateway.sdk.getOS
10
- import com.intellij.credentialStore.CredentialAttributes
11
- import com.intellij.credentialStore.askPassword
9
+ import com.coder.gateway.sdk.toURL
10
+ import com.coder.gateway.sdk.withPath
11
+ import com.intellij.ide.BrowserUtil
12
12
import com.intellij.ide.IdeBundle
13
13
import com.intellij.openapi.Disposable
14
14
import com.intellij.openapi.application.ApplicationManager
15
+ import com.intellij.openapi.application.ModalityState
16
+ import com.intellij.openapi.application.invokeAndWaitIfNeeded
15
17
import com.intellij.openapi.progress.ProgressIndicator
16
18
import com.intellij.openapi.progress.ProgressManager
17
19
import com.intellij.openapi.progress.Task
18
20
import com.intellij.openapi.ui.panel.ComponentPanelBuilder
19
21
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
22
+ import com.intellij.ui.AppIcon
20
23
import com.intellij.ui.IconManager
24
+ import com.intellij.ui.components.JBTextField
25
+ import com.intellij.ui.components.dialog
21
26
import com.intellij.ui.dsl.builder.BottomGap
22
27
import com.intellij.ui.dsl.builder.RightGap
23
28
import com.intellij.ui.dsl.builder.TopGap
@@ -29,12 +34,12 @@ import kotlinx.coroutines.CoroutineScope
29
34
import kotlinx.coroutines.Dispatchers
30
35
import kotlinx.coroutines.cancel
31
36
import org.zeroturnaround.exec.ProcessExecutor
32
- import java.net.URL
37
+ import java.awt.Dimension
33
38
import java.util.logging.Logger
34
39
35
40
class CoderAuthStepView : CoderWorkspacesWizardStep , Disposable {
36
41
private val cs = CoroutineScope (Dispatchers .Main )
37
- private var model = LoginModel ()
42
+ private var model = CoderWorkspacesWizardModel ()
38
43
private val coderClient: CoderRestClientService = ApplicationManager .getApplication().getService(CoderRestClientService ::class .java)
39
44
40
45
override val component = panel {
@@ -51,14 +56,8 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
51
56
row {
52
57
browserLink(CoderGatewayBundle .message(" gateway.connector.view.login.documentation.action" ), " https://coder.com/docs/coder/latest/workspaces" )
53
58
}.bottomGap(BottomGap .MEDIUM )
54
- row {
55
- label(CoderGatewayBundle .message(" gateway.connector.view.login.url.label" ))
56
- textField().resizableColumn().horizontalAlign(HorizontalAlign .FILL ).gap(RightGap .SMALL ).bindText(model::url)
57
- cell()
58
- }
59
-
60
- row(CoderGatewayBundle .message(" gateway.connector.view.login.email.label" )) {
61
- textField().resizableColumn().bindText(model::email)
59
+ row(CoderGatewayBundle .message(" gateway.connector.view.login.url.label" )) {
60
+ textField().resizableColumn().horizontalAlign(HorizontalAlign .FILL ).gap(RightGap .SMALL ).bindText(model::coderURL)
62
61
cell()
63
62
}
64
63
}
@@ -71,45 +70,31 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
71
70
72
71
override fun onInit (wizardModel : CoderWorkspacesWizardModel ) {
73
72
model.apply {
74
- url = wizardModel.loginModel.url
75
- email = wizardModel.loginModel.email
76
- password = wizardModel.loginModel.password
73
+ coderURL = wizardModel.coderURL
74
+ token = wizardModel.token
77
75
}
78
76
component.apply ()
79
77
}
80
78
81
- override suspend fun onNext (wizardModel : CoderWorkspacesWizardModel ) {
82
- val password = askPassword(
83
- null ,
84
- CoderGatewayBundle .message(" gateway.connector.view.login.credentials.dialog.title" ),
85
- CoderGatewayBundle .message(" gateway.connector.view.login.password.label" ),
86
- CredentialAttributes (" Coder" ),
87
- true
88
- )
89
- model.password = password
90
- val authTask = object : Task .Modal (null , " Authenticate and setup coder" , false ) {
91
- override fun run (pi : ProgressIndicator ) {
92
-
93
- pi.apply {
94
- text = " Authenticating ${model.email} on ${model.url} ..."
95
- fraction = 0.3
96
- }
97
-
98
- val url = URL (model.url)
99
- coderClient.initClientSession(url, model.email, model.password!! )
100
- wizardModel.apply {
101
- loginModel = model.copy()
102
- }
79
+ override fun onNext (wizardModel : CoderWorkspacesWizardModel ): Boolean {
80
+ BrowserUtil .browse(model.coderURL.toURL().withPath(" /login?redirect=%2Fcli-auth" ))
81
+ val pastedToken = askToken()
103
82
83
+ if (pastedToken?.isNullOrBlank() == true || coderClient.initClientSession(model.coderURL.toURL(), pastedToken) == null ) {
84
+ return false
85
+ }
86
+ model.token = pastedToken
87
+ val authTask = object : Task .Modal (null , CoderGatewayBundle .message(" gateway.connector.view.login.cli.downloader.dialog.title" ), false ) {
88
+ override fun run (pi : ProgressIndicator ) {
104
89
pi.apply {
105
90
text = " Downloading coder cli..."
106
- fraction = 0.4
91
+ fraction = 0.1
107
92
}
108
93
109
- val cliManager = CoderCLIManager (URL (url.protocol, url.host, url.port, " " ))
94
+ val cliManager = CoderCLIManager (model.coderURL.toURL( ))
110
95
val cli = cliManager.download() ? : throw IllegalStateException (" Could not download coder binary" )
111
96
if (getOS() != OS .WINDOWS ) {
112
- pi.fraction = 0.5
97
+ pi.fraction = 0.4
113
98
val chmodOutput = ProcessExecutor ().command(" chmod" , " +x" , cli.toAbsolutePath().toString()).readOutput(true ).execute().outputUTF8()
114
99
logger.info(" chmod +x ${cli.toAbsolutePath()} $chmodOutput " )
115
100
}
@@ -118,15 +103,41 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
118
103
fraction = 0.5
119
104
}
120
105
121
- val loginOutput = ProcessExecutor ().command(cli.toAbsolutePath().toString(), " login" , url.toString() , " --token" , coderClient.sessionToken).readOutput(true ).execute().outputUTF8()
106
+ val loginOutput = ProcessExecutor ().command(cli.toAbsolutePath().toString(), " login" , model.coderURL , " --token" , coderClient.sessionToken).readOutput(true ).execute().outputUTF8()
122
107
logger.info(" coder-cli login output: $loginOutput " )
123
- pi.fraction = 0.6
108
+ pi.fraction = 0.8
124
109
val sshConfigOutput = ProcessExecutor ().command(cli.toAbsolutePath().toString(), " config-ssh" ).readOutput(true ).execute().outputUTF8()
125
110
logger.info(" coder-cli config-ssh output: $sshConfigOutput " )
126
111
pi.fraction = 1.0
127
112
}
128
113
}
114
+ wizardModel.apply {
115
+ coderURL = model.coderURL
116
+ token = model.token
117
+ }
129
118
ProgressManager .getInstance().run (authTask)
119
+ return true
120
+ }
121
+
122
+ private fun askToken (): String? {
123
+ return invokeAndWaitIfNeeded(ModalityState .any()) {
124
+ lateinit var sessionTokenTextField: JBTextField
125
+
126
+ val panel = panel {
127
+ row {
128
+ label(CoderGatewayBundle .message(" gateway.connector.view.login.token.label" ))
129
+ sessionTokenTextField = textField().applyToComponent {
130
+ minimumSize = Dimension (320 , - 1 )
131
+ }.component
132
+ }
133
+ }
134
+
135
+ AppIcon .getInstance().requestAttention(null , true )
136
+ if (! dialog(CoderGatewayBundle .message(" gateway.connector.view.login.token.dialog" ), panel = panel, focusedComponent = sessionTokenTextField).showAndGet()) {
137
+ return @invokeAndWaitIfNeeded null
138
+ }
139
+ return @invokeAndWaitIfNeeded sessionTokenTextField.text
140
+ }
130
141
}
131
142
132
143
override fun dispose () {
0 commit comments