Skip to content

Commit 1bb58f3

Browse files
committed
feat: support RDP-specific deep links
1 parent 5e0298d commit 1bb58f3

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

Coder-Desktop/Coder-Desktop/URLHandler.swift

+80-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import SwiftUI
23
import URLRouting
34

45
@MainActor
@@ -20,20 +21,65 @@ class URLHandler {
2021
guard deployment.host() == url.host else {
2122
throw .invalidAuthority(url.host() ?? "<none>")
2223
}
24+
let route: CoderRoute
2325
do {
24-
switch try router.match(url: url) {
25-
case let .open(workspace, agent, type):
26-
switch type {
27-
case let .rdp(creds):
28-
handleRDP(workspace: workspace, agent: agent, creds: creds)
29-
}
30-
}
26+
route = try router.match(url: url)
3127
} catch {
3228
throw .routerError(url: url)
3329
}
3430

35-
func handleRDP(workspace _: String, agent _: String, creds _: RDPCredentials) {
36-
// TODO: Handle RDP
31+
switch route {
32+
case let .open(workspace, agent, type):
33+
switch type {
34+
case let .rdp(creds):
35+
try handleRDP(workspace: workspace, agent: agent, creds: creds)
36+
}
37+
}
38+
}
39+
40+
private func handleRDP(workspace: String, agent: String, creds: RDPCredentials) throws(URLError) {
41+
guard vpn.state == .connected else {
42+
throw .openError(.coderConnectOffline)
43+
}
44+
45+
guard let workspace = vpn.menuState.findWorkspace(name: workspace) else {
46+
throw .openError(.invalidWorkspace(workspace: workspace))
47+
}
48+
49+
guard let agent = vpn.menuState.findAgent(workspaceID: workspace.id, name: agent) else {
50+
throw .openError(.invalidAgent(workspace: workspace.name, agent: agent))
51+
}
52+
53+
var rdpString = "rdp:full address=s:\(agent.primaryHost):3389"
54+
if let username = creds.username {
55+
rdpString += "&username=s:\(username)"
56+
}
57+
guard let url = URL(string: rdpString) else {
58+
throw .openError(.couldNotCreateRDPURL(rdpString))
59+
}
60+
61+
let alert = NSAlert()
62+
alert.messageText = "Opening RDP"
63+
alert.informativeText = "Connecting to \(agent.primaryHost)."
64+
if let username = creds.username {
65+
alert.informativeText += "\nUsername: \(username)"
66+
}
67+
if creds.password != nil {
68+
alert.informativeText += "\nThe password will be copied to your clipboard."
69+
}
70+
71+
alert.alertStyle = .informational
72+
alert.addButton(withTitle: "Open")
73+
alert.addButton(withTitle: "Cancel")
74+
let response = alert.runModal()
75+
if response == .alertFirstButtonReturn {
76+
if let password = creds.password {
77+
NSPasteboard.general.clearContents()
78+
NSPasteboard.general.setString(password, forType: .string)
79+
}
80+
NSWorkspace.shared.open(url)
81+
} else {
82+
// User cancelled
3783
}
3884
}
3985
}
@@ -66,6 +112,7 @@ enum URLError: Error {
66112
case invalidAuthority(String)
67113
case routerError(url: URL)
68114
case noSession
115+
case openError(OpenError)
69116

70117
var description: String {
71118
switch self {
@@ -75,6 +122,30 @@ enum URLError: Error {
75122
"Failed to handle \(url.absoluteString) because the format is unsupported."
76123
case .noSession:
77124
"Not logged in."
125+
case let .openError(error):
126+
error.description
127+
}
128+
}
129+
130+
var localizedDescription: String { description }
131+
}
132+
133+
enum OpenError: Error {
134+
case invalidWorkspace(workspace: String)
135+
case invalidAgent(workspace: String, agent: String)
136+
case coderConnectOffline
137+
case couldNotCreateRDPURL(String)
138+
139+
var description: String {
140+
switch self {
141+
case let .invalidWorkspace(ws):
142+
"Could not find workspace '\(ws)'. Does it exist?"
143+
case .coderConnectOffline:
144+
"Coder Connect must be running."
145+
case let .invalidAgent(workspace: workspace, agent: agent):
146+
"Could not find agent '\(agent)' in workspace '\(workspace)'. Is the workspace running?"
147+
case let .couldNotCreateRDPURL(rdpString):
148+
"Could not create construct RDP url from '\(rdpString)'."
78149
}
79150
}
80151

Coder-Desktop/Coder-Desktop/VPN/MenuState.swift

+9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ struct VPNMenuState {
5858
// or have any invalid UUIDs.
5959
var invalidAgents: [Vpn_Agent] = []
6060

61+
public func findAgent(workspaceID: UUID, name: String) -> Agent? {
62+
agents.first(where: { $0.value.wsID == workspaceID && $0.value.name == name })?.value
63+
}
64+
65+
public func findWorkspace(name: String) -> Workspace? {
66+
workspaces
67+
.first(where: { $0.value.name == name })?.value
68+
}
69+
6170
mutating func upsertAgent(_ agent: Vpn_Agent) {
6271
guard
6372
let id = UUID(uuidData: agent.id),

0 commit comments

Comments
 (0)