Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: support RDP-specific deep links#147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 6 commits intomainfromethan/rdp-deeplink
May 12, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletionCoder-Desktop/Coder-Desktop/Coder_DesktopApp.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,7 +85,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
image: "MenuBarIcon",
onAppear: {
// If the VPN is enabled, it's likely the token isn't expired
guardcase .disabled =self.vpn.state, self.state.hasSession else { return }
guard self.vpn.state != .connected, self.state.hasSession else { return }
Task { @MainActor in
await self.state.handleTokenExpiry()
}
Expand Down
64 changes: 57 additions & 7 deletionsCoder-Desktop/Coder-Desktop/URLHandler.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import Foundation
import SwiftUI
import VPNLib

@MainActor
Expand All@@ -20,20 +21,69 @@ class URLHandler {
guard deployment.host() == url.host else {
throw .invalidAuthority(url.host() ?? "<none>")
}
let route: CoderRoute
do {
switch try router.match(url: url) {
case let .open(workspace, agent, type):
route = try router.match(url: url)
} catch {
throw .matchError(url: url)
}

switch route {
case let .open(workspace, agent, type):
do {
switch type {
case let .rdp(creds):
handleRDP(workspace: workspace, agent: agent, creds: creds)
tryhandleRDP(workspace: workspace, agent: agent, creds: creds)
}
} catch {
throw .openError(error)
}
} catch {
throw .matchError(url: url)
}
}

private func handleRDP(workspace: String, agent: String, creds: RDPCredentials) throws(OpenError) {
guard vpn.state == .connected else {
throw .coderConnectOffline
}

guard let workspace = vpn.menuState.findWorkspace(name: workspace) else {
throw .invalidWorkspace(workspace: workspace)
}

guard let agent = vpn.menuState.findAgent(workspaceID: workspace.id, name: agent) else {
throw .invalidAgent(workspace: workspace.name, agent: agent)
}

var rdpString = "rdp:full address=s:\(agent.primaryHost):3389"
if let username = creds.username {
rdpString += "&username=s:\(username)"
}
guard let url = URL(string: rdpString) else {
throw .couldNotCreateRDPURL(rdpString)
}

func handleRDP(workspace _: String, agent _: String, creds _: RDPCredentials) {
// TODO: Handle RDP
let alert = NSAlert()
alert.messageText = "Opening RDP"
alert.informativeText = "Connecting to \(agent.primaryHost)."
if let username = creds.username {
alert.informativeText += "\nUsername: \(username)"
}
if creds.password != nil {
alert.informativeText += "\nThe password will be copied to your clipboard."
}

alert.alertStyle = .informational
alert.addButton(withTitle: "Open")
alert.addButton(withTitle: "Cancel")
let response = alert.runModal()
if response == .alertFirstButtonReturn {
if let password = creds.password {
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(password, forType: .string)
}
NSWorkspace.shared.open(url)
} else {
// User cancelled
}
}
}
9 changes: 9 additions & 0 deletionsCoder-Desktop/Coder-Desktop/VPN/MenuState.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,6 +58,15 @@ struct VPNMenuState {
// or have any invalid UUIDs.
var invalidAgents: [Vpn_Agent] = []

public func findAgent(workspaceID: UUID, name: String) -> Agent? {
agents.first(where: { $0.value.wsID == workspaceID && $0.value.name == name })?.value
}

public func findWorkspace(name: String) -> Workspace? {
workspaces
.first(where: { $0.value.name == name })?.value
}

mutating func upsertAgent(_ agent: Vpn_Agent) {
guard
let id = UUID(uuidData: agent.id),
Expand Down
26 changes: 26 additions & 0 deletionsCoder-Desktop/VPNLib/CoderRouter.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ import Foundation
import URLRouting

// This is in VPNLib to avoid depending on `swift-collections` in both the app & extension.
// https://github.com/coder/coder-desktop-macos/issues/149
public struct CoderRouter: ParserPrinter {
public init() {}

Expand DownExpand Up@@ -33,6 +34,7 @@ public enum RouterError: Error {
case invalidAuthority(String)
case matchError(url: URL)
case noSession
case openError(OpenError)

public var description: String {
switch self {
Expand All@@ -42,6 +44,30 @@ public enum RouterError: Error {
"Failed to handle \(url.absoluteString) because the format is unsupported."
case .noSession:
"Not logged in."
case let .openError(error):
error.description
}
}

public var localizedDescription: String { description }
}

public enum OpenError: Error {
case invalidWorkspace(workspace: String)
case invalidAgent(workspace: String, agent: String)
case coderConnectOffline
case couldNotCreateRDPURL(String)

public var description: String {
switch self {
case let .invalidWorkspace(ws):
"Could not find workspace '\(ws)'. Does it exist?"
case .coderConnectOffline:
"Coder Connect must be running."
case let .invalidAgent(workspace: workspace, agent: agent):
"Could not find agent '\(agent)' in workspace '\(workspace)'. Is the workspace running?"
case let .couldNotCreateRDPURL(rdpString):
"Could not construct RDP URL from '\(rdpString)'."
}
}

Expand Down
2 changes: 2 additions & 0 deletionsCoder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,8 @@ public protocol FileSyncDaemon: ObservableObject {
func resetSessions(ids: [String]) async throws(DaemonError)
}

// File Sync related code is in VPNLib to workaround a linking issue
// https://github.com/coder/coder-desktop-macos/issues/149
@MainActor
public class MutagenDaemon: FileSyncDaemon {
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "mutagen")
Expand Down
2 changes: 1 addition & 1 deletionscripts/build.sh
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -131,7 +131,7 @@ xcodebuild \
CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" \
CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION=YES \
OTHER_CODE_SIGN_FLAGS='--timestamp' |LC_ALL="en_US.UTF-8" xcpretty
OTHER_CODE_SIGN_FLAGS='--timestamp' |xcbeautify

# Create exportOptions.plist
EXPORT_OPTIONS_PATH="./build/exportOptions.plist"
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp