When returning HTTP errors, send toasts if request is HTMX

This commit is contained in:
Alessio 2024-08-19 14:43:28 -07:00
parent f8988abef1
commit 5d0fd63591
11 changed files with 47 additions and 36 deletions

View File

@ -15,7 +15,7 @@ func (app *Application) Bookmarks(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Has("scrape") {
if app.IsScrapingDisabled {
app.InfoLog.Printf("Would have scraped: %s", r.URL.Path)
http.Error(w, "Scraping is disabled (are you logged in?)", 401)
http.Error(w, "Scraping is disabled (are you logged in?)", 401) // TODO: toast
return
}
@ -33,7 +33,7 @@ func (app *Application) Bookmarks(w http.ResponseWriter, r *http.Request) {
c := persistence.NewUserFeedBookmarksCursor(app.ActiveUser.Handle)
err := parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}

View File

@ -17,12 +17,12 @@ func (app *Application) UserFollow(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(parts) != 2 {
app.error_400_with_message(w, "Bad URL: "+r.URL.Path)
app.error_400_with_message(w, r, "Bad URL: "+r.URL.Path)
return
}
user, err := app.Profile.GetUserByHandle(scraper.UserHandle(parts[1]))
if err != nil {
app.error_404(w)
app.error_404(w, r)
return
}
@ -41,12 +41,12 @@ func (app *Application) UserUnfollow(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(parts) != 2 {
app.error_400_with_message(w, "Bad URL: "+r.URL.Path)
app.error_400_with_message(w, r, "Bad URL: "+r.URL.Path)
return
}
user, err := app.Profile.GetUserByHandle(scraper.UserHandle(parts[1]))
if err != nil {
app.error_404(w)
app.error_404(w, r)
return
}

View File

@ -39,7 +39,7 @@ func (app *Application) ListDetailFeed(w http.ResponseWriter, r *http.Request) {
c := persistence.NewListCursor(list.ID)
err := parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}
feed, err := app.Profile.NextPage(c, app.ActiveUser.ID)
@ -98,7 +98,7 @@ func (app *Application) ListDetail(w http.ResponseWriter, r *http.Request) {
case "remove_user":
app.ListRemoveUser(w, r)
default:
app.error_404(w)
app.error_404(w, r)
}
}
@ -109,7 +109,7 @@ func (app *Application) ListAddUser(w http.ResponseWriter, r *http.Request) {
}
user, err := app.Profile.GetUserByHandle(UserHandle(handle))
if err != nil {
app.error_400_with_message(w, "Fetch user: "+err.Error())
app.error_400_with_message(w, r, "Fetch user: "+err.Error())
return
}
list := get_list_from_context(r.Context())
@ -124,7 +124,7 @@ func (app *Application) ListRemoveUser(w http.ResponseWriter, r *http.Request) {
}
user, err := app.Profile.GetUserByHandle(UserHandle(handle))
if err != nil {
app.error_400_with_message(w, "Fetch user: "+err.Error())
app.error_400_with_message(w, r, "Fetch user: "+err.Error())
return
}
list := get_list_from_context(r.Context())
@ -141,12 +141,12 @@ func (app *Application) Lists(w http.ResponseWriter, r *http.Request) {
if parts[0] != "" { // If there's an ID param
_list_id, err := strconv.Atoi(parts[0])
if err != nil {
app.error_400_with_message(w, "List ID must be a number")
app.error_400_with_message(w, r, "List ID must be a number")
return
}
list, err := app.Profile.GetListById(ListID(_list_id))
if err != nil {
app.error_404(w)
app.error_404(w, r)
return
}
req_with_ctx := r.WithContext(add_list_to_context(r.Context(), list))

View File

@ -71,7 +71,7 @@ func (app *Application) after_login(w http.ResponseWriter, r *http.Request, api
// Ensure the user is downloaded
user, err := scraper.GetUser(api.UserHandle)
if err != nil {
app.error_404(w)
app.error_404(w, r)
return
}
panic_if(app.Profile.SaveUser(&user))
@ -115,7 +115,7 @@ func (app *Application) ChangeSession(w http.ResponseWriter, r *http.Request) {
panic_if(json.Unmarshal(formdata, &form)) // TODO: HTTP 400 not 500
err = app.SetActiveUser(scraper.UserHandle(form.AccountName))
if err != nil {
app.error_400_with_message(w, fmt.Sprintf("User not in database: %s", form.AccountName))
app.error_400_with_message(w, r, fmt.Sprintf("User not in database: %s", form.AccountName))
return
}
data := Notifications{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}

View File

@ -202,7 +202,7 @@ func (app *Application) Messages(w http.ResponseWriter, r *http.Request) {
app.traceLog.Printf("'Messages' handler (path: %q)", r.URL.Path)
if app.ActiveUser.ID == 0 {
app.error_401(w)
app.error_401(w, r)
return
}

View File

@ -50,7 +50,7 @@ func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
// Redirect GET param "q" to use a URL param instead
search_text = r.URL.Query().Get("q")
if search_text == "" {
app.error_400_with_message(w, "Empty search query")
app.error_400_with_message(w, r, "Empty search query")
return
// TODO: return an actual page
}
@ -114,19 +114,19 @@ func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
c, err := persistence.NewCursorFromSearchQuery(search_text)
if err != nil {
app.error_400_with_message(w, err.Error())
app.error_400_with_message(w, r, err.Error())
return
// TODO: return actual page
}
err = parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}
var is_ok bool
c.SortOrder, is_ok = persistence.SortOrderFromString(r.URL.Query().Get("sort-order"))
if !is_ok && r.URL.Query().Get("sort-order") != "" {
app.error_400_with_message(w, "Invalid sort order")
app.error_400_with_message(w, r, "Invalid sort order")
}
feed, err := app.Profile.NextPage(c, app.ActiveUser.ID)

View File

@ -9,7 +9,7 @@ func (app *Application) NavSidebarPollUpdates(w http.ResponseWriter, r *http.Req
// Must be an HTMX request, otherwise HTTP 400
if !is_htmx(r) {
app.error_400_with_message(w, "This is an HTMX-only endpoint, not a page")
app.error_400_with_message(w, r, "This is an HTMX-only endpoint, not a page")
return
}

View File

@ -20,7 +20,7 @@ func (app *Application) OfflineTimeline(w http.ResponseWriter, r *http.Request)
c := persistence.NewTimelineCursor()
err := parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}
@ -65,7 +65,7 @@ func (app *Application) Timeline(w http.ResponseWriter, r *http.Request) {
}
err := parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}

View File

@ -106,7 +106,7 @@ func (app *Application) TweetDetail(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
val, err := strconv.Atoi(parts[1])
if err != nil {
app.error_400_with_message(w, fmt.Sprintf("Invalid tweet ID: %q", parts[1]))
app.error_400_with_message(w, r, fmt.Sprintf("Invalid tweet ID: %q", parts[1]))
return
}
tweet_id := scraper.TweetID(val)
@ -123,7 +123,7 @@ func (app *Application) TweetDetail(w http.ResponseWriter, r *http.Request) {
app.ErrorLog.Print(fmt.Errorf("TweetDetail (%d): %w", tweet_id, err))
if errors.Is(err, ErrNotFound) {
// Can't find the tweet; abort
app.toast(w, r, Toast{Title: "Not found", Message: "Tweet not found in database", Type: "error"})
app.error_404(w, r)
return
} else if errors.Is(err, scraper.ErrSessionInvalidated) {
toasts = append(toasts, Toast{

View File

@ -21,7 +21,7 @@ func (app *Application) UserFeed(w http.ResponseWriter, r *http.Request) {
user, err = scraper.GetUser(scraper.UserHandle(parts[0]))
}
if err != nil {
app.error_404(w)
app.error_404(w, r)
return
}
panic_if(app.Profile.SaveUser(&user))
@ -78,7 +78,7 @@ func (app *Application) UserFeed(w http.ResponseWriter, r *http.Request) {
}
err = parse_cursor_value(&c, r)
if err != nil {
app.error_400_with_message(w, "invalid cursor (must be a number)")
app.error_400_with_message(w, r, "invalid cursor (must be a number)")
return
}

View File

@ -12,20 +12,31 @@ func panic_if(err error) {
}
}
// func (app *Application) error_400(w http.ResponseWriter) {
// http.Error(w, "Bad Request", 400)
// }
func (app *Application) error_400_with_message(w http.ResponseWriter, msg string) {
http.Error(w, fmt.Sprintf("Bad Request\n\n%s", msg), 400)
func (app *Application) error_400_with_message(w http.ResponseWriter, r *http.Request, msg string) {
if is_htmx(r) {
w.WriteHeader(400)
app.toast(w, r, Toast{Title: "Bad Request", Message: msg, Type: "error"})
} else {
http.Error(w, fmt.Sprintf("Bad Request\n\n%s", msg), 400)
}
}
func (app *Application) error_401(w http.ResponseWriter) {
http.Error(w, "Please log in or set an active session", 401)
func (app *Application) error_401(w http.ResponseWriter, r *http.Request) {
if is_htmx(r) {
w.WriteHeader(401)
app.toast(w, r, Toast{Title: "Login required", Message: "Please log in or set an active session", Type: "error"})
} else {
http.Error(w, "Please log in or set an active session", 401)
}
}
func (app *Application) error_404(w http.ResponseWriter) {
http.Error(w, "Not Found", 404)
func (app *Application) error_404(w http.ResponseWriter, r *http.Request) {
if is_htmx(r) {
w.WriteHeader(404)
app.toast(w, r, Toast{Title: "Not found", Type: "error"})
} else {
http.Error(w, "Not Found", 404)
}
}
func (app *Application) error_500(w http.ResponseWriter, r *http.Request, err error) {