Skip to content

Commit f80c296

Browse files
committed
feat: support RDP-specific deep links
1 parent f6bc9e3 commit f80c296

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

Coder-Desktop/Coder-Desktop/URLHandler.swift

Lines changed: 80 additions & 9 deletions
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

Lines changed: 9 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy