Skip to content

Commit 2d3eb72

Browse files
committed
chore: refactor error page actions
Instead of multiple bool/string vars, there is a single actions array. This will let us add new types of links without extra variables.
1 parent d22d34e commit 2d3eb72

File tree

9 files changed

+222
-107
lines changed

9 files changed

+222
-107
lines changed

coderd/idpsync/idpsync.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,16 @@ type HTTPError struct {
251251
func (e HTTPError) Write(rw http.ResponseWriter, r *http.Request) {
252252
if e.RenderStaticPage {
253253
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
254-
Status: e.Code,
255-
HideStatus: true,
256-
Title: e.Msg,
257-
Description: e.Detail,
258-
RetryEnabled: false,
259-
DashboardURL: "/login",
260-
254+
Status: e.Code,
255+
HideStatus: true,
256+
Title: e.Msg,
257+
Description: e.Detail,
258+
Actions: []site.Action{
259+
{
260+
URL: "/login",
261+
Text: "Back to site",
262+
},
263+
},
261264
RenderDescriptionMarkdown: e.RenderDetailMarkdown,
262265
})
263266
return

coderd/oauth2provider/authorize.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,18 @@ func ShowAuthorizePage(accessURL *url.URL) http.HandlerFunc {
7575

7676
callbackURL, err := url.Parse(app.CallbackURL)
7777
if err != nil {
78-
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{Status: http.StatusInternalServerError, HideStatus: false, Title: "Internal Server Error", Description: err.Error(), RetryEnabled: false, DashboardURL: accessURL.String(), Warnings: nil})
78+
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
79+
Status: http.StatusInternalServerError,
80+
HideStatus: false,
81+
Title: "Internal Server Error",
82+
Description: err.Error(),
83+
Actions: []site.Action{
84+
{
85+
URL: accessURL.String(),
86+
Text: "Back to site",
87+
},
88+
},
89+
})
7990
return
8091
}
8192

@@ -85,7 +96,19 @@ func ShowAuthorizePage(accessURL *url.URL) http.HandlerFunc {
8596
for i, err := range validationErrs {
8697
errStr[i] = err.Detail
8798
}
88-
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{Status: http.StatusBadRequest, HideStatus: false, Title: "Invalid Query Parameters", Description: "One or more query parameters are missing or invalid.", RetryEnabled: false, DashboardURL: accessURL.String(), Warnings: errStr})
99+
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
100+
Status: http.StatusBadRequest,
101+
HideStatus: false,
102+
Title: "Invalid Query Parameters",
103+
Description: "One or more query parameters are missing or invalid.",
104+
Warnings: errStr,
105+
Actions: []site.Action{
106+
{
107+
URL: accessURL.String(),
108+
Text: "Back to site",
109+
},
110+
},
111+
})
89112
return
90113
}
91114

coderd/tailnet.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,9 @@ func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID u
199199
proxy := httputil.NewSingleHostReverseProxy(&tgt)
200200
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, theErr error) {
201201
var (
202-
desc = "Failed to proxy request to application: " + theErr.Error()
203-
additionalInfo = ""
204-
additionalButtonLink = ""
205-
additionalButtonText = ""
202+
desc = "Failed to proxy request to application: " + theErr.Error()
203+
additionalInfo = ""
204+
actions = []site.Action{}
206205
)
207206

208207
var tlsError tls.RecordHeaderError
@@ -222,21 +221,28 @@ func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID u
222221
app = app.ChangePortProtocol(targetProtocol)
223222

224223
switchURL.Host = fmt.Sprintf("%s%s", app.String(), strings.TrimPrefix(wildcardHostname, "*"))
225-
additionalButtonLink = switchURL.String()
226-
additionalButtonText = fmt.Sprintf("Switch to %s", strings.ToUpper(targetProtocol))
224+
actions = append(actions, site.Action{
225+
URL: switchURL.String(),
226+
Text: fmt.Sprintf("Switch to %s", strings.ToUpper(targetProtocol)),
227+
})
227228
additionalInfo += fmt.Sprintf("This error seems to be due to an app protocol mismatch, try switching to %s.", strings.ToUpper(targetProtocol))
228229
}
229230
}
230231

231232
site.RenderStaticErrorPage(w, r, site.ErrorPageData{
232-
Status: http.StatusBadGateway,
233-
Title: "Bad Gateway",
234-
Description: desc,
235-
RetryEnabled: true,
236-
DashboardURL: dashboardURL.String(),
237-
AdditionalInfo: additionalInfo,
238-
AdditionalButtonLink: additionalButtonLink,
239-
AdditionalButtonText: additionalButtonText,
233+
Status: http.StatusBadGateway,
234+
Title: "Bad Gateway",
235+
Description: desc,
236+
Actions: append(actions, []site.Action{
237+
{
238+
Text: "Retry",
239+
},
240+
{
241+
URL: dashboardURL.String(),
242+
Text: "Back to site",
243+
},
244+
}...),
245+
AdditionalInfo: additionalInfo,
240246
})
241247
}
242248
proxy.Director = s.director(agentID, proxy.Director)

