@@ -6,7 +6,7 @@ struct Agent: Identifiable, Equatable, Comparable {
6
6
let id : UUID
7
7
let name : String
8
8
let status : AgentStatus
9
- let copyableDNS : String
9
+ let hosts : [ String ]
10
10
let wsName : String
11
11
let wsID : UUID
12
12
@@ -17,6 +17,9 @@ struct Agent: Identifiable, Equatable, Comparable {
17
17
}
18
18
return lhs. wsName. localizedCompare ( rhs. wsName) == . orderedAscending
19
19
}
20
+
21
+ // Hosts arrive sorted by length, the shortest looks best in the UI.
22
+ var primaryHost : String ? { hosts. first }
20
23
}
21
24
22
25
enum AgentStatus : Int , Equatable , Comparable {
@@ -42,7 +45,7 @@ enum AgentStatus: Int, Equatable, Comparable {
42
45
struct Workspace : Identifiable , Equatable , Comparable {
43
46
let id : UUID
44
47
let name : String
45
- var agents : [ UUID ]
48
+ var agents : Set < UUID >
46
49
47
50
static func < ( lhs: Workspace , rhs: Workspace ) -> Bool {
48
51
lhs. name. localizedCompare ( rhs. name) == . orderedAscending
@@ -52,42 +55,63 @@ struct Workspace: Identifiable, Equatable, Comparable {
52
55
struct VPNMenuState {
53
56
var agents : [ UUID : Agent ] = [ : ]
54
57
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 ] = [ ]
55
61
56
62
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
+ }
59
72
// An existing agent with the same name, belonging to the same workspace
60
73
// is from a previous workspace build, and should be removed.
61
74
agents. filter { $0. value. name == agent. name && $0. value. wsID == wsID }
62
75
. 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
+
65
79
agents [ id] = Agent (
66
80
id: id,
67
81
name: agent. name,
68
82
// If last handshake was not within last five minutes, the agent is unhealthy
69
83
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,
74
87
wsID: wsID
75
88
)
76
89
}
77
90
78
91
mutating func deleteAgent( withId id: Data ) {
79
- guard let id = UUID ( uuidData: id) else { return }
92
+ guard let agentUUID = UUID ( uuidData: id) else { return }
80
93
// 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 )
83
96
workspaces [ agent. wsID] = ws
84
97
}
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
+ }
86
103
}
87
104
88
105
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
+ }
91
115
}
92
116
93
117
mutating func deleteWorkspace( withId id: Data ) {
@@ -100,7 +124,7 @@ struct VPNMenuState {
100
124
workspaces [ wsID] = nil
101
125
}
102
126
103
- func sorted( ) -> [ VPNMenuItem ] {
127
+ var sorted : [ VPNMenuItem ] {
104
128
var items = agents. values. map { VPNMenuItem . agent ( $0) }
105
129
// Workspaces with no agents are shown as offline
106
130
items += workspaces. filter { _, value in
0 commit comments