Skip to content

Commit 7908649

Browse files
committed
feat: add region querying to pre-fetched html
1 parent 5b9c378 commit 7908649

File tree

5 files changed

+86
-25
lines changed

5 files changed

+86
-25
lines changed

enterprise/coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
6767

6868
api.AGPL.Options.SetUserGroups = api.setUserGroups
6969
api.AGPL.SiteHandler.AppearanceFetcher = api.fetchAppearanceConfig
70+
api.AGPL.SiteHandler.RegionsFetcher = api.fetchRegions
7071

7172
oauthConfigs := &httpmw.OAuth2Configs{
7273
Github: options.GithubOAuth2Config,

enterprise/coderd/workspaceproxy.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,30 @@ func (api *API) forceWorkspaceProxyHealthUpdate(ctx context.Context) {
3939
// NOTE: this doesn't need a swagger definition since AGPL already has one, and
4040
// this route overrides the AGPL one.
4141
func (api *API) regions(rw http.ResponseWriter, r *http.Request) {
42-
ctx := r.Context()
43-
//nolint:gocritic // this route intentionally requests resources that users
42+
regions, err := api.fetchRegions(r.Context())
43+
if err != nil {
44+
httpapi.InternalServerError(rw, err)
45+
return
46+
}
47+
48+
httpapi.Write(r.Context(), rw, http.StatusOK, regions)
49+
}
50+
51+
func (api *API) fetchRegions(ctx context.Context) (codersdk.RegionsResponse, error) {
52+
//nolint:gocritic // this intentionally requests resources that users
4453
// cannot usually access in order to give them a full list of available
4554
// regions.
4655
ctx = dbauthz.AsSystemRestricted(ctx)
4756

4857
primaryRegion, err := api.AGPL.PrimaryRegion(ctx)
4958
if err != nil {
50-
httpapi.InternalServerError(rw, err)
51-
return
59+
return codersdk.RegionsResponse{}, err
5260
}
5361
regions := []codersdk.Region{primaryRegion}
5462

5563
proxies, err := api.Database.GetWorkspaceProxies(ctx)
5664
if err != nil {
57-
httpapi.InternalServerError(rw, err)
58-
return
65+
return codersdk.RegionsResponse{}, err
5966
}
6067

6168
// Only add additional regions if the proxy health is enabled.
@@ -81,9 +88,9 @@ func (api *API) regions(rw http.ResponseWriter, r *http.Request) {
8188
}
8289
}
8390

84-
httpapi.Write(ctx, rw, http.StatusOK, codersdk.RegionsResponse{
91+
return codersdk.RegionsResponse{
8592
Regions: regions,
86-
})
93+
}, nil
8794
}
8895

8996
// @Summary Update workspace proxy

site/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<meta property="entitlements" content="{{ .Entitlements }}" />
2121
<meta property="appearance" content="{{ .Appearance }}" />
2222
<meta property="experiments" content="{{ .Experiments }}" />
23+
<meta property="regions" content="{{ .Regions }}" />
2324
<!-- We need to set data-react-helmet to be able to override it in the workspace page -->
2425
<link
2526
rel="alternate icon"

site/site.go

+50-17
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ type Handler struct {
146146
buildInfoJSON string
147147

148148
AppearanceFetcher func(ctx context.Context) (codersdk.AppearanceConfig, error)
149+
RegionsFetcher func(ctx context.Context) (codersdk.RegionsResponse, error)
149150

150151
Entitlements atomic.Pointer[codersdk.Entitlements]
151152
Experiments atomic.Pointer[codersdk.Experiments]
@@ -231,6 +232,7 @@ type htmlState struct {
231232
Entitlements string
232233
Appearance string
233234
Experiments string
235+
Regions string
234236
}
235237

236238
type csrfState struct {
@@ -313,33 +315,64 @@ func (h *Handler) renderHTMLWithState(rw http.ResponseWriter, r *http.Request, f
313315
})
314316
err := eg.Wait()
315317
if err == nil {
316-
user, err := json.Marshal(db2sdk.User(user, orgIDs))
317-
if err == nil {
318-
state.User = html.EscapeString(string(user))
319-
}
320-
entitlements := h.Entitlements.Load()
321-
if entitlements != nil {
322-
entitlements, err := json.Marshal(entitlements)
318+
var wg sync.WaitGroup
319+
wg.Add(1)
320+
go func() {
321+
defer wg.Done()
322+
user, err := json.Marshal(db2sdk.User(user, orgIDs))
323323
if err == nil {
324-
state.Entitlements = html.EscapeString(string(entitlements))
324+
state.User = html.EscapeString(string(user))
325325
}
326+
}()
327+
entitlements := h.Entitlements.Load()
328+
if entitlements != nil {
329+
wg.Add(1)
330+
go func() {
331+
defer wg.Done()
332+
entitlements, err := json.Marshal(entitlements)
333+
if err == nil {
334+
state.Entitlements = html.EscapeString(string(entitlements))
335+
}
336+
}()
326337
}
327338
if h.AppearanceFetcher != nil {
328-
cfg, err := h.AppearanceFetcher(ctx)
329-
if err == nil {
330-
appearance, err := json.Marshal(cfg)
339+
wg.Add(1)
340+
go func() {
341+
defer wg.Done()
342+
cfg, err := h.AppearanceFetcher(ctx)
331343
if err == nil {
332-
state.Appearance = html.EscapeString(string(appearance))
344+
appearance, err := json.Marshal(cfg)
345+
if err == nil {
346+
state.Appearance = html.EscapeString(string(appearance))
347+
}
333348
}
334-
}
349+
}()
350+
}
351+
if h.RegionsFetcher != nil {
352+
wg.Add(1)
353+
go func() {
354+
defer wg.Done()
355+
regions, err := h.RegionsFetcher(ctx)
356+
if err == nil {
357+
regions, err := json.Marshal(regions)
358+
if err == nil {
359+
state.Regions = html.EscapeString(string(regions))
360+
}
361+
}
362+
}()
335363
}
336364
experiments := h.Experiments.Load()
337365
if experiments != nil {
338-
experiments, err := json.Marshal(experiments)
339-
if err == nil {
340-
state.Experiments = html.EscapeString(string(experiments))
341-
}
366+
wg.Add(1)
367+
go func() {
368+
defer wg.Done()
369+
experiments, err := json.Marshal(experiments)
370+
if err == nil {
371+
state.Experiments = html.EscapeString(string(experiments))
372+
}
373+
}()
342374
}
375+
wg.Wait()
343376
}
344377
}
345378

site/src/contexts/ProxyContext.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
9292
)
9393

9494
const queryKey = ["get-proxies"]
95+
// This doesn't seem like an idiomatic way to get react-query to use the
96+
// initial data without performing an API request on mount, but it works.
97+
//
98+
// If anyone would like to clean this up in the future, it should seed data
99+
// from the `meta` tag if it exists, and not fetch the regions route.
100+
const [initialData] = useState(() => {
101+
// Build info is injected by the Coder server into the HTML document.
102+
const regions = document.querySelector("meta[property=regions]")
103+
if (regions) {
104+
const rawContent = regions.getAttribute("content")
105+
try {
106+
return JSON.parse(rawContent as string)
107+
} catch (ex) {
108+
// Ignore this and fetch as normal!
109+
}
110+
}
111+
})
95112
const {
96113
data: proxiesResp,
97114
error: proxiesError,
@@ -100,6 +117,8 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
100117
} = useQuery({
101118
queryKey,
102119
queryFn: getWorkspaceProxies,
120+
staleTime: initialData ? Infinity : undefined,
121+
initialData,
103122
})
104123

105124
// Every time we get a new proxiesResponse, update the latency check

0 commit comments

Comments
 (0)