@@ -23,8 +23,13 @@ namespace Coder.Desktop.App.ViewModels;
23
23
24
24
public interface IAgentViewModelFactory
25
25
{
26
- public AgentViewModel Create ( IAgentExpanderHost expanderHost , Uuid id , string hostname , string hostnameSuffix ,
26
+ public AgentViewModel Create ( IAgentExpanderHost expanderHost , Uuid id , string fullyQualifiedDomainName ,
27
+ string hostnameSuffix ,
27
28
AgentConnectionStatus connectionStatus , Uri dashboardBaseUrl , string ? workspaceName ) ;
29
+
30
+ public AgentViewModel CreateDummy ( IAgentExpanderHost expanderHost , Uuid id ,
31
+ string hostnameSuffix ,
32
+ AgentConnectionStatus connectionStatus , Uri dashboardBaseUrl , string workspaceName ) ;
28
33
}
29
34
30
35
public class AgentViewModelFactory (
@@ -33,14 +38,32 @@ public class AgentViewModelFactory(
33
38
ICredentialManager credentialManager ,
34
39
IAgentAppViewModelFactory agentAppViewModelFactory ) : IAgentViewModelFactory
35
40
{
36
- public AgentViewModel Create ( IAgentExpanderHost expanderHost , Uuid id , string hostname , string hostnameSuffix ,
41
+ public AgentViewModel Create ( IAgentExpanderHost expanderHost , Uuid id , string fullyQualifiedDomainName ,
42
+ string hostnameSuffix ,
37
43
AgentConnectionStatus connectionStatus , Uri dashboardBaseUrl , string ? workspaceName )
38
44
{
39
45
return new AgentViewModel ( childLogger , coderApiClientFactory , credentialManager , agentAppViewModelFactory ,
40
46
expanderHost , id )
41
47
{
42
- Hostname = hostname ,
43
- HostnameSuffix = hostnameSuffix ,
48
+ ConfiguredFqdn = fullyQualifiedDomainName ,
49
+ ConfiguredHostname = string . Empty ,
50
+ ConfiguredHostnameSuffix = hostnameSuffix ,
51
+ ConnectionStatus = connectionStatus ,
52
+ DashboardBaseUrl = dashboardBaseUrl ,
53
+ WorkspaceName = workspaceName ,
54
+ } ;
55
+ }
56
+
57
+ public AgentViewModel CreateDummy ( IAgentExpanderHost expanderHost , Uuid id ,
58
+ string hostnameSuffix ,
59
+ AgentConnectionStatus connectionStatus , Uri dashboardBaseUrl , string workspaceName )
60
+ {
61
+ return new AgentViewModel ( childLogger , coderApiClientFactory , credentialManager , agentAppViewModelFactory ,
62
+ expanderHost , id )
63
+ {
64
+ ConfiguredFqdn = string . Empty ,
65
+ ConfiguredHostname = workspaceName ,
66
+ ConfiguredHostnameSuffix = hostnameSuffix ,
44
67
ConnectionStatus = connectionStatus ,
45
68
DashboardBaseUrl = dashboardBaseUrl ,
46
69
WorkspaceName = workspaceName ,
@@ -84,15 +107,56 @@ public partial class AgentViewModel : ObservableObject, IModelUpdateable<AgentVi
84
107
85
108
public readonly Uuid Id ;
86
109
110
+ // This is set only for "dummy" agents that represent unstarted workspaces. If set, then ConfiguredFqdn
111
+ // should be empty, otherwise it will override this.
112
+ [ ObservableProperty ]
113
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostname ) ) ]
114
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostnameSuffix ) ) ]
115
+ [ NotifyPropertyChangedFor ( nameof ( FullyQualifiedDomainName ) ) ]
116
+ public required partial string ConfiguredHostname { get ; set ; }
117
+
118
+ // This should be set if we have an FQDN from the VPN service, and overrides ConfiguredHostname if set.
87
119
[ ObservableProperty ]
88
- [ NotifyPropertyChangedFor ( nameof ( FullHostname ) ) ]
89
- public required partial string Hostname { get ; set ; }
120
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostname ) ) ]
121
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostnameSuffix ) ) ]
122
+ [ NotifyPropertyChangedFor ( nameof ( FullyQualifiedDomainName ) ) ]
123
+ public required partial string ConfiguredFqdn { get ; set ; }
90
124
91
125
[ ObservableProperty ]
92
- [ NotifyPropertyChangedFor ( nameof ( FullHostname ) ) ]
93
- public required partial string HostnameSuffix { get ; set ; } // including leading dot
126
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostname ) ) ]
127
+ [ NotifyPropertyChangedFor ( nameof ( ViewableHostnameSuffix ) ) ]
128
+ [ NotifyPropertyChangedFor ( nameof ( FullyQualifiedDomainName ) ) ]
129
+ public required partial string ConfiguredHostnameSuffix { get ; set ; }
130
+
131
+
132
+ public string FullyQualifiedDomainName
133
+ {
134
+ get
135
+ {
136
+ if ( ! string . IsNullOrEmpty ( ConfiguredFqdn ) ) return ConfiguredFqdn ;
137
+ return ConfiguredHostname + ConfiguredHostnameSuffix ;
138
+ }
139
+ }
94
140
95
- public string FullHostname => Hostname + HostnameSuffix ;
141
+ /// <summary>
142
+ /// ViewableHostname is the hostname portion of the fully qualified domain name (FQDN) specifically for
143
+ /// views that render it differently than the suffix. If the ConfiguredHostnameSuffix doesn't actually
144
+ /// match the FQDN, then this will be the entire FQDN, and ViewableHostnameSuffix will be empty.
145
+ /// </summary>
146
+ public string ViewableHostname => ! FullyQualifiedDomainName . EndsWith ( ConfiguredHostnameSuffix )
147
+ ? FullyQualifiedDomainName
148
+ : FullyQualifiedDomainName . Substring ( 0 ,
149
+ FullyQualifiedDomainName . Length - ConfiguredHostnameSuffix . Length ) ;
150
+
151
+ /// <summary>
152
+ /// ViewableHostnameSuffix is the domain suffix portion of the fully qualified domain name (FQDN)
153
+ /// specifically for views that render it differently from the rest of the FQDN. If the
154
+ /// ConfiguredHostnameSuffix doesn't actually match the FQDN, then this will be empty and the
155
+ /// ViewableHostname will contain the entire FQDN.
156
+ /// </summary>
157
+ public string ViewableHostnameSuffix => FullyQualifiedDomainName . EndsWith ( ConfiguredHostnameSuffix )
158
+ ? ConfiguredHostnameSuffix
159
+ : string . Empty ;
96
160
97
161
[ ObservableProperty ]
98
162
[ NotifyPropertyChangedFor ( nameof ( ShowExpandAppsMessage ) ) ]
@@ -202,10 +266,12 @@ public bool TryApplyChanges(AgentViewModel model)
202
266
203
267
// To avoid spurious UI updates which cause flashing, don't actually
204
268
// write to values unless they've changed.
205
- if ( Hostname != model . Hostname )
206
- Hostname = model . Hostname ;
207
- if ( HostnameSuffix != model . HostnameSuffix )
208
- HostnameSuffix = model . HostnameSuffix ;
269
+ if ( ConfiguredFqdn != model . ConfiguredFqdn )
270
+ ConfiguredFqdn = model . ConfiguredFqdn ;
271
+ if ( ConfiguredHostname != model . ConfiguredHostname )
272
+ ConfiguredHostname = model . ConfiguredHostname ;
273
+ if ( ConfiguredHostnameSuffix != model . ConfiguredHostnameSuffix )
274
+ ConfiguredHostnameSuffix = model . ConfiguredHostnameSuffix ;
209
275
if ( ConnectionStatus != model . ConnectionStatus )
210
276
ConnectionStatus = model . ConnectionStatus ;
211
277
if ( DashboardBaseUrl != model . DashboardBaseUrl )
@@ -337,12 +403,13 @@ private void ContinueFetchApps(Task<WorkspaceAgent> task)
337
403
{
338
404
Scheme = scheme ,
339
405
Host = "vscode-remote" ,
340
- Path = $ "/ssh-remote+{ FullHostname } /{ workspaceAgent . ExpandedDirectory } ",
406
+ Path = $ "/ssh-remote+{ FullyQualifiedDomainName } /{ workspaceAgent . ExpandedDirectory } ",
341
407
} . Uri ;
342
408
}
343
409
catch ( Exception e )
344
410
{
345
- _logger . LogWarning ( e , "Could not craft app URI for display app {displayApp}, app will not appear in list" ,
411
+ _logger . LogWarning ( e ,
412
+ "Could not craft app URI for display app {displayApp}, app will not appear in list" ,
346
413
displayApp ) ;
347
414
continue ;
348
415
}
@@ -365,7 +432,7 @@ private void CopyHostname(object parameter)
365
432
{
366
433
RequestedOperation = DataPackageOperation . Copy ,
367
434
} ;
368
- dataPackage . SetText ( FullHostname ) ;
435
+ dataPackage . SetText ( FullyQualifiedDomainName ) ;
369
436
Clipboard . SetContent ( dataPackage ) ;
370
437
371
438
if ( parameter is not FrameworkElement frameworkElement ) return ;
0 commit comments