mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add region querying to pre-fetched html (#8077)
This commit is contained in:
@@ -67,6 +67,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
|
||||
api.AGPL.Options.SetUserGroups = api.setUserGroups
|
||||
api.AGPL.SiteHandler.AppearanceFetcher = api.fetchAppearanceConfig
|
||||
api.AGPL.SiteHandler.RegionsFetcher = api.fetchRegions
|
||||
|
||||
oauthConfigs := &httpmw.OAuth2Configs{
|
||||
Github: options.GithubOAuth2Config,
|
||||
|
||||
@@ -39,23 +39,30 @@ func (api *API) forceWorkspaceProxyHealthUpdate(ctx context.Context) {
|
||||
// NOTE: this doesn't need a swagger definition since AGPL already has one, and
|
||||
// this route overrides the AGPL one.
|
||||
func (api *API) regions(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
//nolint:gocritic // this route intentionally requests resources that users
|
||||
regions, err := api.fetchRegions(r.Context())
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, regions)
|
||||
}
|
||||
|
||||
func (api *API) fetchRegions(ctx context.Context) (codersdk.RegionsResponse, error) {
|
||||
//nolint:gocritic // this intentionally requests resources that users
|
||||
// cannot usually access in order to give them a full list of available
|
||||
// regions.
|
||||
ctx = dbauthz.AsSystemRestricted(ctx)
|
||||
|
||||
primaryRegion, err := api.AGPL.PrimaryRegion(ctx)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
return codersdk.RegionsResponse{}, err
|
||||
}
|
||||
regions := []codersdk.Region{primaryRegion}
|
||||
|
||||
proxies, err := api.Database.GetWorkspaceProxies(ctx)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
return codersdk.RegionsResponse{}, err
|
||||
}
|
||||
|
||||
// Only add additional regions if the proxy health is enabled.
|
||||
@@ -81,9 +88,9 @@ func (api *API) regions(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.RegionsResponse{
|
||||
return codersdk.RegionsResponse{
|
||||
Regions: regions,
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
// @Summary Update workspace proxy
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<meta property="entitlements" content="{{ .Entitlements }}" />
|
||||
<meta property="appearance" content="{{ .Appearance }}" />
|
||||
<meta property="experiments" content="{{ .Experiments }}" />
|
||||
<meta property="regions" content="{{ .Regions }}" />
|
||||
<!-- We need to set data-react-helmet to be able to override it in the workspace page -->
|
||||
<link
|
||||
rel="alternate icon"
|
||||
|
||||
+50
-17
@@ -146,6 +146,7 @@ type Handler struct {
|
||||
buildInfoJSON string
|
||||
|
||||
AppearanceFetcher func(ctx context.Context) (codersdk.AppearanceConfig, error)
|
||||
RegionsFetcher func(ctx context.Context) (codersdk.RegionsResponse, error)
|
||||
|
||||
Entitlements atomic.Pointer[codersdk.Entitlements]
|
||||
Experiments atomic.Pointer[codersdk.Experiments]
|
||||
@@ -231,6 +232,7 @@ type htmlState struct {
|
||||
Entitlements string
|
||||
Appearance string
|
||||
Experiments string
|
||||
Regions string
|
||||
}
|
||||
|
||||
type csrfState struct {
|
||||
@@ -313,33 +315,64 @@ func (h *Handler) renderHTMLWithState(rw http.ResponseWriter, r *http.Request, f
|
||||
})
|
||||
err := eg.Wait()
|
||||
if err == nil {
|
||||
user, err := json.Marshal(db2sdk.User(user, orgIDs))
|
||||
if err == nil {
|
||||
state.User = html.EscapeString(string(user))
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
user, err := json.Marshal(db2sdk.User(user, orgIDs))
|
||||
if err == nil {
|
||||
state.User = html.EscapeString(string(user))
|
||||
}
|
||||
}()
|
||||
entitlements := h.Entitlements.Load()
|
||||
if entitlements != nil {
|
||||
entitlements, err := json.Marshal(entitlements)
|
||||
if err == nil {
|
||||
state.Entitlements = html.EscapeString(string(entitlements))
|
||||
}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
entitlements, err := json.Marshal(entitlements)
|
||||
if err == nil {
|
||||
state.Entitlements = html.EscapeString(string(entitlements))
|
||||
}
|
||||
}()
|
||||
}
|
||||
if h.AppearanceFetcher != nil {
|
||||
cfg, err := h.AppearanceFetcher(ctx)
|
||||
if err == nil {
|
||||
appearance, err := json.Marshal(cfg)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
cfg, err := h.AppearanceFetcher(ctx)
|
||||
if err == nil {
|
||||
state.Appearance = html.EscapeString(string(appearance))
|
||||
appearance, err := json.Marshal(cfg)
|
||||
if err == nil {
|
||||
state.Appearance = html.EscapeString(string(appearance))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
if h.RegionsFetcher != nil {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
regions, err := h.RegionsFetcher(ctx)
|
||||
if err == nil {
|
||||
regions, err := json.Marshal(regions)
|
||||
if err == nil {
|
||||
state.Regions = html.EscapeString(string(regions))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
experiments := h.Experiments.Load()
|
||||
if experiments != nil {
|
||||
experiments, err := json.Marshal(experiments)
|
||||
if err == nil {
|
||||
state.Experiments = html.EscapeString(string(experiments))
|
||||
}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
experiments, err := json.Marshal(experiments)
|
||||
if err == nil {
|
||||
state.Experiments = html.EscapeString(string(experiments))
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,23 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
)
|
||||
|
||||
const queryKey = ["get-proxies"]
|
||||
// This doesn't seem like an idiomatic way to get react-query to use the
|
||||
// initial data without performing an API request on mount, but it works.
|
||||
//
|
||||
// If anyone would like to clean this up in the future, it should seed data
|
||||
// from the `meta` tag if it exists, and not fetch the regions route.
|
||||
const [initialData] = useState(() => {
|
||||
// Build info is injected by the Coder server into the HTML document.
|
||||
const regions = document.querySelector("meta[property=regions]")
|
||||
if (regions) {
|
||||
const rawContent = regions.getAttribute("content")
|
||||
try {
|
||||
return JSON.parse(rawContent as string)
|
||||
} catch (ex) {
|
||||
// Ignore this and fetch as normal!
|
||||
}
|
||||
}
|
||||
})
|
||||
const {
|
||||
data: proxiesResp,
|
||||
error: proxiesError,
|
||||
@@ -100,6 +117,8 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
} = useQuery({
|
||||
queryKey,
|
||||
queryFn: getWorkspaceProxies,
|
||||
staleTime: initialData ? Infinity : undefined,
|
||||
initialData,
|
||||
})
|
||||
|
||||
// Every time we get a new proxiesResponse, update the latency check
|
||||
|
||||
Reference in New Issue
Block a user