@@ -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,55 @@ 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 ; } // including leading dot
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 [ 0 ..^ ConfiguredHostnameSuffix . Length ] ;
149
+
150
+ /// <summary>
151
+ /// ViewableHostnameSuffix is the domain suffix portion (including leading dot) of the fully qualified
152
+ /// domain name (FQDN) specifically for views that render it differently from the rest of the FQDN. If
153
+ /// the ConfiguredHostnameSuffix doesn't actually match the FQDN, then this will be empty and the
154
+ /// ViewableHostname will contain the entire FQDN.
155
+ /// </summary>
156
+ public string ViewableHostnameSuffix => FullyQualifiedDomainName . EndsWith ( ConfiguredHostnameSuffix )
157
+ ? ConfiguredHostnameSuffix
158
+ : string . Empty ;
96
159
97
160
[ ObservableProperty ]
98
161
[ NotifyPropertyChangedFor ( nameof ( ShowExpandAppsMessage ) ) ]
@@ -202,10 +265,12 @@ public bool TryApplyChanges(AgentViewModel model)
202
265
203
266
// To avoid spurious UI updates which cause flashing, don't actually
204
267
// 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 ;
268
+ if ( ConfiguredFqdn != model . ConfiguredFqdn )
269
+ ConfiguredFqdn = model . ConfiguredFqdn ;
270
+ if ( ConfiguredHostname != model . ConfiguredHostname )
271
+ ConfiguredHostname = model . ConfiguredHostname ;
272
+ if ( ConfiguredHostnameSuffix != model . ConfiguredHostnameSuffix )
273
+ ConfiguredHostnameSuffix = model . ConfiguredHostnameSuffix ;
209
274
if ( ConnectionStatus != model . ConnectionStatus )
210
275
ConnectionStatus = model . ConnectionStatus ;
211
276
if ( DashboardBaseUrl != model . DashboardBaseUrl )
@@ -337,12 +402,13 @@ private void ContinueFetchApps(Task<WorkspaceAgent> task)
337
402
{
338
403
Scheme = scheme ,
339
404
Host = "vscode-remote" ,
340
- Path = $ "/ssh-remote+{ FullHostname } /{ workspaceAgent . ExpandedDirectory } ",
405
+ Path = $ "/ssh-remote+{ FullyQualifiedDomainName } /{ workspaceAgent . ExpandedDirectory } ",
341
406
} . Uri ;
342
407
}
343
408
catch ( Exception e )
344
409
{
345
- _logger . LogWarning ( e , "Could not craft app URI for display app {displayApp}, app will not appear in list" ,
410
+ _logger . LogWarning ( e ,
411
+ "Could not craft app URI for display app {displayApp}, app will not appear in list" ,
346
412
displayApp ) ;
347
413
continue ;
348
414
}
@@ -365,7 +431,7 @@ private void CopyHostname(object parameter)
365
431
{
366
432
RequestedOperation = DataPackageOperation . Copy ,
367
433
} ;
368
- dataPackage . SetText ( FullHostname ) ;
434
+ dataPackage . SetText ( FullyQualifiedDomainName ) ;
369
435
Clipboard . SetContent ( dataPackage ) ;
370
436
371
437
if ( parameter is not FrameworkElement frameworkElement ) return ;
0 commit comments