coderd/workspaceapps/errors.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"net/http"
66
"net/url"
7+
"path"
78

89
"cdr.dev/slog"
910
"github.com/coder/coder/v2/codersdk"
@@ -30,12 +31,16 @@ func WriteWorkspaceApp404(log slog.Logger, accessURL *url.URL, rw http.ResponseW
3031
}
3132

3233
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
33-
Status: http.StatusNotFound,
34-
Title: "Application Not Found",
35-
Description: "The application or workspace you are trying to access does not exist or you do not have permission to access it.",
36-
RetryEnabled: false,
37-
DashboardURL: accessURL.String(),
38-
Warnings: warnings,
34+
Status: http.StatusNotFound,
35+
Title: "Application Not Found",
36+
Description: "The application or workspace you are trying to access does not exist or you do not have permission to access it.",
37+
Warnings: warnings,
38+
Actions: []site.Action{
39+
{
40+
URL: accessURL.String(),
41+
Text: "Back to site",
42+
},
43+
},
3944
})
4045
}
4146

@@ -60,11 +65,15 @@ func WriteWorkspaceApp500(log slog.Logger, accessURL *url.URL, rw http.ResponseW
6065
)
6166

6267
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
63-
Status: http.StatusInternalServerError,
64-
Title: "Internal Server Error",
65-
Description: "An internal server error occurred.",
66-
RetryEnabled: false,
67-
DashboardURL: accessURL.String(),
68+
Status: http.StatusInternalServerError,
69+
Title: "Internal Server Error",
70+
Description: "An internal server error occurred.",
71+
Actions: []site.Action{
72+
{
73+
URL: accessURL.String(),
74+
Text: "Back to site",
75+
},
76+
},
6877
})
6978
}
7079

@@ -85,11 +94,18 @@ func WriteWorkspaceAppOffline(log slog.Logger, accessURL *url.URL, rw http.Respo
8594
}
8695

8796
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
88-
Status: http.StatusBadGateway,
89-
Title: "Application Unavailable",
90-
Description: msg,
91-
RetryEnabled: true,
92-
DashboardURL: accessURL.String(),
97+
Status: http.StatusBadGateway,
98+
Title: "Application Unavailable",
99+
Description: msg,
100+
Actions: []site.Action{
101+
{
102+
Text: "Retry",
103+
},
104+
{
105+
URL: accessURL.String(),
106+
Text: "Back to site",
107+
},
108+
},
93109
})
94110
}
95111

@@ -110,10 +126,14 @@ func WriteWorkspaceOffline(log slog.Logger, accessURL *url.URL, rw http.Response
110126
}
111127

112128
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
113-
Status: http.StatusBadRequest,
114-
Title: "Workspace Offline",
115-
Description: fmt.Sprintf("Last workspace transition was to the %q state. Start the workspace to access its applications.", codersdk.WorkspaceTransitionStop),
116-
RetryEnabled: false,
117-
DashboardURL: accessURL.String(),
129+
Status: http.StatusBadRequest,
130+
Title: "Workspace Offline",
131+
Description: fmt.Sprintf("Last workspace transition was to the %q state. Start the workspace to access its applications.", codersdk.WorkspaceTransitionStop),
132+
Actions: []site.Action{
133+
{
134+
URL: accessURL.String(),
135+
Text: "Back to site",
136+
},
137+
},
118138
})
119139
}

