@@ -6,7 +6,7 @@ struct Agent: Identifiable, Equatable, Comparable {
66 let id : UUID
77 let name : String
88 let status : AgentStatus
9- let copyableDNS : String
9+ let hosts : [ String ]
1010 let wsName : String
1111 let wsID : UUID
1212
@@ -17,6 +17,9 @@ struct Agent: Identifiable, Equatable, Comparable {
1717 }
1818 return lhs. wsName. localizedCompare ( rhs. wsName) == . orderedAscending
1919 }
20+
21+ // Hosts arrive sorted by length, the shortest looks best in the UI.
22+ var primaryHost : String ? { hosts. first }
2023}
2124
2225enum AgentStatus : Int , Equatable , Comparable {
@@ -42,7 +45,7 @@ enum AgentStatus: Int, Equatable, Comparable {
4245struct Workspace : Identifiable , Equatable , Comparable {
4346 let id : UUID
4447 let name : String
45- var agents : [ UUID ]
48+ var agents : Set < UUID >
4649
4750 static func < ( lhs: Workspace , rhs: Workspace ) -> Bool {
4851 lhs. name. localizedCompare ( rhs. name) == . orderedAscending
@@ -52,42 +55,63 @@ struct Workspace: Identifiable, Equatable, Comparable {
5255struct VPNMenuState {
5356 var agents : [ UUID : Agent ] = [ : ]
5457 var workspaces : [ UUID : Workspace ] = [ : ]
58+ // Upserted agents that don't belong to any known workspace, have no FQDNs,
59+ // or have any invalid UUIDs.
60+ var invalidAgents : [ Vpn_Agent ] = [ ]
5561
5662 mutating func upsertAgent( _ agent: Vpn_Agent ) {
57- guard let id = UUID ( uuidData: agent. id) else { return }
58- guard let wsID = UUID ( uuidData: agent. workspaceID) else { return }
63+ guard
64+ let id = UUID ( uuidData: agent. id) ,
65+ let wsID = UUID ( uuidData: agent. workspaceID) ,
66+ var workspace = workspaces [ wsID] ,
67+ !agent. fqdn. isEmpty
68+ else {
69+ invalidAgents. append ( agent)
70+ return
71+ }
5972 // An existing agent with the same name, belonging to the same workspace
6073 // is from a previous workspace build, and should be removed.
6174 agents. filter { $0. value. name == agent. name && $0. value. wsID == wsID }
6275 . forEach { agents [ $0. key] = nil }
63- workspaces [ wsID] ? . agents. append ( id)
64- let wsName = workspaces [ wsID] ? . name ?? " Unknown Workspace "
76+ workspace. agents. insert ( id)
77+ workspaces [ wsID] = workspace
78+
6579 agents [ id] = Agent (
6680 id: id,
6781 name: agent. name,
6882 // If last handshake was not within last five minutes, the agent is unhealthy
6983 status: agent. lastHandshake. date > Date . now. addingTimeInterval ( - 300 ) ? . okay : . warn,
70- // Choose the shortest hostname, and remove trailing dot if present
71- copyableDNS: agent. fqdn. min ( by: { $0. count < $1. count } )
72- . map { $0. hasSuffix ( " . " ) ? String ( $0. dropLast ( ) ) : $0 } ?? " UNKNOWN " ,
73- wsName: wsName,
84+ // Remove trailing dot if present
85+ hosts: agent. fqdn. map { $0. hasSuffix ( " . " ) ? String ( $0. dropLast ( ) ) : $0 } ,
86+ wsName: workspace. name,
7487 wsID: wsID
7588 )
7689 }
7790
7891 mutating func deleteAgent( withId id: Data ) {
79- guard let id = UUID ( uuidData: id) else { return }
92+ guard let agentUUID = UUID ( uuidData: id) else { return }
8093 // Update Workspaces
81- if let agent = agents [ id ] , var ws = workspaces [ agent. wsID] {
82- ws. agents. removeAll { $0 == id }
94+ if let agent = agents [ agentUUID ] , var ws = workspaces [ agent. wsID] {
95+ ws. agents. remove ( agentUUID )
8396 workspaces [ agent. wsID] = ws
8497 }
85- agents [ id] = nil
98+ agents [ agentUUID] = nil
99+ // Remove from invalid agents if present
100+ invalidAgents. removeAll { invalidAgent in
101+ invalidAgent. id == id
102+ }
86103 }
87104
88105 mutating func upsertWorkspace( _ workspace: Vpn_Workspace ) {
89- guard let id = UUID ( uuidData: workspace. id) else { return }
90- workspaces [ id] = Workspace ( id: id, name: workspace. name, agents: [ ] )
106+ guard let wsID = UUID ( uuidData: workspace. id) else { return }
107+ workspaces [ wsID] = Workspace ( id: wsID, name: workspace. name, agents: [ ] )
108+ // Check if we can associate any invalid agents with this workspace
109+ invalidAgents. filter { agent in
110+ agent. workspaceID == workspace. id
111+ } . forEach { agent in
112+ invalidAgents. removeAll { $0 == agent }
113+ upsertAgent ( agent)
114+ }
91115 }
92116
93117 mutating func deleteWorkspace( withId id: Data ) {
@@ -100,7 +124,7 @@ struct VPNMenuState {
100124 workspaces [ wsID] = nil
101125 }
102126
103- func sorted( ) -> [ VPNMenuItem ] {
127+ var sorted : [ VPNMenuItem ] {
104128 var items = agents. values. map { VPNMenuItem . agent ( $0) }
105129 // Workspaces with no agents are shown as offline
106130 items += workspaces. filter { _, value in
0 commit comments