Skip to content

Commit 1f81ee5

Browse files
committed
Read URL and token from config on launch
In case the user has already configured the CLI. This will only happen without user input until the first successful connection. After that the "use existing token" checkbox must be used to read the token from the config. It will only be used if it matches the URL. A secondary effect of the checkbox is that it will not open the browser window so it can also be used in the case where you already have a token you want to use but it is not in the CLI config. Lastly I removed some padding in an attempt to regain the space lost by the additions.
1 parent f51b551 commit 1f81ee5

File tree

3 files changed

+87
-17
lines changed

3 files changed

+87
-17
lines changed

src/main/kotlin/com/coder/gateway/models/CoderWorkspacesWizardModel.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ data class CoderWorkspacesWizardModel(
55
var token: String = "",
66
var buildVersion: String = "",
77
var localCliPath: String = "",
8-
var selectedWorkspace: WorkspaceAgentModel? = null
9-
)
8+
var selectedWorkspace: WorkspaceAgentModel? = null,
9+
var useExistingToken: Boolean = false
10+
)

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ import com.intellij.ui.RelativeFont
4646
import com.intellij.ui.ToolbarDecorator
4747
import com.intellij.ui.components.JBTextField
4848
import com.intellij.ui.components.dialog
49-
import com.intellij.ui.dsl.builder.AlignX
50-
import com.intellij.ui.dsl.builder.AlignY
51-
import com.intellij.ui.dsl.builder.BottomGap
52-
import com.intellij.ui.dsl.builder.RightGap
53-
import com.intellij.ui.dsl.builder.TopGap
54-
import com.intellij.ui.dsl.builder.bindText
55-
import com.intellij.ui.dsl.builder.panel
49+
import com.intellij.ui.dsl.builder.*
5650
import com.intellij.ui.table.TableView
5751
import com.intellij.util.ui.ColumnInfo
5852
import com.intellij.util.ui.JBFont
@@ -76,8 +70,12 @@ import java.awt.event.MouseListener
7670
import java.awt.event.MouseMotionListener
7771
import java.awt.font.TextAttribute
7872
import java.awt.font.TextAttribute.UNDERLINE_ON
73+
import java.nio.file.Files
74+
import java.nio.file.Path
75+
import java.nio.file.Paths
7976
import java.net.SocketTimeoutException
8077
import javax.swing.Icon
78+
import javax.swing.JCheckBox
8179
import javax.swing.JTable
8280
import javax.swing.JTextField
8381
import javax.swing.ListSelectionModel
@@ -100,6 +98,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
10098
private val appPropertiesService: PropertiesComponent = service()
10199

102100
private var tfUrl: JTextField? = null
101+
private var cbExistingToken: JCheckBox? = null
103102
private var listTableModelOfWorkspaces = ListTableModel<WorkspaceAgentModel>(
104103
WorkspaceIconColumnInfo(""),
105104
WorkspaceNameColumnInfo("Name"),
@@ -201,13 +200,13 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
201200
font = JBFont.h3().asBold()
202201
icon = CoderIcons.LOGO_16
203202
}
204-
}.topGap(TopGap.SMALL).bottomGap(BottomGap.MEDIUM)
203+
}.topGap(TopGap.SMALL)
205204
row {
206205
cell(ComponentPanelBuilder.createCommentComponent(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.comment"), false, -1, true))
207206
}
208207
row {
209208
browserLink(CoderGatewayBundle.message("gateway.connector.view.login.documentation.action"), "https://coder.com/docs/coder-oss/latest/workspaces")
210-
}.bottomGap(BottomGap.MEDIUM)
209+
}
211210
row(CoderGatewayBundle.message("gateway.connector.view.login.url.label")) {
212211
tfUrl = textField().resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL).bindText(localWizardModel::coderURL).applyToComponent {
213212
addActionListener {
@@ -223,6 +222,17 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
223222
}
224223
cell()
225224
}
225+
row {
226+
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
227+
.bindSelected(localWizardModel::useExistingToken)
228+
.component
229+
}
230+
row {
231+
cell(ComponentPanelBuilder.createCommentComponent(
232+
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.tooltip",
233+
CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label")),
234+
false, -1, true))
235+
}
226236
row {
227237
scrollCell(toolbar.createPanel().apply {
228238
add(notificationBanner.component.apply { isVisible = false }, "South")
@@ -313,18 +323,70 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
313323
if (localWizardModel.coderURL.isNotBlank() && localWizardModel.token.isNotBlank()) {
314324
triggerWorkspacePolling()
315325
} else {
316-
val url = appPropertiesService.getValue(CODER_URL_KEY)
317-
val token = appPropertiesService.getValue(SESSION_TOKEN)
318-
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
326+
val (url, token) = readStorageOrConfig()
327+
if (!url.isNullOrBlank()) {
319328
localWizardModel.coderURL = url
320-
localWizardModel.token = token
321329
tfUrl?.text = url
330+
}
331+
if (!token.isNullOrBlank()) {
332+
localWizardModel.token = token
333+
}
334+
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
322335
loginAndLoadWorkspace(token, true)
323336
}
324337
}
325338
updateWorkspaceActions()
326339
}
327340

341+
/**
342+
* Return the URL and token from storage or the CLI config.
343+
*/
344+
private fun readStorageOrConfig(): Pair<String?, String?> {
345+
val url = appPropertiesService.getValue(CODER_URL_KEY)
346+
val token = appPropertiesService.getValue(SESSION_TOKEN)
347+
if (!url.isNullOrBlank() && !token.isNullOrBlank()) {
348+
return url to token
349+
}
350+
return readConfig()
351+
}
352+
353+
/**
354+
* Return the URL and token from the CLI config.
355+
*/
356+
private fun readConfig(): Pair<String?, String?> {
357+
val configDir = getConfigDir()
358+
try {
359+
val url = Files.readString(configDir.resolve("url"))
360+
val token = Files.readString(configDir.resolve("session"))
361+
return url to token
362+
} catch (e: Exception) {
363+
return null to null // Probably has not configured the CLI yet.
364+
}
365+
}
366+
367+
/**
368+
* Return the config directory used by the CLI.
369+
*/
370+
private fun getConfigDir(): Path {
371+
var dir = System.getenv("CODER_CONFIG_DIR")
372+
if (!dir.isNullOrBlank()) {
373+
return Path.of(dir)
374+
}
375+
// The Coder CLI uses https://github.com/kirsle/configdir so this should
376+
// match how it behaves.
377+
return when(getOS()) {
378+
OS.WINDOWS -> Paths.get(System.getenv("APPDATA"), "coderv2")
379+
OS.MAC -> Paths.get(System.getenv("HOME"), "Library/Application Support/coderv2")
380+
else -> {
381+
dir = System.getenv("XDG_CACHE_HOME")
382+
if (!dir.isNullOrBlank()) {
383+
return Paths.get(dir, "coderv2")
384+
}
385+
return Paths.get(System.getenv("HOME"), ".config/coderv2")
386+
}
387+
}
388+
}
389+
328390
private fun updateWorkspaceActions() {
329391
goToDashboardAction.isEnabled = coderClient.isReady
330392
createWorkspaceAction.isEnabled = coderClient.isReady
@@ -440,8 +502,13 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
440502

441503
private fun askToken(openBrowser: Boolean): String? {
442504
val getTokenUrl = localWizardModel.coderURL.toURL().withPath("/login?redirect=%2Fcli-auth")
443-
if (openBrowser) {
505+
if (openBrowser && !localWizardModel.useExistingToken) {
444506
BrowserUtil.browse(getTokenUrl)
507+
} else if (localWizardModel.useExistingToken) {
508+
val (url, token) = readConfig()
509+
if (url == localWizardModel.coderURL && !token.isNullOrBlank()) {
510+
localWizardModel.token = token
511+
}
445512
}
446513
var tokenFromUser: String? = null
447514
ApplicationManager.getApplication().invokeAndWait({

src/main/resources/messages/CoderGatewayBundle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ gateway.connector.description=Connects to a Coder Workspace dev environment so t
33
gateway.connector.action.text=Connect to Coder
44
gateway.connector.view.login.documentation.action=Learn more about Coder
55
gateway.connector.view.login.url.label=URL:
6+
gateway.connector.view.login.existing-token.label=Use existing token
7+
gateway.connector.view.login.existing-token.tooltip=Checking "{0}" will prevent the browser from being launched for generating a new token. Additionally, if a token is already configured for this URL via the CLI it will appear as the default and can be used as-is or replaced.
68
gateway.connector.view.login.token.dialog=Paste your token here:
79
gateway.connector.view.login.token.label=Session Token:
810
gateway.connector.view.coder.workspaces.header.text=Coder Workspaces
@@ -28,4 +30,4 @@ gateway.connector.recentconnections.title=Recent Coder Workspaces
2830
gateway.connector.recentconnections.new.wizard.button.tooltip=Open a new Coder Workspace
2931
gateway.connector.recentconnections.remove.button.tooltip=Remove from Recent Connections
3032
gateway.connector.recentconnections.terminal.button.tooltip=Open SSH Web Terminal
31-
gateway.connector.coder.connection.provider.title=Connecting to Coder workspace...
33+
gateway.connector.coder.connection.provider.title=Connecting to Coder workspace...

0 commit comments

Comments
 (0)