coderd/workspaceapps/proxy.go

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,14 @@ func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request,
185185
Status: http.StatusBadRequest,
186186
Title: "Bad Request",
187187
Description: "Could not decrypt API key. Workspace app API key smuggling is not permitted on the primary access URL. Please remove the query parameter and try again.",
188-
// Retry is disabled because the user needs to remove the query
188+
// No retry is included because the user needs to remove the query
189189
// parameter before they try again.
190-
RetryEnabled: false,
191-
DashboardURL: s.DashboardURL.String(),
190+
Actions: []site.Action{
191+
{
192+
URL: s.DashboardURL.String(),
193+
Text: "Back to site",
194+
},
195+
},
192196
})
193197
return false
194198
}
@@ -204,10 +208,14 @@ func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request,
204208
Status: http.StatusBadRequest,
205209
Title: "Bad Request",
206210
Description: "Could not decrypt API key. Please remove the query parameter and try again.",
207-
// Retry is disabled because the user needs to remove the query
211+
// No retry is included because the user needs to remove the query
208212
// parameter before they try again.
209-
RetryEnabled: false,
210-
DashboardURL: s.DashboardURL.String(),
213+
Actions: []site.Action{
214+
{
215+
URL: s.DashboardURL.String(),
216+
Text: "Back to site",
217+
},
218+
},
211219
})
212220
return false
213221
}
@@ -224,11 +232,15 @@ func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request,
224232
// startup, but we'll check anyways.
225233
s.Logger.Error(r.Context(), "could not split invalid app hostname", slog.F("hostname", s.Hostname))
226234
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
227-
Status: http.StatusInternalServerError,
228-
Title: "Internal Server Error",
229-
Description: "The app is configured with an invalid app wildcard hostname. Please contact an administrator.",
230-
RetryEnabled: false,
231-
DashboardURL: s.DashboardURL.String(),
235+
Status: http.StatusInternalServerError,
236+
Title: "Internal Server Error",
237+
Description: "The app is configured with an invalid app wildcard hostname. Please contact an administrator.",
238+
Actions: []site.Action{
239+
{
240+
URL: s.DashboardURL.String(),
241+
Text: "Back to site",
242+
},
243+
},
232244
})
233245
return false
234246
}
@@ -274,11 +286,15 @@ func (s *Server) handleAPIKeySmuggling(rw http.ResponseWriter, r *http.Request,
274286
func (s *Server) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) {
275287
if s.DisablePathApps {
276288
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
277-
Status: http.StatusForbidden,
278-
Title: "Forbidden",
279-
Description: "Path-based applications are disabled on this Coder deployment by the administrator.",
280-
RetryEnabled: false,
281-
DashboardURL: s.DashboardURL.String(),
289+
Status: http.StatusForbidden,
290+
Title: "Forbidden",
291+
Description: "Path-based applications are disabled on this Coder deployment by the administrator.",
292+
Actions: []site.Action{
293+
{
294+
URL: s.DashboardURL.String(),
295+
Text: "Back to site",
296+
},
297+
},
282298
})
283299
return
284300
}
@@ -287,11 +303,15 @@ func (s *Server) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request)
287303
// lookup the username from token. We used to redirect by doing this lookup.
288304
if chi.URLParam(r, "user") == codersdk.Me {
289305
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
290-
Status: http.StatusNotFound,
291-
Title: "Application Not Found",
292-
Description: "Applications must be accessed with the full username, not @me.",
293-
RetryEnabled: false,
294-
DashboardURL: s.DashboardURL.String(),
306+
Status: http.StatusNotFound,
307+
Title: "Application Not Found",
308+
Description: "Applications must be accessed with the full username, not @me.",
309+
Actions: []site.Action{
310+
{
311+
URL: s.DashboardURL.String(),
312+
Text: "Back to site",
313+
},
314+
},
295315
})
296316
return
297317
}
@@ -519,11 +539,15 @@ func (s *Server) parseHostname(rw http.ResponseWriter, r *http.Request, next htt
519539
app, err := appurl.ParseSubdomainAppURL(subdomain)
520540
if err != nil {
521541
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
522-
Status: http.StatusBadRequest,
523-
Title: "Invalid Application URL",
524-
Description: fmt.Sprintf("Could not parse subdomain application URL %q: %s", subdomain, err.Error()),
525-
RetryEnabled: false,
526-
DashboardURL: s.DashboardURL.String(),
542+
Status: http.StatusBadRequest,
543+
Title: "Invalid Application URL",
544+
Description: fmt.Sprintf("Could not parse subdomain application URL %q: %s", subdomain, err.Error()),
545+
Actions: []site.Action{
546+
{
547+
URL: s.DashboardURL.String(),
548+
Text: "Back to site",
549+
},
550+
},
527551
})
528552
return appurl.ApplicationURL{}, false
529553
}
@@ -547,11 +571,18 @@ func (s *Server) proxyWorkspaceApp(rw http.ResponseWriter, r *http.Request, appT
547571
appURL, err := url.Parse(appToken.AppURL)
548572
if err != nil {
549573
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{
550-
Status: http.StatusBadRequest,
551-
Title: "Bad Request",
552-
Description: fmt.Sprintf("Application has an invalid URL %q: %s", appToken.AppURL, err.Error()),
553-
RetryEnabled: true,
554-
DashboardURL: s.DashboardURL.String(),
574+
Status: http.StatusBadRequest,
575+
Title: "Bad Request",
576+
Description: fmt.Sprintf("Application has an invalid URL %q: %s", appToken.AppURL, err.Error()),
577+
Actions: []site.Action{
578+
{
579+
Text: "Retry",
580+
},
581+
{
582+
URL: s.DashboardURL.String(),
583+
Text: "Back to site",
584+
},
585+
},
555586
})
556587
return
557588
}

enterprise/wsproxy/wsproxy.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,12 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
379379
HideStatus: true,
380380
Description: "This workspace proxy is DERP-only and cannot be used for browser connections. " +
381381
"Please use a different region directly from the dashboard. Click to be redirected!",
382-
RetryEnabled: false,
383-
DashboardURL: opts.DashboardURL.String(),
382+
Actions: []site.Action{
383+
{
384+
URL: opts.DashboardURL.String(),
385+
Text: "Back to site",
386+
},
387+
},
384388
})
385389
}
386390
serveDerpOnlyHandler := func(r chi.Router) {
@@ -422,8 +426,12 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
422426
HideStatus: true,
423427
Description: "Workspace Proxies route traffic in terminals and apps directly to your workspace. " +
424428
"This page must be loaded from the dashboard. Click to be redirected!",
425-
RetryEnabled: false,
426-
DashboardURL: opts.DashboardURL.String(),
429+
Actions: []site.Action{
430+
{
431+
URL: opts.DashboardURL.String(),
432+
Text: "Back to site",
433+
},
434+
},
427435
})
428436
})
429437

0 commit comments

Comments
 (0)