diff --git a/src/templates/src/error.html b/src/templates/src/error.html
new file mode 100644
index 00000000..27ba47cd
--- /dev/null
+++ b/src/templates/src/error.html
@@ -0,0 +1,16 @@
+{{ template "base.html" . }}
+
+{{ define "content" }}
+
+
+ Hi there, {{ if .User }}{{ .User.Name }}{{ else }}visitor{{ end }}!
+
+
+
+ We have encountered an error while processing your request.
+
+ If this keeps happening, please let us know.
+
+
+
+{{ end }}
diff --git a/src/templates/types.go b/src/templates/types.go
index e86e7d75..248ef5ad 100644
--- a/src/templates/types.go
+++ b/src/templates/types.go
@@ -6,14 +6,15 @@ import (
)
type BaseData struct {
- Title string
- CanonicalLink string
- OpenGraphItems []OpenGraphItem
- BackgroundImage BackgroundImage
- Theme string
- BodyClasses []string
- Breadcrumbs []Breadcrumb
- Notices []Notice
+ Title string
+ CanonicalLink string
+ OpenGraphItems []OpenGraphItem
+ BackgroundImage BackgroundImage
+ Theme string
+ BodyClasses []string
+ Breadcrumbs []Breadcrumb
+ Notices []Notice
+ ReportIssueMailto string
CurrentUrl string
LoginPageUrl string
diff --git a/src/website/auth.go b/src/website/auth.go
index ce325978..3eab2039 100644
--- a/src/website/auth.go
+++ b/src/website/auth.go
@@ -53,7 +53,7 @@ func Login(c *RequestContext) ResponseData {
form, err := c.GetFormValues()
if err != nil {
- return ErrorResponse(http.StatusBadRequest, NewSafeError(err, "request must contain form data"))
+ return c.ErrorResponse(http.StatusBadRequest, NewSafeError(err, "request must contain form data"))
}
redirect := form.Get("redirect")
@@ -84,7 +84,7 @@ func Login(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
return showLoginWithFailure(c, redirect)
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to look up user by username"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to look up user by username"))
}
}
user := userRow.(*models.User)
@@ -92,7 +92,7 @@ func Login(c *RequestContext) ResponseData {
success, err := tryLogin(c, user, password)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if !success {
@@ -106,7 +106,7 @@ func Login(c *RequestContext) ResponseData {
res := c.Redirect(redirect, http.StatusSeeOther)
err = loginUser(c, user, &res)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
return res
}
@@ -179,7 +179,7 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
userAlreadyExists = false
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
}
}
@@ -200,7 +200,7 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
emailAlreadyExists = false
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user"))
}
}
c.Perf.EndBlock()
@@ -215,7 +215,7 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
c.Perf.StartBlock("SQL", "Create user and one time token")
tx, err := c.Conn.Begin(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
}
defer tx.Rollback(c.Context())
@@ -231,7 +231,7 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
username, emailAddress, hashed.String(), now, displayName, c.GetIP(),
).Scan(&newUserId)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to store user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to store user"))
}
ott := models.GenerateToken()
@@ -247,7 +247,7 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
newUserId,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to store one-time token"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to store one-time token"))
}
c.Perf.EndBlock()
@@ -257,13 +257,13 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
}
err = email.SendRegistrationEmail(emailAddress, mailName, username, ott, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send registration email"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send registration email"))
}
c.Perf.StartBlock("SQL", "Commit user")
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit user to the db"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit user to the db"))
}
c.Perf.EndBlock()
return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther)
@@ -349,7 +349,7 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
success, err := tryLogin(c, validationResult.User, password)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
} else if !success {
var res ResponseData
baseData := getBaseData(c)
@@ -366,7 +366,7 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
c.Perf.StartBlock("SQL", "Updating user status and deleting token")
tx, err := c.Conn.Begin(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
}
defer tx.Rollback(c.Context())
@@ -380,7 +380,7 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
validationResult.User.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user status"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user status"))
}
_, err = tx.Exec(c.Context(),
@@ -390,12 +390,12 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
validationResult.OneTimeToken.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete one time token"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete one time token"))
}
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit transaction"))
}
c.Perf.EndBlock()
@@ -403,7 +403,7 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
res.AddFutureNotice("success", "You've completed your registration successfully!")
err = loginUser(c, validationResult.User, &res)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
return res
}
@@ -464,7 +464,7 @@ func RequestPasswordResetSubmit(c *RequestContext) ResponseData {
c.Perf.EndBlock()
if err != nil {
if !errors.Is(err, db.ErrNoMatchingRows) {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to look up user by username"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to look up user by username"))
}
}
if userRow != nil {
@@ -487,7 +487,7 @@ func RequestPasswordResetSubmit(c *RequestContext) ResponseData {
c.Perf.EndBlock()
if err != nil {
if !errors.Is(err, db.ErrNoMatchingRows) {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch onetimetoken for user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch onetimetoken for user"))
}
}
var resetToken *models.OneTimeToken
@@ -508,7 +508,7 @@ func RequestPasswordResetSubmit(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete onetimetoken"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete onetimetoken"))
}
resetToken = nil
}
@@ -530,13 +530,13 @@ func RequestPasswordResetSubmit(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create onetimetoken"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create onetimetoken"))
}
resetToken = tokenRow.(*models.OneTimeToken)
err = email.SendPasswordReset(user.Email, user.BestName(), user.Username, resetToken.Content, resetToken.Expires, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send email"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send email"))
}
}
}
@@ -624,7 +624,7 @@ func DoPasswordResetSubmit(c *RequestContext) ResponseData {
c.Perf.StartBlock("SQL", "Update user's password and delete reset token")
tx, err := c.Conn.Begin(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to start db transaction"))
}
defer tx.Rollback(c.Context())
@@ -638,7 +638,7 @@ func DoPasswordResetSubmit(c *RequestContext) ResponseData {
validationResult.User.ID,
)
if err != nil || tag.RowsAffected() == 0 {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user's password"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user's password"))
}
if validationResult.User.Status == models.UserStatusInactive {
@@ -652,7 +652,7 @@ func DoPasswordResetSubmit(c *RequestContext) ResponseData {
validationResult.User.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user's status"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user's status"))
}
}
@@ -664,12 +664,12 @@ func DoPasswordResetSubmit(c *RequestContext) ResponseData {
validationResult.OneTimeToken.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete onetimetoken"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete onetimetoken"))
}
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit password reset to the db"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit password reset to the db"))
}
c.Perf.EndBlock()
@@ -677,7 +677,7 @@ func DoPasswordResetSubmit(c *RequestContext) ResponseData {
res.AddFutureNotice("success", "Password changed successfully.")
err = loginUser(c, validationResult.User, &res)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
return res
}
diff --git a/src/website/blogs.go b/src/website/blogs.go
index 85c5111c..8b264098 100644
--- a/src/website/blogs.go
+++ b/src/website/blogs.go
@@ -50,7 +50,7 @@ func BlogIndex(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch total number of blog posts"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch total number of blog posts"))
}
numPages := NumPages(numPosts, postsPerPage)
@@ -88,7 +88,7 @@ func BlogIndex(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch blog posts for index"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch blog posts for index"))
}
var entries []blogIndexEntry
@@ -112,7 +112,7 @@ func BlogIndex(c *RequestContext) ResponseData {
isProjectOwner := false
owners, err := FetchProjectOwners(c, c.CurrentProject.ID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project owners"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project owners"))
}
for _, owner := range owners {
if owner.ID == c.CurrentUser.ID {
@@ -232,7 +232,7 @@ func BlogNewThreadSubmit(c *RequestContext) ResponseData {
err = c.Req.ParseForm()
if err != nil {
- return ErrorResponse(http.StatusBadRequest, oops.New(err, "the form data was invalid"))
+ return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "the form data was invalid"))
}
title := c.Req.Form.Get("title")
unparsed := c.Req.Form.Get("body")
@@ -266,7 +266,7 @@ func BlogNewThreadSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new blog post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new blog post"))
}
newThreadUrl := hmnurl.BuildBlogThread(c.CurrentProject.Slug, threadId, title)
@@ -339,7 +339,7 @@ func BlogPostEditSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit blog post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit blog post"))
}
postUrl := hmnurl.BuildBlogThreadWithPostHash(c.CurrentProject.Slug, cd.ThreadID, postData.Thread.Title, cd.PostID)
@@ -385,7 +385,7 @@ func BlogPostReplySubmit(c *RequestContext) ResponseData {
err = c.Req.ParseForm()
if err != nil {
- return ErrorResponse(http.StatusBadRequest, oops.New(nil, "the form data was invalid"))
+ return c.ErrorResponse(http.StatusBadRequest, oops.New(nil, "the form data was invalid"))
}
unparsed := c.Req.Form.Get("body")
if unparsed == "" {
@@ -396,7 +396,7 @@ func BlogPostReplySubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to blog post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to blog post"))
}
newPostUrl := hmnurl.BuildBlogPost(c.CurrentProject.Slug, cd.ThreadID, newPostId)
@@ -462,7 +462,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete post"))
}
if threadDeleted {
diff --git a/src/website/discord.go b/src/website/discord.go
index 25040841..b03cd212 100644
--- a/src/website/discord.go
+++ b/src/website/discord.go
@@ -53,19 +53,19 @@ func DiscordOAuthCallback(c *RequestContext) ResponseData {
code := query.Get("code")
res, err := discord.ExchangeOAuthCode(c.Context(), code, hmnurl.BuildDiscordOAuthCallback())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to exchange Discord authorization code"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to exchange Discord authorization code"))
}
expiry := time.Now().Add(time.Duration(res.ExpiresIn) * time.Second)
user, err := discord.GetCurrentUserAsOAuth(c.Context(), res.AccessToken)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch Discord user info"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch Discord user info"))
}
// Add the role on Discord
err = discord.AddGuildMemberRole(c.Context(), user.ID, config.Config.Discord.MemberRoleID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to add member role"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to add member role"))
}
// Add the user to our database
@@ -85,7 +85,7 @@ func DiscordOAuthCallback(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save new Discord user info"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save new Discord user info"))
}
return c.Redirect(hmnurl.BuildUserSettings("discord"), http.StatusSeeOther)
@@ -110,7 +110,7 @@ func DiscordUnlink(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
return c.Redirect(hmnurl.BuildUserSettings("discord"), http.StatusSeeOther)
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get Discord user for unlink"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get Discord user for unlink"))
}
}
discordUser := iDiscordUser.(*models.DiscordUser)
@@ -123,12 +123,12 @@ func DiscordUnlink(c *RequestContext) ResponseData {
discordUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete Discord user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete Discord user"))
}
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit Discord user delete"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit Discord user delete"))
}
err = discord.RemoveGuildMemberRole(c.Context(), discordUser.UserID, config.Config.Discord.MemberRoleID)
@@ -149,13 +149,13 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData {
c.Logger.Warn().Msg("could not do showcase backlog because no discord user exists")
return c.Redirect(hmnurl.BuildUserProfile(c.CurrentUser.Username), http.StatusSeeOther)
} else if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get discord user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get discord user"))
}
duser := iduser.(*models.DiscordUser)
ok, err := discord.AllowedToCreateMessageSnippet(c.Context(), c.Conn, duser.UserID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if !ok {
diff --git a/src/website/feed.go b/src/website/feed.go
index bae6fe62..aec49462 100644
--- a/src/website/feed.go
+++ b/src/website/feed.go
@@ -45,7 +45,7 @@ func Feed(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get count of feed posts"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get count of feed posts"))
}
numPages := int(math.Ceil(float64(numPosts) / postsPerPage))
@@ -87,7 +87,7 @@ func Feed(c *RequestContext) ResponseData {
posts, err := fetchAllPosts(c, lineageBuilder, currentUserId, howManyPostsToSkip, postsPerPage)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
}
baseData := getBaseData(c)
@@ -165,7 +165,7 @@ func AtomFeed(c *RequestContext) ResponseData {
posts, err := fetchAllPosts(c, lineageBuilder, nil, 0, itemsPerFeed)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
}
feedData.Posts = posts
@@ -203,7 +203,7 @@ func AtomFeed(c *RequestContext) ResponseData {
itemsPerFeed,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects"))
}
var projectIds []int
projectMap := make(map[int]int) // map[project id]index in slice
@@ -235,7 +235,7 @@ func AtomFeed(c *RequestContext) ResponseData {
projectIds,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects owners"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed projects owners"))
}
for _, res := range owners.ToSlice() {
owner := res.(*ownerResult)
@@ -277,7 +277,7 @@ func AtomFeed(c *RequestContext) ResponseData {
itemsPerFeed,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
}
snippetQuerySlice := snippetQueryResult.ToSlice()
for _, s := range snippetQuerySlice {
diff --git a/src/website/forums.go b/src/website/forums.go
index 1e2ece10..00e96fe7 100644
--- a/src/website/forums.go
+++ b/src/website/forums.go
@@ -330,7 +330,7 @@ func ForumMarkRead(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to mark all posts as read"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to mark all posts as read"))
}
// Delete thread unread info
@@ -342,7 +342,7 @@ func ForumMarkRead(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete thread unread info"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete thread unread info"))
}
// Delete subforum unread info
@@ -354,7 +354,7 @@ func ForumMarkRead(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete subforum unread info"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete subforum unread info"))
}
} else {
c.Perf.StartBlock("SQL", "Update SLRIs")
@@ -373,7 +373,7 @@ func ForumMarkRead(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update forum slris"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update forum slris"))
}
c.Perf.StartBlock("SQL", "Delete TLRIs")
@@ -394,13 +394,13 @@ func ForumMarkRead(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete unnecessary tlris"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete unnecessary tlris"))
}
}
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit SLRI/TLRI updates"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to commit SLRI/TLRI updates"))
}
var redirUrl string
@@ -503,7 +503,7 @@ func ForumThread(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update forum tlri"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update forum tlri"))
}
}
@@ -546,7 +546,7 @@ func ForumPostRedirect(c *RequestContext) ResponseData {
cd.ThreadID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch post ids"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch post ids"))
}
postQuerySlice := postQueryResult.ToSlice()
c.Perf.EndBlock()
@@ -574,7 +574,7 @@ func ForumPostRedirect(c *RequestContext) ResponseData {
cd.ThreadID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch thread title"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch thread title"))
}
c.Perf.EndBlock()
threadTitle := threadTitleQueryResult.(*threadTitleQuery).ThreadTitle
@@ -625,7 +625,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
err = c.Req.ParseForm()
if err != nil {
- return ErrorResponse(http.StatusBadRequest, oops.New(err, "the form data was invalid"))
+ return c.ErrorResponse(http.StatusBadRequest, oops.New(err, "the form data was invalid"))
}
title := c.Req.Form.Get("title")
unparsed := c.Req.Form.Get("body")
@@ -665,7 +665,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread"))
}
newThreadUrl := hmnurl.BuildForumThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), threadId, title, 1)
@@ -711,7 +711,7 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData {
err = c.Req.ParseForm()
if err != nil {
- return ErrorResponse(http.StatusBadRequest, oops.New(nil, "the form data was invalid"))
+ return c.ErrorResponse(http.StatusBadRequest, oops.New(nil, "the form data was invalid"))
}
unparsed := c.Req.Form.Get("body")
if unparsed == "" {
@@ -722,7 +722,7 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to forum post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to forum post"))
}
newPostUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, newPostId)
@@ -784,7 +784,7 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit forum post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit forum post"))
}
postUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
@@ -846,7 +846,7 @@ func ForumPostDeleteSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to delete post"))
}
if threadDeleted {
diff --git a/src/website/landing.go b/src/website/landing.go
index 6baaacf8..09952e73 100644
--- a/src/website/landing.go
+++ b/src/website/landing.go
@@ -67,7 +67,7 @@ func Index(c *RequestContext) ResponseData {
numProjectsToGet*2, // hedge your bets against projects that don't have any content
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get projects for home page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get projects for home page"))
}
defer iterProjects.Close()
@@ -274,7 +274,7 @@ func Index(c *RequestContext) ResponseData {
models.ThreadTypeProjectBlogPost,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
}
newsPostResult := newsPostRow.(*newsPostQuery)
c.Perf.EndBlock()
@@ -299,7 +299,7 @@ func Index(c *RequestContext) ResponseData {
`,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
}
snippetQuerySlice := snippetQueryResult.ToSlice()
showcaseItems := make([]templates.TimelineItem, 0, len(snippetQuerySlice))
@@ -343,7 +343,7 @@ func Index(c *RequestContext) ResponseData {
WheelJamUrl: hmnurl.BuildJamIndex(),
}, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render landing page template"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render landing page template"))
}
return res
diff --git a/src/website/podcast.go b/src/website/podcast.go
index 536f3f24..3f820409 100644
--- a/src/website/podcast.go
+++ b/src/website/podcast.go
@@ -32,7 +32,7 @@ type PodcastIndexData struct {
func PodcastIndex(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, true, "")
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil {
@@ -41,7 +41,7 @@ func PodcastIndex(c *RequestContext) ResponseData {
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
baseData := getBaseData(c)
@@ -63,7 +63,7 @@ func PodcastIndex(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("podcast_index.html", podcastIndexData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast index page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast index page"))
}
return res
}
@@ -76,12 +76,12 @@ type PodcastEditData struct {
func PodcastEdit(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, false, "")
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || !canEdit {
@@ -99,7 +99,7 @@ func PodcastEdit(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("podcast_edit.html", podcastEditData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast edit page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast edit page"))
}
return res
}
@@ -107,12 +107,12 @@ func PodcastEdit(c *RequestContext) ResponseData {
func PodcastEditSubmit(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, false, "")
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || !canEdit {
@@ -128,7 +128,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
c.Perf.EndBlock()
if err != nil {
// NOTE(asaf): The error for exceeding the max filesize doesn't have a special type, so we can't easily detect it here.
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to parse form"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to parse form"))
}
title := c.Req.Form.Get("title")
@@ -143,7 +143,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
c.Perf.StartBlock("SQL", "Updating podcast")
tx, err := c.Conn.Begin(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to start db transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to start db transaction"))
}
defer tx.Rollback(c.Context())
@@ -153,7 +153,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
if errors.As(err, &rejectErr) {
return RejectRequest(c, rejectErr.Error())
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to save podcast image"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to save podcast image"))
}
}
@@ -173,7 +173,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
podcastResult.Podcast.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to update podcast"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to update podcast"))
}
} else {
_, err = tx.Exec(c.Context(),
@@ -192,7 +192,7 @@ func PodcastEditSubmit(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to commit db transaction"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to commit db transaction"))
}
res := c.Redirect(hmnurl.BuildPodcastEdit(c.CurrentProject.Slug), http.StatusSeeOther)
@@ -212,7 +212,7 @@ func PodcastEpisode(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, true, episodeGUIDStr)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || len(podcastResult.Episodes) == 0 {
@@ -246,7 +246,7 @@ func PodcastEpisode(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("podcast_episode.html", podcastEpisodeData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode page"))
}
return res
}
@@ -264,12 +264,12 @@ type PodcastEpisodeEditData struct {
func PodcastEpisodeNew(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, false, "")
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || !canEdit {
@@ -278,7 +278,7 @@ func PodcastEpisodeNew(c *RequestContext) ResponseData {
episodeFiles, err := GetEpisodeFiles(c.CurrentProject.Slug)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
}
podcast := templates.PodcastToTemplate(c.CurrentProject.Slug, podcastResult.Podcast, "")
@@ -291,7 +291,7 @@ func PodcastEpisodeNew(c *RequestContext) ResponseData {
EpisodeFiles: episodeFiles,
}, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode new page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode new page"))
}
return res
}
@@ -304,12 +304,12 @@ func PodcastEpisodeEdit(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, true, episodeGUIDStr)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || len(podcastResult.Episodes) == 0 || !canEdit {
@@ -318,7 +318,7 @@ func PodcastEpisodeEdit(c *RequestContext) ResponseData {
episodeFiles, err := GetEpisodeFiles(c.CurrentProject.Slug)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
}
episode := podcastResult.Episodes[0]
@@ -339,7 +339,7 @@ func PodcastEpisodeEdit(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("podcast_episode_edit.html", podcastEpisodeEditData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode edit page"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast episode edit page"))
}
return res
}
@@ -350,12 +350,12 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
isEdit := found && episodeGUIDStr != ""
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, isEdit, episodeGUIDStr)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canEdit, err := CanEditProject(c, c.CurrentUser, podcastResult.Podcast.ProjectID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil || (isEdit && len(podcastResult.Episodes) == 0) || !canEdit {
@@ -366,7 +366,7 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
episodeFiles, err := GetEpisodeFiles(c.CurrentProject.Slug)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to fetch podcast episode file list"))
}
c.Req.ParseForm()
@@ -399,7 +399,7 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
c.Perf.StartBlock("MP3", "Parsing mp3 file for duration")
file, err := os.Open(fmt.Sprintf("public/media/podcast/%s/%s", c.CurrentProject.Slug, episodeFile))
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to open podcast file"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to open podcast file"))
}
mp3Decoder := mp3.NewDecoder(file)
@@ -421,7 +421,7 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
file.Close()
c.Perf.EndBlock()
if decodingError != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to decode mp3 file"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to decode mp3 file"))
}
c.Perf.StartBlock("MARKDOWN", "Parsing description")
@@ -455,7 +455,7 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to update podcast episode"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to update podcast episode"))
}
} else {
guid := uuid.New()
@@ -480,7 +480,7 @@ func PodcastEpisodeSubmit(c *RequestContext) ResponseData {
)
c.Perf.EndBlock()
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to create podcast episode"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to create podcast episode"))
}
}
@@ -504,7 +504,7 @@ type PodcastRSSData struct {
func PodcastRSS(c *RequestContext) ResponseData {
podcastResult, err := FetchPodcast(c, c.CurrentProject.ID, true, "")
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
if podcastResult.Podcast == nil {
@@ -529,7 +529,7 @@ func PodcastRSS(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("podcast.xml", podcastRSSData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast RSS"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render podcast RSS"))
}
return res
}
diff --git a/src/website/projects.go b/src/website/projects.go
index eed0196c..60157559 100644
--- a/src/website/projects.go
+++ b/src/website/projects.go
@@ -71,7 +71,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
models.VisibleProjectLifecycles,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects"))
}
allProjectsSlice := allProjects.ToSlice()
c.Perf.EndBlock()
@@ -112,7 +112,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user projects"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user projects"))
}
for _, project := range userProjectsResult.ToSlice() {
p := project.(*UserProjectQuery).Project
@@ -244,7 +244,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
return FourOhFour(c)
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project by slug"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project by slug"))
}
}
project = &projectQueryResult.(*projectQuery).Project
@@ -262,7 +262,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
owners, err := FetchProjectOwners(c, project.ID)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, err)
+ return c.ErrorResponse(http.StatusInternalServerError, err)
}
canView := false
@@ -312,7 +312,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
project.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch screenshots for project"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch screenshots for project"))
}
c.Perf.EndBlock()
@@ -332,7 +332,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
project.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project links"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project links"))
}
c.Perf.EndBlock()
@@ -363,7 +363,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
maxRecentActivity,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project posts"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project posts"))
}
c.Perf.EndBlock()
@@ -445,7 +445,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
var res ResponseData
err = res.WriteTemplate("project_homepage.html", projectHomepageData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render project homepage template"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render project homepage template"))
}
return res
}
diff --git a/src/website/requesthandling.go b/src/website/requesthandling.go
index 6ffbfe1b..a1cefe6c 100644
--- a/src/website/requesthandling.go
+++ b/src/website/requesthandling.go
@@ -246,6 +246,15 @@ func (c *RequestContext) Redirect(dest string, code int) ResponseData {
return res
}
+func (c *RequestContext) ErrorResponse(status int, errs ...error) ResponseData {
+ res := ResponseData{
+ StatusCode: status,
+ Errors: errs,
+ }
+ res.MustWriteTemplate("error.html", getBaseData(c), c.Perf)
+ return res
+}
+
type ResponseData struct {
StatusCode int
Body *bytes.Buffer
@@ -304,13 +313,6 @@ func (rd *ResponseData) MustWriteTemplate(name string, data interface{}, rp *per
}
}
-func ErrorResponse(status int, errs ...error) ResponseData {
- return ResponseData{
- StatusCode: status,
- Errors: errs,
- }
-}
-
func doRequest(rw http.ResponseWriter, c *RequestContext, h Handler) {
defer func() {
/*
@@ -320,6 +322,7 @@ func doRequest(rw http.ResponseWriter, c *RequestContext, h Handler) {
if recovered := recover(); recovered != nil {
rw.WriteHeader(http.StatusInternalServerError)
logging.LogPanicValue(c.Logger, recovered, "request panicked and was not handled")
+ rw.Write([]byte("There was a problem handling your request.\nPlease notify an admin at team@handmade.network"))
}
}()
diff --git a/src/website/routes.go b/src/website/routes.go
index 4a7c6b99..10aef4c6 100644
--- a/src/website/routes.go
+++ b/src/website/routes.go
@@ -38,6 +38,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
defer logPerf()
defer LogContextErrors(c, &res)
+ defer MiddlewarePanicCatcher(c, &res)
return h(c)
}
@@ -53,6 +54,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
defer logPerf()
defer LogContextErrors(c, &res)
+ defer MiddlewarePanicCatcher(c, &res)
defer storeNoticesInCookie(c, &res)
@@ -74,6 +76,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
defer logPerf()
defer LogContextErrors(c, &res)
+ defer MiddlewarePanicCatcher(c, &res)
defer storeNoticesInCookie(c, &res)
@@ -284,6 +287,8 @@ func getBaseData(c *RequestContext) templates.BaseData {
Session: templateSession,
Notices: notices,
+ ReportIssueMailto: "team@handmade.network",
+
OpenGraphItems: buildDefaultOpenGraphItems(c.CurrentProject),
IsProjectPage: !c.CurrentProject.IsHMN(),
@@ -355,7 +360,7 @@ func FetchProjectBySlug(ctx context.Context, conn *pgxpool.Pool, slug string) (*
func ProjectCSS(c *RequestContext) ResponseData {
color := c.URL().Query().Get("color")
if color == "" {
- return ErrorResponse(http.StatusBadRequest, NewSafeError(nil, "You must provide a 'color' parameter.\n"))
+ return c.ErrorResponse(http.StatusBadRequest, NewSafeError(nil, "You must provide a 'color' parameter.\n"))
}
baseData := getBaseData(c)
@@ -386,7 +391,7 @@ func ProjectCSS(c *RequestContext) ResponseData {
res.Header().Add("Content-Type", "text/css")
err := res.WriteTemplate("project.css", templateData, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to generate project CSS"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to generate project CSS"))
}
return res
@@ -423,7 +428,7 @@ func RejectRequest(c *RequestContext, reason string) ResponseData {
RejectReason: reason,
}, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to render reject template"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "Failed to render reject template"))
}
return res
}
@@ -439,7 +444,7 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
dbProject, err := FetchProjectBySlug(c.Context(), c.Conn, slug)
if err != nil {
- return false, ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch current project"))
+ return false, c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch current project"))
}
if dbProject == nil {
return false, c.Redirect(hmnurl.BuildHomepage(), http.StatusSeeOther)
@@ -453,7 +458,7 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) {
if err == nil {
user, session, err := getCurrentUserAndSession(c, sessionCookie.Value)
if err != nil {
- return false, ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get current user"))
+ return false, c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get current user"))
}
c.CurrentUser = user
@@ -539,6 +544,19 @@ func LogContextErrors(c *RequestContext, res *ResponseData) {
}
}
+func MiddlewarePanicCatcher(c *RequestContext, res *ResponseData) {
+ if recovered := recover(); recovered != nil {
+ maybeError, ok := recovered.(*error)
+ var err error
+ if ok {
+ err = *maybeError
+ } else {
+ err = oops.New(nil, fmt.Sprintf("Recovered from panic with value: %v", recovered))
+ }
+ *res = c.ErrorResponse(http.StatusInternalServerError, err)
+ }
+}
+
const NoticesCookieName = "hmn_notices"
func getNoticesFromCookie(c *RequestContext) []templates.Notice {
diff --git a/src/website/routes_test.go b/src/website/routes_test.go
index 0e4d088d..bbd822a9 100644
--- a/src/website/routes_test.go
+++ b/src/website/routes_test.go
@@ -35,7 +35,7 @@ func TestLogContextErrors(t *testing.T) {
}
routes.GET(regexp.MustCompile("^/test$"), func(c *RequestContext) ResponseData {
- return ErrorResponse(http.StatusInternalServerError, err1, err2)
+ return c.ErrorResponse(http.StatusInternalServerError, err1, err2)
})
srv := httptest.NewServer(router)
diff --git a/src/website/showcase.go b/src/website/showcase.go
index f7b7f015..3577fa15 100644
--- a/src/website/showcase.go
+++ b/src/website/showcase.go
@@ -36,7 +36,7 @@ func Showcase(c *RequestContext) ResponseData {
`,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets"))
}
snippetQuerySlice := snippetQueryResult.ToSlice()
showcaseItems := make([]templates.TimelineItem, 0, len(snippetQuerySlice))
diff --git a/src/website/snippet.go b/src/website/snippet.go
index 0a279331..08d5c86d 100644
--- a/src/website/snippet.go
+++ b/src/website/snippet.go
@@ -53,7 +53,7 @@ func Snippet(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
return FourOhFour(c)
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippet"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippet"))
}
}
c.Perf.EndBlock()
@@ -111,7 +111,7 @@ func Snippet(c *RequestContext) ResponseData {
Snippet: snippet,
}, c.Perf)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render snippet template"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render snippet template"))
}
return res
}
diff --git a/src/website/user.go b/src/website/user.go
index dc35a399..01d7b550 100644
--- a/src/website/user.go
+++ b/src/website/user.go
@@ -59,7 +59,7 @@ func UserProfile(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
return FourOhFour(c)
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user: %s", username))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user: %s", username))
}
}
profileUser = userResult.(*models.User)
@@ -80,7 +80,7 @@ func UserProfile(c *RequestContext) ResponseData {
profileUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch links for user: %s", username))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch links for user: %s", username))
}
userLinksSlice := userLinkQueryResult.ToSlice()
profileUserLinks := make([]templates.Link, 0, len(userLinksSlice))
@@ -108,7 +108,7 @@ func UserProfile(c *RequestContext) ResponseData {
models.VisibleProjectLifecycles,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects for user: %s", username))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects for user: %s", username))
}
projectQuerySlice := projectQueryResult.ToSlice()
templateProjects := make([]templates.Project, 0, len(projectQuerySlice))
@@ -139,7 +139,7 @@ func UserProfile(c *RequestContext) ResponseData {
models.VisibleProjectLifecycles,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch posts for user: %s", username))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch posts for user: %s", username))
}
postQuerySlice := postQueryResult.ToSlice()
c.Perf.EndBlock()
@@ -163,7 +163,7 @@ func UserProfile(c *RequestContext) ResponseData {
profileUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for user: %s", username))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for user: %s", username))
}
snippetQuerySlice := snippetQueryResult.ToSlice()
c.Perf.EndBlock()
@@ -272,7 +272,7 @@ func UserSettings(c *RequestContext) ResponseData {
c.CurrentUser.ID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
}
links := ilinks.ToSlice()
@@ -295,7 +295,7 @@ func UserSettings(c *RequestContext) ResponseData {
if errors.Is(err, db.ErrNoMatchingRows) {
// this is fine, but don't fetch any more messages
} else if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user's Discord account"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user's Discord account"))
} else {
duser := iduser.(*models.DiscordUser)
tmp := templates.DiscordUserToTemplate(duser)
@@ -316,7 +316,7 @@ func UserSettings(c *RequestContext) ResponseData {
config.Config.Discord.ShowcaseChannelID,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check for unsaved user messages"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check for unsaved user messages"))
}
}
@@ -402,7 +402,7 @@ func UserSettingsSave(c *RequestContext) ResponseData {
discordDeleteSnippetOnMessageDelete,
)
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update user"))
}
// Process links
@@ -460,7 +460,7 @@ func UserSettingsSave(c *RequestContext) ResponseData {
if errors.As(err, &rejectErr) {
return RejectRequest(c, rejectErr.Error())
} else {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save new avatar"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save new avatar"))
}
}
@@ -468,7 +468,7 @@ func UserSettingsSave(c *RequestContext) ResponseData {
err = tx.Commit(c.Context())
if err != nil {
- return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save user settings"))
+ return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to save user settings"))
}
return c.Redirect(hmnurl.BuildUserSettings(""), http.StatusSeeOther)
@@ -489,7 +489,7 @@ func updatePassword(c *RequestContext, tx pgx.Tx, old, new, confirm string) *Res
ok, err := auth.CheckPassword(old, oldHashedPassword)
if err != nil {
- res := ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check user's password"))
+ res := c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to check user's password"))
return &res
}
@@ -501,7 +501,7 @@ func updatePassword(c *RequestContext, tx pgx.Tx, old, new, confirm string) *Res
newHashedPassword := auth.HashPassword(new)
err = auth.UpdatePassword(c.Context(), tx, c.CurrentUser.Username, newHashedPassword)
if err != nil {
- res := ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update password"))
+ res := c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to update password"))
return &res
}