From 5d0fd635919dcd9ae36e8cb0ccb346b459a685c1 Mon Sep 17 00:00:00 2001 From: Alessio Date: Mon, 19 Aug 2024 14:43:28 -0700 Subject: [PATCH] When returning HTTP errors, send toasts if request is HTMX --- internal/webserver/handler_bookmarks.go | 4 +-- internal/webserver/handler_follow_unfollow.go | 8 ++--- internal/webserver/handler_lists.go | 12 +++---- internal/webserver/handler_login.go | 4 +-- internal/webserver/handler_messages.go | 2 +- internal/webserver/handler_search.go | 8 ++--- internal/webserver/handler_sidebar.go | 2 +- internal/webserver/handler_timeline.go | 4 +-- internal/webserver/handler_tweet_detail.go | 4 +-- internal/webserver/handler_user_feed.go | 4 +-- internal/webserver/response_helpers.go | 31 +++++++++++++------ 11 files changed, 47 insertions(+), 36 deletions(-) diff --git a/internal/webserver/handler_bookmarks.go b/internal/webserver/handler_bookmarks.go index 84de2ee..9fce875 100644 --- a/internal/webserver/handler_bookmarks.go +++ b/internal/webserver/handler_bookmarks.go @@ -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 } diff --git a/internal/webserver/handler_follow_unfollow.go b/internal/webserver/handler_follow_unfollow.go index 977e3b8..f685cc1 100644 --- a/internal/webserver/handler_follow_unfollow.go +++ b/internal/webserver/handler_follow_unfollow.go @@ -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 } diff --git a/internal/webserver/handler_lists.go b/internal/webserver/handler_lists.go index bc774d4..329ae6e 100644 --- a/internal/webserver/handler_lists.go +++ b/internal/webserver/handler_lists.go @@ -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)) diff --git a/internal/webserver/handler_login.go b/internal/webserver/handler_login.go index f5f37e4..d1e47d0 100644 --- a/internal/webserver/handler_login.go +++ b/internal/webserver/handler_login.go @@ -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))} diff --git a/internal/webserver/handler_messages.go b/internal/webserver/handler_messages.go index dab8aaf..63acdfe 100644 --- a/internal/webserver/handler_messages.go +++ b/internal/webserver/handler_messages.go @@ -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 } diff --git a/internal/webserver/handler_search.go b/internal/webserver/handler_search.go index 50b2868..179a0bc 100644 --- a/internal/webserver/handler_search.go +++ b/internal/webserver/handler_search.go @@ -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) diff --git a/internal/webserver/handler_sidebar.go b/internal/webserver/handler_sidebar.go index 47c0e9e..b02a8d9 100644 --- a/internal/webserver/handler_sidebar.go +++ b/internal/webserver/handler_sidebar.go @@ -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 } diff --git a/internal/webserver/handler_timeline.go b/internal/webserver/handler_timeline.go index 0910d9f..a4d0c51 100644 --- a/internal/webserver/handler_timeline.go +++ b/internal/webserver/handler_timeline.go @@ -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 } diff --git a/internal/webserver/handler_tweet_detail.go b/internal/webserver/handler_tweet_detail.go index d6c2a60..6f61b01 100644 --- a/internal/webserver/handler_tweet_detail.go +++ b/internal/webserver/handler_tweet_detail.go @@ -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{ diff --git a/internal/webserver/handler_user_feed.go b/internal/webserver/handler_user_feed.go index df37fb7..5540f14 100644 --- a/internal/webserver/handler_user_feed.go +++ b/internal/webserver/handler_user_feed.go @@ -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 } diff --git a/internal/webserver/response_helpers.go b/internal/webserver/response_helpers.go index 4a50ee5..ac9f21c 100644 --- a/internal/webserver/response_helpers.go +++ b/internal/webserver/response_helpers.go @@ -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) {