fix: unify handler errors
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
|||||||
"mal/internal/db"
|
"mal/internal/db"
|
||||||
"mal/internal/domain"
|
"mal/internal/domain"
|
||||||
"mal/internal/observability"
|
"mal/internal/observability"
|
||||||
|
"mal/internal/server"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -75,11 +76,19 @@ func (h *AnimeHandler) Register(r *gin.Engine) {
|
|||||||
|
|
||||||
func (h *AnimeHandler) HandleProducers(c *gin.Context) {
|
func (h *AnimeHandler) HandleProducers(c *gin.Context) {
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
q := strings.TrimSpace(c.Query("q"))
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
if err != nil {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid page")
|
||||||
|
return
|
||||||
|
}
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
limit, err := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
||||||
|
if err != nil {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid limit")
|
||||||
|
return
|
||||||
|
}
|
||||||
if limit < 1 {
|
if limit < 1 {
|
||||||
limit = 12
|
limit = 12
|
||||||
}
|
}
|
||||||
@@ -113,7 +122,15 @@ func (h *AnimeHandler) HandleProducers(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"producers_fetch_failed",
|
||||||
|
"anime",
|
||||||
|
"failed to load producers",
|
||||||
|
map[string]any{"q": q, "page": page, "limit": limit},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,27 +346,57 @@ func (h *AnimeHandler) HandleBrowse(c *gin.Context) {
|
|||||||
orderBy := c.Query("order_by")
|
orderBy := c.Query("order_by")
|
||||||
sort := c.Query("sort")
|
sort := c.Query("sort")
|
||||||
sfw := c.Query("sfw") != "false"
|
sfw := c.Query("sfw") != "false"
|
||||||
studioID, _ := strconv.Atoi(c.Query("studio"))
|
studioID := 0
|
||||||
if studioID < 0 {
|
if raw := strings.TrimSpace(c.Query("studio")); raw != "" {
|
||||||
studioID = 0
|
id, err := strconv.Atoi(raw)
|
||||||
|
if err != nil || id < 0 {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid studio id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
studioID = id
|
||||||
}
|
}
|
||||||
|
|
||||||
var genres []int
|
var genres []int
|
||||||
for _, g := range c.QueryArray("genres") {
|
for _, g := range c.QueryArray("genres") {
|
||||||
id, _ := strconv.Atoi(g)
|
id, err := strconv.Atoi(g)
|
||||||
|
if err != nil {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid genre id")
|
||||||
|
return
|
||||||
|
}
|
||||||
if id > 0 {
|
if id > 0 {
|
||||||
genres = append(genres, id)
|
genres = append(genres, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
if err != nil {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid page")
|
||||||
|
return
|
||||||
|
}
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := h.svc.SearchAdvanced(c.Request.Context(), q, animeType, status, orderBy, sort, genres, studioID, sfw, page, 24)
|
res, err := h.svc.SearchAdvanced(c.Request.Context(), q, animeType, status, orderBy, sort, genres, studioID, sfw, page, 24)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"browse_search_failed",
|
||||||
|
"anime",
|
||||||
|
"failed to load browse results",
|
||||||
|
map[string]any{
|
||||||
|
"q": q,
|
||||||
|
"type": animeType,
|
||||||
|
"status": status,
|
||||||
|
"order_by": orderBy,
|
||||||
|
"sort": sort,
|
||||||
|
"studio": studioID,
|
||||||
|
"sfw": sfw,
|
||||||
|
"page": page,
|
||||||
|
},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,9 +481,9 @@ func (h *AnimeHandler) HandleBrowse(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AnimeHandler) HandleAnimeDetails(c *gin.Context) {
|
func (h *AnimeHandler) HandleAnimeDetails(c *gin.Context) {
|
||||||
id, _ := strconv.Atoi(c.Param("id"))
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
if id <= 0 {
|
if err != nil || id <= 0 {
|
||||||
c.Status(http.StatusNotFound)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid anime id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,9 +570,9 @@ func (h *AnimeHandler) HandleAnimeDetails(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AnimeHandler) HandleHTMLWatchOrder(c *gin.Context) {
|
func (h *AnimeHandler) HandleHTMLWatchOrder(c *gin.Context) {
|
||||||
id, _ := strconv.Atoi(c.Query("animeId"))
|
id, err := strconv.Atoi(c.Query("animeId"))
|
||||||
if id <= 0 {
|
if err != nil || id <= 0 {
|
||||||
c.Status(http.StatusBadRequest)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid anime id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,11 +849,19 @@ func (h *AnimeHandler) HandleRandomAnime(c *gin.Context) {
|
|||||||
|
|
||||||
anime, err := h.svc.GetRandomAnime(ctx)
|
anime, err := h.svc.GetRandomAnime(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch random anime"})
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"random_anime_fetch_failed",
|
||||||
|
"anime",
|
||||||
|
"failed to fetch random anime",
|
||||||
|
nil,
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if anime.MalID == 0 {
|
if anime.MalID == 0 {
|
||||||
c.JSON(http.StatusBadGateway, gin.H{"error": "Random anime unavailable"})
|
server.RespondHTMLOrJSONError(c, http.StatusBadGateway, "random anime unavailable")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,20 +879,32 @@ func (h *AnimeHandler) HandleRandomAnime(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AnimeHandler) HandleAnimeReviews(c *gin.Context) {
|
func (h *AnimeHandler) HandleAnimeReviews(c *gin.Context) {
|
||||||
id, _ := strconv.Atoi(c.Param("id"))
|
id, err := strconv.Atoi(c.Param("id"))
|
||||||
if id <= 0 {
|
if err != nil || id <= 0 {
|
||||||
c.Status(http.StatusNotFound)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid anime id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
if err != nil {
|
||||||
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid page")
|
||||||
|
return
|
||||||
|
}
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
reviews, hasNextPage, err := h.svc.GetReviews(c.Request.Context(), id, page)
|
reviews, hasNextPage, err := h.svc.GetReviews(c.Request.Context(), id, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"anime_reviews_fetch_failed",
|
||||||
|
"anime",
|
||||||
|
"failed to load reviews",
|
||||||
|
map[string]any{"anime_id": id, "page": page},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mal/internal/domain"
|
"mal/internal/domain"
|
||||||
|
"mal/internal/server"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -35,13 +36,21 @@ func (h *WatchlistHandler) HandleUpdateWatchlist(c *gin.Context) {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&body); err != nil || body.AnimeID <= 0 || body.Status == "" {
|
if err := c.ShouldBindJSON(&body); err != nil || body.AnimeID <= 0 || body.Status == "" {
|
||||||
c.Status(http.StatusBadRequest)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid request body")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.svc.UpdateEntry(c.Request.Context(), userID, body.AnimeID, body.Status)
|
err := h.svc.UpdateEntry(c.Request.Context(), userID, body.AnimeID, body.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"watchlist_update_failed",
|
||||||
|
"watchlist",
|
||||||
|
"failed to update watchlist entry",
|
||||||
|
map[string]any{"user_id": userID, "anime_id": body.AnimeID, "status": body.Status},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,16 +64,24 @@ func (h *WatchlistHandler) HandleDeleteWatchlist(c *gin.Context) {
|
|||||||
userID = u.ID
|
userID = u.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
animeID, _ := strconv.ParseInt(c.Param("id"), 10, 64)
|
animeID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||||
|
|
||||||
if animeID <= 0 {
|
if err != nil || animeID <= 0 {
|
||||||
c.Status(http.StatusBadRequest)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid anime id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.svc.RemoveEntry(c.Request.Context(), userID, animeID)
|
err = h.svc.RemoveEntry(c.Request.Context(), userID, animeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"watchlist_remove_failed",
|
||||||
|
"watchlist",
|
||||||
|
"failed to remove watchlist entry",
|
||||||
|
map[string]any{"user_id": userID, "anime_id": animeID},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,16 +95,24 @@ func (h *WatchlistHandler) HandleDeleteContinueWatching(c *gin.Context) {
|
|||||||
userID = u.ID
|
userID = u.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
animeID, _ := strconv.ParseInt(c.Param("id"), 10, 64)
|
animeID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||||
|
|
||||||
if animeID <= 0 {
|
if err != nil || animeID <= 0 {
|
||||||
c.Status(http.StatusBadRequest)
|
server.RespondHTMLOrJSONError(c, http.StatusBadRequest, "invalid anime id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.svc.DeleteContinueWatching(c.Request.Context(), userID, animeID)
|
err = h.svc.DeleteContinueWatching(c.Request.Context(), userID, animeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"continue_watching_delete_failed",
|
||||||
|
"watchlist",
|
||||||
|
"failed to delete continue watching entry",
|
||||||
|
map[string]any{"user_id": userID, "anime_id": animeID},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +128,15 @@ func (h *WatchlistHandler) HandleGetWatchlist(c *gin.Context) {
|
|||||||
|
|
||||||
entries, err := h.svc.GetWatchlist(c.Request.Context(), userID)
|
entries, err := h.svc.GetWatchlist(c.Request.Context(), userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Status(http.StatusInternalServerError)
|
server.RespondError(
|
||||||
|
c,
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
"watchlist_load_failed",
|
||||||
|
"watchlist",
|
||||||
|
"failed to load watchlist",
|
||||||
|
map[string]any{"user_id": userID},
|
||||||
|
err,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user